Spellcaster presents: TTTTTTTTTT HH HH EEEEEEEEEE MM MM AAAA GGGGGGGGG TT HH HH EE MMM MMM AA AA GG TT HH HH EE MM M M MM AA AA GG TT HHHHHHHHHH EEEEEE MM MM MM AAAAAAAA GG TT HH HH EE MM MM AA AA GG GGGG TT HH HH EE MM MM AA AA GG GG TT HH HH EEEEEEEEEE MM MM AA AA GGGGGGGG Issue 9 20-7-96 -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- Index: 1. Introduction 1.1. About the magazine 1.2. About the author 1.3. Distribution 1.4. Subscribing 1.5. Contribuitions 1.6. Hellos and greets 2. Mailroom 3. The Turbo Assembler Bible 4. Designing a text adventure - Part II 4.1. Looking at a room 4.2. Player input and the command parser 4.3. Basic movement commands 5. How to make a 100% assembly program 6. Scrolling horizontaly in text mode 7. Sprites Part II - Animation and movement 7.1. Animating a sprite 7.2. Moving a sprite 7.3. Doing it all together 8. Graphics Part VIII - Text in graphic mode 8.1. Basics 8.2. Fixed sized fonts 8.3. Proportional fonts 8.4. Colorfonts 9. Hints and tips 10. Points of view 11. The adventures of Spellcaster, part 9 -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 1. Introduction 1.1. About the magazine Welcome to number 9 of 'The Mag', brought to you, as usual, by Spellcaster, alias Diogo de Andrade. Big issue... Again... This is becomming a hobbit... Ooopss... Sorry, a habbit... :))) I'm reading Tolkien, so this is an understandable mistake... :) This issue is _VERY_ late... This is another hobbit... :)) It is late because I have lots of projects and exams to do and I don't have the time. Also, this issue features another article by Scorpio, which like the last one, is pretty different from the usual... You should be reading this issue in the first of the SpellUtilities, SpellView ! It's a program designed to view text with some features, like colors and other neat stuff... :) You probably can get it from the same place you got this issue, but if you can't, look in the BBS's listed somewhere in this issue, or look in my HomePage... It includes full source code, so you can change it, and see how it was done... :) Well, thinking better, don't use SpellView... SpellView SUCKS ! I only now noticed that it screws up with files with a certain size... And this issue is very big for SpellView to read it... Shit... Well, back to the old drawing board... :((( Ok, other notice... You can use SpellView, but only version 1.2... That's a fixed version that saves up some memory... It can read files almost three times larger than version 1.1 could... :))) This magazine is dedicated to all the programmers and would-be programmers out there, especially to those that can't access the Net easily to get valuable information, to those who wish to learn how to program anything, from demos to games, passing through utilities and all sort of thing your mind can think of, and to those that can't find the right information. When you read this magazine, I'll assume some things. First, I assume you have Borland's Turbo Pascal, version 6 and upwards (and TASM for the assembly tutorials). I'll also think you have a 80386 (or 386 for short; a 486 would be even better), a load of patience and a sense of humor. This last is almost essencial, because I don't receive any money for doing this, so I must have fun doing it. I will also take for certain you have the 9th grade (or equivelent). Finally, I will assume that you have the last issues of 'The Mag', and that you have grasped the concepts I tried to transmit. If you don't have the issues, you can get them by mail, writing to one of the adresses shown below (Snail mail and Email). As I stated above, this magazine will be made especially for those who don't know where to get information, or want it all in the same place, and to those who want to learn how to program, so I'll try to build knowledge, building up your skills issue by issue. If you sometimes fail to grasp some concept, don't despair; try to work it out. That's what I did... Almost everything I know was learnt from painfull experience. If you re-re-re-read the article, and still can't understand it, just drop a line, by mail, or just plain forget it. Most of the things I try to teach here aren't linked to each other (unless I say so), so if you don't understand something, skip it and go back to it some weeks later. It should be clearer for you then. Likewise, if you see any terms or words you don't understand, follow the same measures as before. Ok, as I'm earing the Net gurus and other god-like creatures talking already, I'm just going to explain why I use Pascal. For starters, Pascal is a very good language, ideal for the beginner, like BASIC (yech!), but it's powerfull enough to make top-notch programms. Also, I'll will be using assembly language in later issues, and Pascal makes it so EASY to use. Finally, if you don't like my choice of language, you can stop whining. The teory behind each article is very simple, and common with any of the main languages (C, C++, Assembly - Yes, that's true... BASIC isn't a decent language). Just one last thing... The final part of the magazine is a little story made up by my distorted mind. It's just a little humor I like to write, and it hasn't got nothing to do with programming (well, it has a little), but, as I said before, I just like to write it. 1.2. About the author Ok, so I'm a little egocentric, but tell me... If you had the trouble of writing hundreds of lines, wouldn't you like someone to know you, even by name ? My name is Diogo de Andrade, alias Spellcaster, and I'm the creator, editor and writer of this magazine. I live in a small town called Setubal, just near Lisbon, the capital of Portugal... If you don't know where it is, get an encyclopedia, and look for Europe. Then, look for Spain. Next to it, there's Portugal, and Setubal is in the middle. I'm 18 years old, and I just made it in to the university (if you do want to know, I'm in the Technical Institute of Lisbon, Portugal), so I'm not a God-Like creature, with dozens of years of practice (I only program by eight years now, and I started in a Spectrum, progressing later to an Amiga. I only program in the PC for a year or so), with a mega-computer (I own a 386SX, 16 Mhz), that wear glasses with lens that look like the bottom of a bottle (I use glasses, but only sometimes), that has his head bigger than a pumpkin (I have a normal sized head) and with an IQ of over 220 (mine is actually something like 180-190). I can program in C, C++, Pascal, Assembly and even BASIC (yech!). So, if I am a normal person, why do I spend time writing this ? Well, because I have the insane urge to write thousands of words every now and then, and while I'm at it, I may do something productive, like teaching someone. I may be young, but I know a lot about computers (how humble I am; I know, modesty isn't one of my qualities). Just one more thing, if you ever program anything, please send to me... I would love to see some work you got, maybe I could learn something with it. Also, give me a greet in your program/game/demo... I love seeing my name. 1.3. Distribution I don't really know when can I do another issue, so, there isn't a fixed space of time between two issues. General rule, I will try to do one every month, maybe more, probably less (Eheheheh). This is getting to an issue every two months, so, I'll think I'll change the above text... :) 'The Mag' is available by the following means: - Snail Mail : My address is below, in the Contributions seccion... Just send me a disk and tell me what issues you want, and I will send you them... - E-Mail : If you E-mail me and ask me for some issues, I will Email you back with the relevant issues attached. - Internet : You can access the Spellcaster page and take the issues out of there in: http://alfa.ist.utl.pt/~l42686 Follow the docs link... - Anonymous ftp: I've put this issue of 'The Mag' on the ftp.cdrom.com site... I don't know if they'll accept it there, because that's a demo only site, and my mag doesn't cover only demos, but anyways, try it out... It has lots of source code of demos. 1.4. Subscribing If you want, I'm starting "The Mag"'s subscription list... To get 'The Mag' by Email every month, you just need to mail me and tell me so... Then, you will receive it every time a new issue is made... 1.5. Contributions I as I stated before, I'm not a God... I do make mistakes, and I don't have (always) the best way of doing things. So, if you think you've spotted an error, or you have thought of a better way of doing things, let me know. I'll be happy to receive anything, even if it is just mail saying 'Keep it up'. As all human beings, I need incentive. Also, if you do like to write, please do... Send in articles, they will be welcome, and you will have the chance to see your names up in lights. They can be about anything, for a review of a book or program that can help a programmer, to a point of view or a moan. I'm specially interested in articles explaining XMS, EMS, DMA and Soundblaster/GUS. If anyone out there has a question or wants to see an article about something in particular, feel free to write... All letters will be answered, provided you give me your address. If you have a BBS and you want it to include this magazine, feel free to write me... I don't have a modem, so I can only send you 'The Mag' by Email. You can also contact me personally, if you study on the IST (if you don't know what the IST is, you don't study there). I'm the freshman with the black hair and dark-brown eyes... Yes, the one that is skipping classes to code his (first) next demo !! :) My adress is: Praceta Carlos Manito Torres, n§4/6§C 2900 Set£bal Portugal Email: l42686@alfa.ist.utl.pt And if you want to contact me on the lighter side, get into the Lost Eden talker... To do that telnet to: Alfa.Ist.Utl.Pt : Port 1414 If that server is down, try the Cital talker, in Zeus.Ci.Ua.PT : Port 6969 I'm almost always there in the afternoon... As you may have guessed already, my handle is Spellcaster (I wonder why...)... 1.6. Hellos and greets I'll say hellos and thanks to all my friend, especially for those who put up with my constant whining. Special greets go to Denthor from Asphyxia (for the excelent VGA trainers), Draeden from VLA (for assembly tutorials), Joaquim Elder Guerreiro, alias Dr.Shadow (Delta Team is still up), Joao Neves for sugestions, testing and BBS services, and all the demo groups out there. I will also send greets to everybody that responded to my mag... Thank you very much ! -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 2. Mailroom Ok, let's open up some mail... The first is from Ben Basset from Australia: ------------------------------------------------------------------------------- First of all let me say G'DAY from me...Ben Bassett from West Australia. I started reading through your "The Mag" magazines sometime last week. I am currently studying Computer Science at Curtin University, so I already know most of the Pascal stuff. I'm learning about the mode 13h graphics mode from your tutorials. I'm currently working my way through issue 7, and my goal is to produce a half-way decent demo or game. I like your style of writing, informal yet informative, keep up the good work! Down to business - Circles issue 4. Note: What I write below is an observation that I made while looking through issue 4. I think I'm on the right track here, but if not, you can throw this heap of utter bollocks in the trash. ********* I notice when you pre-generate the sin and cos values, you use 1800 reals for each. I think this may be a mistake. If you are using a radian gap of 0.005, you only need 1257 points to draw all the way around the circle. eg. (2*PI)/0.005 = 1256.637 What you are doing when you use 1800 points is that every point beyond 1257 (ie. points 1258 to 1800) is drawing over a point that you have already plotted. You only need 1257 to reach 2*PI. Here's what I've done: Declare 2 constants: radian_gap=0.005; number_circle_points=round((2*PI)/radian_gap)+1; and use number_circle_points wherever you used to put 1799 ie. for i:=0 to number_circle_points do you should use this when getting the memory for the sin and cos arrays, and also when plotting the points themselves. I tried this out on your Tunnel.pas demo, and got around 30% speed-up when drawing the circles. ********* Well, thats about it for the serious stuff (I'm not very serious by nature anyway). Hope to see some cool stuff in future mags! --Ben Bassett. ------------------------------------------------------------------------------- Ok, Ben, you got me there... I guess I made the mistake because I used a diferent step in my first calculations... Well, thanks for the mistake correction... :) Just to prove I'm human... The second letters comes... Let me see... Oh, Ben Basset again... :))) ------------------------------------------------------------------------------- I forgot to mention that you should set the circle_point_gap (or whatever I called it) to reflect the maximum size of circle you want to draw. There's not much point in plotting thousands of points for circles with radius 4. In general I have experiented and come up with the following values: circle_point_gap radius of circle 0.1 less than 8 0.05 less than 13 0.01 less than 49 0.005 less than 97 0.001 pretty much any size. I only mention this because it saves time and memory (and I LIKE time and memory!). --Ben Bassett ------------------------------------------------------------------------------- Well, another smart remark... Time and memory are _VERY_ important, and they are often opposites to each other (this is, when you want more speed, you have less memory, and vice versa)... But in this case, he won both... Is this guy learning ? He should be teaching me... :))) Well, that's the end of this issue's mailroom... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 3. The Turbo Assembler Bible Ok, this is the article writen by Scorpio (Yuppi, less work for me !). I, Spellcaster will make my coments between square brackets []... Your cue, Scorpio... Allright... I'm writing another article today... I was writtin' one about 3DStudio, but I think that I don't have enough knowledge about 3DS to make an article... Just some Texture-mappings, etc... If you still think you could use such an article, email me and I'll be happy to send it to Spellcaster so that he puts it in the next issue(or so) of THE MAG . [ Write it !!! Less work for me, and a more general mag ! ] Let's get to the point, now... This is an article 'bout an Assembly book I'm currently reading. It is called "The Waite Group's Turbo Assembler Bible", a (+-)720 pages and (+-) 1.5Kg book...:) It was printed in 1991, its author is Gary Syck, which was working in various data-communications programs but that also was into graphics programming. The book follows the usual (academic) guidelines, and here are the various chapters: o Chapter 1: Programming the MS-DOS systems  Overview of the MS-DOS Operating System Segments & Offsets, registers, PSP, DOS file system, etc... [ It would be very nice to see an article on the PSP... They are rare... HINT, HINT ! :)))) ]  Introduction to Using Turbo Assembler Let's not forget that this is a book on TASM...  Introduction to the Turbo Debugger Same as above. o Chapter 2: Processor Instructions  Data Movement instructions The name says it all...  Arithmetic, Logic, Bit-Shift Instructions .....  Procedures, Loops and Jumps .....  Processor control and Protected Mode Operation Includes some examples on how to change to Pmode and back to Real mode, Pmode specific instructions and some general info... It seems short...Also, some code to switch through various tasks... o Chapter 3: TASM Directives and Operators  Segment declaration  .......  Code generation, error handling, ..... o Chapter 4: Techniques  Writing ASM modules for HLL (High level languages)  Using system resources  Acessing and controlling the Hardware  Video control: Text and Graphics o Chapter 5: Appendixes  Various TASM/TLINK, ASCII, BIOS/DOS interrupts,etc. tables... Allright... As you've probably noticed I've not included comments for some of the chapters... It's like this: I'm reading this book page by page, and I'm goin' very slowly... between university projects and commercial work, I have so little time left to read... I just can't wait to get to the PMODE section... Maybe (_MAYBE_) I'll do an article on it... Ok... I've read the [ I want that article on Pmode, pronto ! :)) ] first and a bit of the second chapter, and it follows the usual conventions: Memory segmentation, registers, flags, etc, etc, etc... it is NOT a book to ASM experts, yet it IS a book to ASM experts(or almost-experts). Why? Well,it goes from the very basics of a simple printf('Hello\n") (I wrote a line in C in this article???? I must be sick... :)) to some stuff [ You are definetly sick !! No more C ! Please, I hate C... Great language, but I don't like anyway... C is forbidden in 'The Mag'... And maybe... Hum... Just had an ideia... Maybe I'll do a C version of 'The Mag'... What do you people out there think ?? ] 'bout system resources (LIM EMS usage, mouse programming, COM, etc)...to PMODE, to Breshenham's line drawing algorithm, palette handling... There are lots of stuff to the newbies and it is also a good book for a later reference on some subjects... All of these chapters are presented in an informal way (the one we like :)) and easy to understand...So, if you can't find a book around there, check your university library, 'cos you would be surprised with what you can find there... I got this book from University of Minho's library... Don't know if anyone is from around here, but if u are... check this one out. [ IST library sucks big time !! ] Final considerations: THIS IS NOT A MUST-GET book... try PC Magazine's lab book, or some other... Anyway, if you can get your hands on it... don't loose the opportunity. WOW!!! 79 lines??? I can't believe I'm not sleeping by now... It's 3:36 a.m. and I have to wake up at 6:30 tomorrow... er... today... .. er... in 3 hours... Gotta go, ppl... PLZ let me know if you like (or not) this article... PLZ no more flames... I get enough already... Do you know what I mean, Spellcaster? Unfortunately, you do... [ I sure do, man... :((( ] Well, since I am goin' to sleep now, I'd better save this before falling with my face in the keyboard..r n,iuooooooooqdxoewqiyh.wizs n.wqpw OOPS! sorry 'bout that... CYA next time... (I hope). If you wanna tell me anything at all, email to: si17899@ci.uminho.pt (preferred) or si17899@thor.di.uminho.pt or try to find me in Moosaico (a Moo) at: moo.di.uminho.pt 7777 under the name of (guess:) Scorpio. [ I'm there too, sometimes... :) ] It's me, Spellcaster again... Just to say my end phrase: T-t-t-t-t-h-a-t's all folks ! :)))) -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 4. Designing a text adventure - Part II This article is intended for the beginners who want to do games. I know that text adventures are definetly out, but the ideas behind a text adventure are very similar to those found in graphic adventures, so what I say here can be expanded to a graphic adventure... Note that these are my personal views on how a text adventure should be coded and designed... There can be some errors and irregularities in the code and explanations, because I'm writing this at the same time I'm coding the adventure, so I will sometimes make mistakes... If you spot one, or have any doubts, mail me ! This particular issue is about the parser and basic commands. 4.1. Looking at a room Well, one the first thing your text adventure makes is 'looking' at the starting room... By 'looking at a room' I mean 'describe the current room'. By describing a room, I mean to output the description of the room, the objects that there exist, any monsters and other game characteres... Well, as we aren't dealing with monsters, objects and characteres, we'll simply output the description of the room, that is stored in Rooms[].Desc array... So, let's make the look procedure: Procedure Look(RoomNumber:Word); Var A:Byte; Begin Writeln; A:=1; While (A<11) And (Rooms[RoomNumber].Desc[A]<>'*') Do Begin Writeln(Rooms[RoomNumber].Desc[A]); Inc(A); End; Writeln; End; If you are wondering why don't you use just a For loop for writing the ten-line description, remember that the description can have less that 10 lines, and in that case, the last line has a '*' character only... Ooopsss... I almost forgot to describe the exits... To do so, add the following lines: Writeln('Visible exits:'); If Rooms[RoomNumber].North<>0 Then Write('North '); If Rooms[RoomNumber].South<>0 Then Write('South '); If Rooms[RoomNumber].East<>0 Then Write('East '); If Rooms[RoomNumber].West<>0 Then Write('West '); Writeln; To hilite this info, use a different color, like this... Add before describing the room: TextColor(White); And before the exits description: TextColor(Yellow); And everything will be a lot nicer... :) To use this, you must implement the Play procedure, that will mantain the core of the game... For the meanwhile, all the Play procedure does is to output the description of the room... Procedure Play; Var ExitFlag:Boolean; Begin ExitFlag:=False; Look(CurrentRoom); Repeat Until ExitFlag; End; The ExitFlag is a flag that controls the exit of the game... When it is set to true, the game will cease it's execution... It can be put to true because the players quits or wins the game... Then you must add to the main program a call to this procedure, and you must define and initialize the CurrentRoom variable... CurrentRoom stores the current position of the player in the game, so I make it a variable of type Word... And you must initialize it to the value 22, because that is the starting room in FangLore... Do that in the Init procedure... 4.2. Player input and the command parser Now, it is the time to receive input from the user... This is were lots of game fail... Why ? Because their command parser suck !!!! What's the command parser ? Well, the command parser is a procedure (more like a bunch of procedures) that get the input of the user, process it (separating commands and subjects) and then executes the instructions required... Well, I think this is a quite nifty thing to do for a begginner, so I'll try to explain this well enough... First thing, you must receive the input of the player... You do that using the Readln keyword (most adventure games have their own routines to read player input, but I think that's a lot more complicated to explain simply here). ReadLn(Command); You put this in the Play command, after the call to the Look procedure. You must define the Command variable as a string... I gave a little color to this Readln statement in the Fanglore.Pas program: TextColor(LightRed); And I also put it a prompt, so that the player knows that the game is awaiting input: TextColor(White); Writeln('What is thy bidding, master ?'); Then, the next step is to split that string in it's components... The ideia is to, given a string like this: 'Get the mask' you get it splitted in only the words 'get' and 'mask'... The 'the' would vanish, like all the prenoms (I don't know if this word exists... It's similar to the portuguese one... :))). So, let's do a procedure that given a string, returns an array with the words separated: Procedure Parse(S:String;Var Parsed:Array[1..10] Of String); We use a procedure with a Var parameter instead of a function, because a function can't return a whole array... You can't even do it like it is above... You must define a type like this: Type ParsedType:Array[1..10] Of String; And then you can define the procedure like this: Procedure Parse(S:String;Var Parsed:ParsedType); Now, the first thing to to is to split the phrase in it's components... You achieve that by searching for the first space, and getting what's in the string from the beggining to the position of that space, and placing it in the array, and so forth... The complete procedure is like this: Procedure Parse(S:String;Var Parsed:ParsedType); Var ArrayIndex:Byte; StringIndex:Byte; NextSpace:Byte; Begin ArrayIndex:=1; StringIndex:=0; NextSpace:=FindSpace(S,StringIndex); While (StringIndex>=Length(S)) And (ArrayIndex<11) Do Begin NextSpace:=FindSpace(S,StringIndex); Parsed[ArrayIndex]:=GetString(S,StringIndex,NextSpace); StringIndex:=NextSpace; Inc(ArrayIndex); End; End; This parses the string... It uses two functions, FindSpace and GetString, whose code is given next... The first of these finds the next space after the given starting location, while the other grabs a part of a string. Function FindSpace(S:String;Start:Byte):Byte; Begin While (S[Start+1]<>' ') And (Start 64 Kb Data < 64 Kb COMPACT: Code < 64 Kb Data > 64 Kb LARGE: Code > 64 Kb Data > 64 Kb HUGE: Code > 64 Kb Data > 64 Kb Arrays > 64 Kb LARGE and HUGE models are equivelent, but the later is used by high level languages. It isn't of no use to us... .286 - Enables 286 instructions... It can be .386, .386P (for protected mode), etc... I usually use .386... If you don't put anything, the compiler will assume 8086 instructions... .STACK - Tells the compiler the size of the stack, in bytes... I will discuss the stack later. I usually use 200h (that's about 512 bytes). .DATA - Tells the program that the following 'stuff' (you will see what is the stuff later) is to be placed in the data segment. .CODE - Tells the program that the following 'stuff' is to be placed in the code segment. START: - Tells the compiler where the main program begins. END START - Tells the compiler that this is the place where the code ends. I will explain most of this stuff in greater detail later. Right now, if you compiled this and run it, you would expect it to just do nothing and return to Dos... You're partially right... It doesn't do nothing, but it doesn't return to Dos either !!! Why ? Because assembler programs are so stupid that they must tell Dos that they are over... To do that, you must call the Dos interrupt 21h, function 4c, to end the code... So, to do nothing and return to dos, the program would have to be something like this: DOSSEG .MODEL SMALL .286 .STACK 200h .DATA .CODE START: Mov AX,4c00h ; This is a comment... Anything after a semi- ; -colon will be ignored... Just to tell you ; that 4c00h is an hexadecimal number... Int 21h END START The 'Mov AX,4c00h' command moves to the AX register (I think I already told you what a register is) the value 4c00 in hexadecimal (19456 in decimal)... I know I told you that the function number is 4c, but this is to be put in the high part of the word AX... The low part is the exit-code (in this case, it is 0) and is to be used in by parent programs, that is, programs that call other programs. The 'Int 21h' command, calls interrupt 21h (the Dos interrupt)... More on it later... Well, it's just 23:00... I think I'm going to bed know... I'll finish this tomorrow... Yawn !!! :))) Ok, I'm back... I fell a lot better now, after a good (but short) night of sleep... Now, I'll talk about the stack... The stack is a very important part in your program... It's a wonderfull place where you can store stuff you will need later. I've talked about the stack in issue 3 of 'The Mag'... Ehehehe... This was short... Now, about the 'stuff'... In assembler, you can also define variables, altough it is a bit more complicated subject... Variables in assembler are composed by an identifier (like Pascal identifiers), and a type: DB - Byte DW - Word DD - DoubleWord So, no strings or chars... Just plain numbers... Arrays are also defined in a different way... But, let's take a step at a time: Spell DB 10 VidSeg DW 0a000h Large DD 010101010101001b These lines define three variables, named Spell, VidSeg and Large, of types Byte, Word and DoubleWord, respectively, and with initial values 10, 0a000 (hexadecimal) and 010101010101001 (binary). If you don't want to assign an initial value, just put a '?' there... Example: Spell DB ? To access the value of a variable, you put the name of the variable in square brackets... Example: Mov AX,[VidSeg] -> This would load the value of variable VidSeg in register AX... Remember that there exist some commands that don't work with variables. One important thing to notice is that how data is organized in an asm. For example, imagine you made the program like this: .Data Spell DB 10 VidSeg DW 0a000h Large DD 010101010101001b This means that the variables are in the Data segment. And now for the fun stuff... The address of variable Spell: Segment: DS (Data Segment) Offset : 0000 Cool, isn't it ? But that's not all... Addresses of variables VidSeg and Large: Seg(VidSeg)=DS Seg(Large)=DS Ofs(VidSeg)=0001 Ofs(Large)=0003 What can you make of this ? That memory addressing is sequencial !!! That can be usefull, for accessing arrays... But I'll talk about them later. For now, let's talk about tables. Do you knew that you could have something like this: Vector1 DB 10,20,30 DB 20,10,30 DB 30,30,20 Imagine that as a triangle defined by it's corners, with it's (x,y,z) coordinates... You can have all that in just one variable. To access, for example, the y coordinate of the second corner (the 2nd value of the 2nd line), you would have to do something like this: Mov AX,[Vector+4] If the variable was defined as Words (DW) and not bytes (DB), you would have to add 8 to the base address of the variable, that is stored in Vector. So, really, Vector is a pointer to the first element of data after it's definition... [Vector] is the real value of the element (in this case 10). Vector+1 would point to the 2nd element, Vector+2 to the 3rd, etc, while [Vector+1] would return the 2nd value, [Vector+2] the 3rd, etc... Now, let's go to arrays... To define a one-dimensional array, you use use the 'dup' keyword... Example, to define an array with 100 elements of type Word, you would do: SpellArray DW 100 Dup(0) This would make an array initialized with zeros... If you wanted to create an unitialized array, you would have to use a '?' instead of a '0'. SpellArray will now point to the first element, etc... To define a two-dimensional array, you would use... well, I'd rather demonstrate... Imagine you want to define a 100x50 array (100 columns, 50 lines). You would do it like this: SpellArray DW 5000 Dup (?) ; Because 100*50=5000 Surprise !! No two-dimensional arrays... But you can use a trick to simulate them... Remember the first article I did on mode 13h ? Well, this is similar... Imagine youwant to access the element Xth element of line Y. You must multiply the Y for the size in columns of the array, and then add the X... For example: Mov AX,[SpellArray+Y*100+X] ; would load AX with the value of ; element desired. So, I think that it is all said now... Check out the example program FLSCR.ASM, that fills the screen in mode 13h, with random colors... Notice on how fast it is... :) God, I love assembler... Toy with it... In the worst case, you'll crash up your computer and nuke your hard drive (you don't knwo how easy is to do this... :)) Cya in another article... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 6. Scrolling horizontaly in text mode Well, this article is made to order... :) It's quite easy to scroll horizontally text, altough it is complicated (I don't think it's even possible) to do it smoothly in text mode. By smoothly I mean to make appear a pixel at a time... I only know how to do scrolling in text mode making a character appear at a time (though I'm currently studying the possibility of doing this a pixel at a time... Not much success, though ! :). The ideia is this... You have a string S: S:='Spellcaster can''t code anything in Pascal, let alone ASM !'; And you want to scroll it around a window with only 10 characteres wide. What you have to do is this: | | | | <---- This represents the screen... | | | | | |Spellcaster can''t code...... (the text is off screen) | | | | | S|pellcaster can''t code...... | | and later you have: | | Spel|lcaster ca|n''t code...... | | Get the picture ? You must choose what piece of text to display... So, some code (I'll use the GetString function from the article number 4). Program HScroll; Uses Crt; Const WhereX=10; { X coordinate of scroller } WhereY=10; { Y coordinate of scroller } NChars=10; { Number of characters to display at one time } Var S:String; Index:Byte; Function GetString(S:String;Start,Finish:Byte):String; Var A:Byte; Tmp:String; Begin Tmp:=''; For A:=Start+1 To Finish Do Tmp:=Tmp+S[A]; GetString:=Tmp; End; Begin S:='Spellcaster can''t code in Pascal, let alone ASM ! '; Index:=1; Repeat { Scroll the area we want to the left } Move(Mem[$B800:((WhereY*80)+WhereX+1)*2], Mem[$B800:((WhereY*80)+WhereX)*2],NChars*2); { Write the character that enters by the right } GotoXY(WhereX+NChars+1,WhereY+1); Write(S[Index]); Inc(Index); If Index>Length(S) Then Index:=1; { Put a small delay, so that you can actually read } { something... } Delay(50); Until KeyPressed; End. You can add a little color, if you want... It would be nicer... :) If you are wondering why you multiply by two when you are converting (X,Y) coordinates to an Offset value, you must know one thing: Text mode is very similar to graphics mode in the memory adressing with some differences: - Base address is B800, not A000 like in mode 13h - You have two bytes per character, not one byte per pixel. The first byte stores the ASCII value of the character, while the second byte stores the attributes. The attributes in text mode are: the foreground color (the text color), the background color, and if that character blinks or not... So, you can directly access Text Memory, and that's the reason why I multiply by two the calculations... The rest is similar to mode 13h. The attribute byte is organized like this: Bit No. 7 6 5 4 3 2 1 0 Bits 0-3 are the foreground color (16 colors possible). Bits 4-6 are the background color (8 colors possible). Bit 7 is the blinking attribute (if it is set, the text will blink). I think this is fairly simple... Any doubts, mail me... :) A last minute correction... I've stated in the beggining of this article that I didn't think that scrolling smoothly in text mode was possible... Well, I was wrong... A friend of mine did it... At least, he says he did it. I didn't had the chance to see it, but I'll try to, and to get the code also... If I understand it, I will do an article on that... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 7. Sprites Part II - Animation and movement So, let's continue our sprite tutorial... In this issue, we'll discuss basic animation and movement control... We'll use the procedures in the Sprite unit I gave last issue... Let's start with... 7.1. Animating a sprite Animation is what you see in a cartoon, right ? WRONG ! In computer terms, an animation is... Well...er... it is, for example, a static objects that changes itself... But, what is animation really ? How can we create it ? Well, we use the same principle as the movies... Our eyes have a limit number of 'updates' that it can receive... By updates, I mean changes in the enviroment... I think the limit is 22 changes per second... The movies, for example, change the image 50 or 60 times a second, that is, the image is slightly altered 50 or 60 times a second... So, the eyes are deceiveds in thinking the image is moving... You change the image rapidly... But, you don't need to change the image 22 times a second to obtain an animation... You can change it just 1 time a second, or one time every 10 seconds... It doens't really matter, altough the movement is smoother if you update the image more frequently... But enough talk... Let's get to business... I've created two sprites of a spaceship, and they are slightly different from each other... So, I will display one after another on the screen, in the same coordinate, so you'll get the illusion of animation... The procedures are the same used in the last issue of The Mag... Program Animation; Uses Mode13H, Sprites, Crt; Var Images:Array[1..2] Of Pointer; F:File; Begin { Initialize graphics and palette } InitGraph; SetColor(1,63,0,0); SetColor(2,63,40,0); SetColor(3,63,63,0); { Load images from a file } Assign(F,'Ship.Img'); Reset(F,1); LoadImage(F,Images[1]); LoadImage(F,Images[2]); Close(F); { Animate them, until a keypressed occurs !! } Repeat PutImage(160,100,Images[1],VGA); Delay(150); PutImage(160,100,Images[2],VGA); Delay(150); Until Keypressed; { Wraps things up } KillImage(Images[1]); KillImage(Images[2]); CloseGraph; End. Not too impressive, I know... But it's a start... The next part is better... 7.2. Moving a sprite Moving a sprite follows the same ideia as animating it... The ideia is to move the sprite, draw it, clear it, move it again, and so forth... Adapting to the last piece of code... Let's define two more variables, X and Ix, of type Word and ShortInt. Initialize variable X with the value 0, and variable Ix with value 1. Then, replace the Repeat Until cicle with... Or, stuff it, I'll dump here the full code: Program Movement; Uses Mode13H, Sprites, Crt; Var Images:Array[1..2] Of Pointer; F:File; X:Word; Ix:ShortInt; Begin { Initialize graphics and palette } InitGraph; SetColor(1,63,0,0); SetColor(2,63,40,0); SetColor(3,63,63,0); { Load image from a file } Assign(F,'Ship.Img'); Reset(F,1); LoadImage(F,Images[1]); Close(F); { Move it, until a keypressed occurs !! } X:=0; Ix:=3; { The bigger the Ix, the faster the sprite will move } Repeat { Draw it } PutImage(X,100,Images[1],VGA); Delay(50); { Clear it } Cls(0,VGA); { Update it } X:=X+Ix; If X>=300 Then Ix:=-3; If X=0 Then Ix:=3; Until Keypressed; { Wraps things up } KillImage(Images[1]); KillImage(Images[2]); CloseGraph; End. Well, this flickers a lot... The way to mend this is by using virtual screens, or clear just the part of the screen... You should be able to do that yourself... I'm so lazy today... 7.3. Doing it all together So, you know how to move it and you know how to animate it... But do you know how to do it at the same time ? This is easy... For each frame it draws, it shows a picture that has been both moved and animated... Let's see some code, because I'm too lazy to explain it... and because this is soooo easy: Program Animation_And_Movement; Uses Mode13H, Sprites, Crt; Var Images:Array[1..2] Of Pointer; F:File; X:Word; Ix:ShortInt; Pic:Byte; Begin { Initialize graphics and palette } InitGraph; SetColor(1,63,0,0); SetColor(2,63,40,0); SetColor(3,63,63,0); { Load images from a file } Assign(F,'Ship.Img'); Reset(F,1); LoadImage(F,Images[1]); LoadImage(F,Images[2]); Close(F); { Animate them, until a keypressed occurs !! } X:=0; Ix:=1; { The bigger the Ix, the faster the sprite will move } Pic:=1; Repeat { Draw it } PutImage(X,100,Images[Pic],VGA); Delay(50); { Clear it } Cls(0,VGA); { Update it } X:=X+Ix; If X>=300 Then Ix:=-3; If X=0 Then Ix:=3; If Pic=1 Then Pic:=2 Else Pic:=1; Until Keypressed; { Wraps things up } KillImage(Images[1]); KillImage(Images[2]); CloseGraph; End. So, here you have it... This is very easy... Let's wrap it up... In next tutorial about sprites, I'll teach you how to make this move over a background, transparency, and maybe a couple of tricks... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 8. Graphics Part VIII - Text in graphic mode 8.1. Basics Text in graphic mode is very different from text in text mode (as the name implies)... You can't use the standart function Writeln (that is, you can, but it will look UGLY !), nor can you use the Readln (you must program your own function to read input in graphic mode). It's fairly easy to do text in graphics mode. You just have to take in account WHAT do you want to print, WHERE do you want to print it, and with what type of character... Yes, you can use various kinds of characters. That's one the advantages of using text in graphics. To the diferent types of characters (that is, every charset) you call a font. There are several kinds of fonts: - Fixed sized fonts -> As the name implies, the characters have all the same width and height, not to mention color. - Proportional fonts -> Characters can have different widths and heights, altough usually they only change in width. The color is the same in all character. - Colorfonts -> Colorfonts can be fixed sized or proportional. The only difference between this and the last ones I mentioned, is that the characters can have different colors... Not different from each others, it's different inside them. You'll see what I mean latter... Graphical text can be very important in any kind of program you make; almost every demo has a scroller (I'll do an article on them someday), every game shows the score or the briefing or something... Trust me... They are important... So, let's get down and dirty... 8.2. Fixed sized fonts The principle behind this is easy: you use the theory behind the sprite, changing the procedures a little (you don't need to read the size of the sprite, it's already set; you don't put different colors; you don't use pointers, etc...). Let's check out the procedures... Oh, I almost forgot... You must create a structure that lets you store the various characters: Const XSize=16; YSize=16; Type FSFont=Array[1..XSize,1..YSize] Of Byte; Chars=array[' '..'§'] Of FSFont; Var Font:Chars; In case you're wondering, you can define an array like that... You access the elements of the array, like this, for example: Font['A',10,10]:=10; Or C:Char; .......... .......... C:='A'; Font[C,10,10]:=10; So, let's read the font: Procedure LoadFont(Filename:String); Var F:File; Garbage:Word; Begin Assign(F,Filename); Reset(F,1); Blockread(F,Garbage,2); Blockread(F,Garbage,2); Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4); Close(F); End; The structure of the file I'm using has it's first four bytes indicating the X and Y size of the font (you might need it... In this case it isn't needed, so we read them to discardable variables: Garbage). Then it has a sequence of bytes indicating if a certain point is on or off... Caution: in this case, that isn't the color, but if the pixel is draw or not... We'll get to colorfonts soon... I'll just explain the instruction that really reads the data: Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4); Well, I think I've already talked about the Ptr function... Well, the Ptr function is usefull to transforms any structure into a pointer. Basically what it does is transform two values (segment and offset) in a pointer. If you supply the two values as beeing the segment and offset of an existing structure (as you do above), and then read to that place in memory, the data will be read into that structure... That's quite useful, as you can see... :) So, let's move on... Putting down a letter... Nothing could be easier: Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word); Var Cfx,Cfy:Byte; Ccc:Byte; Begin For Cfy:=1 To YSize Do Begin For Cfx:=1 To XSize Do Begin Ccc:=Font[N,Cfy,Cfx]; If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where); End; End; End; Well, what the hell is this ??? Simple... You just scan the array for the character you want, then the computer scans the X and Y indexes of that particular character... When it finds an element different of 0, it places a pixel of the specified color in the VGA screen or in any of the virtual screens... Quite easy, I think... To write a string, you just have to go through the string, writing the characters it founds in sequence. Notice that the increment of the X coordinate is fixed, because all the letters have the same size: Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word); Var Index:Byte; Begin For Index:=0 To Length(N)-1 Do PutLetter(X+Index*16,Y,Col,N[Index+1],Where); End; I've included a sample font, to use with the example program, but that font only contains uppercase letters, so use if wisely, my sons... I intend to release a font editor/grab, together with a sprite editor/graber... Maybe I'll ask help from anyone, since I don't have enough time to do all I want... But, without further due, here's a complete working example: Program FixedSizedFont; Uses Crt,Mode13h; Const XSize=16; YSize=16; Type FSFont=Array[1..XSize,1..YSize] Of Byte; Chars=array[' '..'§'] Of FSFont; Var Font:Chars; Procedure LoadFont(Filename:String); Var F:File; Garbage:Word; Begin Assign(F,Filename); Reset(F,1); Blockread(F,Garbage,2); blockread(F,Garbage,2); Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4); Close(F); End; Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word); Var Cfx,Cfy:Byte; Ccc:Byte; Begin For Cfy:=1 To YSize Do Begin For Cfx:=1 To XSize Do Begin Ccc:=Font[N,Cfy,Cfx]; If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where); End; End; End; Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word); Var Index:Byte; Begin For Index:=0 To Length(N)-1 Do PutLetter(X+Index*16,Y,Col,N[Index+1],Where); End; Begin LoadFont('Fixed.Fnt'); InitGraph; SetColor(1,63,63,0); SetColor(2,63,0,0); SetColor(3,0,63,0); SetColor(4,0,63,63); PutString(0,0,1,'THIS IS A TEST !',VGA); PutString(0,30,2,'SPELLCASTER RULES !',VGA); PutString(0,60,3,'I KNOW MODESTY IS',VGA); PutString(0,90,4,'NOT ONE OF MY',VGA); PutString(0,120,5,'QUALITIES...',VGA); Repeat Until Keypressed; Closegraph; End. So, if this is understood, let's move forward... If it isn't, read it again, and experiment !!! Experimentation is the key to good programming practice... :) I think I'll stop here for now... I'm sleepy (I just write this at night, after college, so it's very tiring)... I'll write the next stuff tomorrow... 8.3. Proportional fonts I'm back !!! Well, where was I... er... Oh... Proportional fonts... So, what's a proportional font ? To answear simply... It's a font that isn't fixed in size... So, the x and y sizes of the font varie from character to character... Evidently, we can not use the same structure as above. But, if the bitmaps for the chars are of different sizes, why not use the sprite system implemented in last issue ? So, for all chars: Type Chars=array[' '..'§'] Of Pointer; So, you read the font to the array of pointers, using the LoadImage function that I gave last issue. So, after you read the chars that matter (in the example program I'll give, I only read some chars, because I didn't had time to draw more chars), you must write them out... To draw _ONE_ char, you use the PutImage_C procedure (we use the clipping procedure because we don't know if the text is wider than the screen). So, the real difficulty is in writing a string. So, when the font was fixed sized, you just used the formula: X:=Xi+CharNumber*XSize to find out the X coordinate of the character indexed by CharNumber. Xi is the original X coordinate (the coordinate of the first character), and XSize is the horizontal size of the font. So, if the horizontal size varies, this formula must varie. Let's use a variable, that increases with the size of the font. For example, we initialize that variable to Xi in the start of the WriteString procedure: X:=Xi; Then, imagine the first character was 20 pixels wide. We draw it, and then add that number to var X: X:=X+XSize(FirstCharacter)+Space And so forth... The Space is any number that specifies the number of pixels computer should leave between chars... If you don't put it there, the letters will be all crunched together... See ? Easy... Let's see some _CODE_ !!! :))) Oooopppppssss... I almost forgot... We must change the PutImage_C procedure for two reasons: 1) What is saved in the file is whetever a certain pixel is on or off, not it's color. 2) This procedure erases all background image, and usually text is output on the top of an image or something. When can fix up both problems in just one sitting: Procedure PutChar(X,Y:Integer;Var Img:Pointer;Where:Word); Var Dx,Dy:Word; A,B:Word; Segm,Offs:Word; Begin Segm:=Seg(Img^); Offs:=Ofs(Img^); Move(Mem[Segm:Offs],Dx,2); Move(Mem[Segm:Offs+2],Dy,2); Offs:=Offs+4; A:=Y; While (A<=Y+DY-1) And (A=MinX) And (Y>=MinY) Then { Check if the pixel is to be set or not } If Mem[Segm:Offs]<>0 Then PutPixel(B,A,Mem[Segm:Offs],Where); Inc(Offs); Inc(B); End; Inc(A); End; End; Another thing we'll need is a function that returns the X size of a certain character. This is easy: Function GetX(Img:Pointer):Word; Var Dx:Word; Segm,Offs:Word; Begin Segm:=Seg(Img^); Offs:=Ofs(Img^); Move(Mem[Segm:Offs],Dx,2); GetX:=Dx; End; Well, this is excessive programming, but... :))) It's easier to understand... So, the example program: Program Proportional; Uses Crt,Sprites,Mode13h; Type Chars=array[' '..'§'] Of Pointer; Var Font:Chars; Procedure ReadFont; Var F:File; A:Char; Begin Assign(F,'Proport.Fnt'); Reset(F,1); LoadImage(F,Font[' ']); LoadImage(F,Font['!']); LoadImage(F,Font[',']); LoadImage(F,Font['-']); LoadImage(F,Font['.']); For A:='0' To '9' Do LoadImage(F,Font[A]); For A:='A' To 'Z' Do LoadImage(F,Font[A]); Close(F); { I just loaded some characters because the file only } { had those characters, saved in that order. } End; Procedure PutChar(X,Y:Integer;Col:Byte;N:Char;Where:Word); Var Dx,Dy:Word; A,B:Word; Segm,Offs:Word; Img:Pointer; Begin Img:=Font[N]; Segm:=Seg(Img^); Offs:=Ofs(Img^); Move(Mem[Segm:Offs],Dx,2); Move(Mem[Segm:Offs+2],Dy,2); Offs:=Offs+4; A:=Y; While (A<=Y+DY-1) And (A=MinX) And (Y>=MinY) Then { Check if the pixel is to be set or not } If Mem[Segm:Offs]<>0 Then PutPixel(B,A,Col,Where); Inc(Offs); Inc(B); End; Inc(A); End; End; Function GetX(Img:Pointer):Word; Var Dx:Word; Segm,Offs:Word; Begin Segm:=Seg(Img^); Offs:=Ofs(Img^); Move(Mem[Segm:Offs],Dx,2); GetX:=Dx; End; Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word); Var Index:Byte; Dx:Word; Begin Dx:=X; For Index:=0 To Length(N)-1 Do Begin PutChar(Dx,Y,Col,N[Index+1],Where); Dx:=Dx+GetX(Font[N[Index+1]])+3; End; End; Begin ReadFont; InitGraph; SetColor(1,63,63,0); SetColor(2,63,0,0); SetColor(3,0,63,0); SetColor(4,0,63,63); PutString(0,0,1,'GRABING',VGA); PutString(0,50,2,'PROPORTIONAL',VGA); PutString(0,100,3,'FONTS',VGA); PutString(0,150,4,'SUCKS !',VGA); Repeat Until Keypressed; Closegraph; End. Again, don't use lowercase letters, because they aren't drawn... The problem of proportional fonts is that they must well designed, or else it will happen to some chars the same as happened to 'u' in the example program... Proportional fonts are just a variation of fixed sized fonts... Easy and effective... Personally, I hate proportional fonts... But if you have enough pacience to work with them, go ahead... They always look good... Well, I'm going to bed again... I'll finish this tomorrow... It's almost 2 am... And I must rise at 5 am... LIFE SUCKS !!!! :))) Cya tomorrow... ;) 8.4. Colorfonts Ok, back again... I'm really getting fed up with this issue of 'The Mag', so I'm gonna wrap this up really quickly... :)))) Colorfonts are fonts with colors in the same char... So, when you store a colorfont, you don't store only if the pixel is on or off, you store it's color... So, as I want to get this over, I'll just dump some code onto your screen... This is a complete example program, that shows how fixed sized colorfonts work: Program ColorFonts; Uses Crt,Mode13h; Const XSize=16; YSize=16; Type FSFont=Array[1..XSize,1..YSize] Of Byte; Chars=array[' '..'§'] Of FSFont; Var Font:Chars; CFontPal:RGBList; Procedure LoadFont(Filename:String); Var F:File; Garbage:Word; Begin Assign(F,Filename); Reset(F,1); Blockread(F,Garbage,2); blockread(F,Garbage,2); Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4); Close(F); End; Procedure PutLetter(X,Y:Word;N:Char;Where:Word); Var Cfx,Cfy:Byte; Ccc:Byte; Begin For Cfy:=1 To YSize Do Begin For Cfx:=1 To XSize Do Begin Ccc:=Font[N,Cfy,Cfx]; If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Ccc,Where); End; End; End; Procedure PutString(X,Y:Integer;N:String;Where:Word); Var Index:Byte; Begin For Index:=0 To Length(N)-1 Do PutLetter(X+Index*16,Y,N[Index+1],Where); End; Begin LoadFont('CFont.Fnt'); { Load a palette to use with the font } LoadPal('CFont.Pal',CFontPal); InitGraph; SetPalette(CFontPal); PutString(0,0,'THIS IS A TEST !',VGA); PutString(0,30,'SPELLCASTER RULES !',VGA); PutString(0,60,'I KNOW MODESTY IS',VGA); PutString(0,90,'NOT ONE OF MY',VGA); PutString(0,120,'QUALITIES !!!',VGA); Repeat Until Keypressed; Closegraph; End. I know I'm lazy... :)) This program reads a palette for the color font... You get the ideia when you run it... Try the program out and experiment stuff, like scrolling text and other stuff like that... Scrollies are a part in almost every demo made by men and aliens... And colorfonts are usually a part of them... So, I say goodbye for now... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 9. Hints and Tips * - Begginners tip ** - Medium tip *** - Advanced tip This issue's Hints and Tips will go deeper into the subject of using logic operators with "normal" numbers. - Odd or Even ? (**) Imagine you want to find out if a certain number is odd or even. Think of the internal representation of the numbers 50 and 51: 50 = 00110010 51 = 00110011 Have you seen the difference ? An even number has it's rightmost bit unset and an odd number has it set... So, how can you check for it ? Simple, use the AND operand: A:=50 AND 1 Now, A is equal to 0, that means that the number is even. So, what's the ideia ? 50 = 00110010 1 = 00000001 50 AND 1 = 00000000 If we used 51: 51 = 00110011 1 = 00000001 51 AND 1 = 00000001 51 AND 1 would return 1 !!! Cool, isn't it ? - Positive or negative ? (**) In the internal conception, integer numbers are represented with 15 bits for the value and 1 bit to act as a flag for positive/negative. So, knowing that the signal bit is the leftmost, we can use a method similar to the one we used to determine if the number was odd or even. For example: -50 = 1000000000110010 -0 = 1000000000000000 I know that -0 doesn't really exist, but in computer terms it exists. Notice this: -0 = 1000000000000000 ||-------------| Signal Bit Value Bits So, continuing with the explanation: -50 AND -0 = -0 Getting a -0 means the number is negative. Otherwise the "function" returns a +0. Well, but in Pascal you can't use -0 as an operand, so how do you resolve the problem ? Well, as Pascal is a very flexible language , it can mix integers with words (unsigned integers), so integer (-0) is equal word 32768. So: -50 = 1000000000110010 32768 = 1000000000000000 -50 AND 32768 = 1000000000000000 So, if the result is equal to 32768, then the number is negative... These are two of the many effects you can do with this kind of algebra. You can even compare numbers... But I'll leave that for next issue... I'm tired and I want to go to bed... Yawn... Just before I fall asleep in front of the keyboard, I'll just tell you that this kind of think is called masking... You are masking bits (covering them up)... Just for the record, it was Scorpio who reminded me of these effects... Cya in another article... -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x- 10. Points of View God, I'm tired of this issue... It was a struggle to make this, because I'm in the middle of exams and all... But, finally it's over... This was the biggest of all issues of 'The Mag' so far... The Mag is growing... Next issue, the continuation of the Text Adventure tut, plus an article on how to make the flame effect (made by Scorpio... I'll just add somethings I was meaning to do for ages...). Also, I'll continue to do the articles on Sprites (transparency and moving over a background)... On the mode 13h tutorials, I'll teach how to convert the procedures to assembler... Yep, that's right... Full assembly procedures in next issue... I'll probably start basic 3d stuff, just to introduce a couple of effects that use standart 3d transformations... I'll also teach how to do 2d rotation of a sprite... Er... I think that's it... Maybe I'll remember something else to put there... Maybe I'll convince Scorpio to do some article (eheheh... Did you hear me, Scorpio ???? :)))) ). Well, That's all the time I've got for this issue... I'm already receiving death threat's by people complaining about the long delay on this issue... Oh, just a bit of publicity... Get the 'Infinity Mag, Issue 1'... That's a portuguese mag made by people in the portuguese demoscene, about the demoscene... Issue 2 is almost out, and both issues include my articles of 'The Mag'... So, stay tuned and get it... It's pretty cool... Altough it requires a computer with VESA... But the look is great... Download it and see... You can get it at the ftp.cdrom.com site (it was in the pub/demos/incoming/mags directory when I last checked, but maybe Snowman moved it from there... If you can't find it in that dir, try the pub/demos/mags/1996... The filename is infy1.zip). You can also try the Realm Of Darkness BBS... The numbers are somewhere in this issue or in the info file... So, bye bye folks... See you in super-special-issue 10 !! :) -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x 11. The adventures of Spellcaster, the rebel programmer of year 2018. Episode 9 - Living on a razor's edge (sometimes cut) As predicted by Deathripper, Kristie was back before supper, but she refuse to say even a word to me... - Let's get over the plan again... - I said, looking at the Gundsen Brothers. - Again ?!! - said finally Kristie, in a loud voice. - How many times will we have to go over that blasted plan ?!! I'm sick and tired of it !! You make it sound like it is complicated ! - Hey, I just don't want any complications... - I responded in a calm voice. - Knock it off, you two ! - Karl said. - Can't you be a little time together without killing each other ? - It's not my fault that this no-good programmer keeps treating use like morons !!! - Excalibur said. - No good-programmer ? - I responded, in an angry tone. - Twice you called me a lammer, Kristie... If you keep up like this, I will walk away and you can both blow Comptel alone... If you can without me... - Well, mister Shitcaster... If that's the way you think, go right ahead... Good ridance... - Kristie said, looking me in the eyes... For a moment, I thought about swallowing my pride, and confess to her my love, but I restrained myself and I turn my back to both of them and walked away. - Good work, Kristie... - Gundsen said. - Well, Karl, what are we supposed to do now ? - He'll be back... - Karl said, with a smile on his face Kristie just looked at Karl, with a sad look on her face. - I don't think he hates you... - said Karl, laughing and turning his back on Kristie, who looked very embaressed... Again, Karl's previsions turned out right... In the morning after, the morning of the attack to the COHISS, I was waiting for them near the jetcar. - You came back ! - said Excalibur, excited, calming down in the next second. - I mean... It's about time ! - she continued in a stern voice. - I want to destroy Comptel... And I don't care who does it with me... - I answered. - So, let's go ! - Karl said, going into the car... Kristie led the car, until we arrived at 100 meters from the COHISS: a small shop, full of hitech equipment... A small fortress... One meter of solid cement, and lot's of guards with lazer machine-guns on the roof. - So, this is it... Are you sure you want to go throuht with this ? - I questioned. - Yep... - Karl said, without hesitating. - As damn sure as I'm gonna be... - Kristie answered, pressing the accelerator pedal to the ground, directly aimming the car at the COHISS's door... The guards on the roof opened fire, but the armour of our vehicle holded on, and we crashed against the door ! The door collapsed with the strength of the Durallium block. We went off the car, with lazer riffles in our hands and we started to defend ourselfs from the guards, while Kristie loaded the car with the stuff we needed. In less than ten minutes, the vehicle was loaded, and we started to hear in the distance the sound of a airship comming in our direction. Almost twenty guards laid in front of us, dead, dripping blood from every wound. - Let's go !! Reinforcements are coming ! - Karl shouted, running for the car. Suddently, one of the dead guards wasn't so dead after all, and he lifted himself up, and he shot Karl right in the chest... He screamed and fell on the ground. I opened fire on the guard and hitted him in his head, making blood spill all over the place. - NOOOOOOOO !!!! - Me and Kristie shouted simultaneosly. - Shit ! - I said, running for Karl's body. He was still breathing... At some cost, I managed to pull him to the car, and then Kristie accelerated. We had killed almost every guard in the place, and running from the others was easy... Difficult was to outrace the above airship, but even that was simple, because we went into narrow streets, where it couldn't follow. - Hang on Karl... - I said, holding Karl's hand. He looked at me, with pain and agony visible in his eyes. - Take care of Kristie... - he said, almost in a whisper. - Hang on just a little bit more. - I said. Kristie continued to speed up the jetcar in the streets, trying to reach out headquarters, before it was too late... See you in the next issue Diogo "SpellCaster" Andrade