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 7 6-4-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. Contribuitions 1.5. Hellos and greets 2. Mail Room 3. 'Unit Gfx, Please Respond' - A true story 3.1. What the hell is a Unit ????? 3.2. Defining a Unit 3.2.1. Interface 3.2.2. Implementation 3.2.3. Startup-Code 3.2.4. The final Unit 3.2.5. Valid and invalid instruction 3.3. Other Unit-related stuff 3.4. The CRT Unit 4. Boolean Algebra - Another look at conditions 4.1. NOT 4.2. AND 4.3. OR 4.4. XOR 4.5. Rules of Boolean Algebra 4.6. Using Boolean algebra 5. Our friend, the pointer - Part II 6. The Lens Effect 6.1. Transformations 6.2. How to make the lens transformation 7. Graphics Part VI - Scrolling Part I 7.1. What is scrolling ? 7.2. Wipe Scrolls 7.3. Other Scrolls 8. Hints and tips 9. Points of view 10. The adventures of Spellcaster, part 7 -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 7 of 'The Mag', brought to you, as usual, by Spellcaster, alias Diogo de Andrade. This issue is quite large (for me, of course, that have to write it...). I know I told you last issue that I could put in this issue the first of the Spellcaster Utilities... Well, I didn't had time to code it, with other projects and stuff, so I think I'll postpone the utilities for the Easter vacation... If you have any suggestions for them, write to me... Also, a little add... I'm trying to change the logo on top, the one that says 'THE MAG', but my ANSI art sucks !! In fact, all my art sucks !!! I was born to code... :) So, if anyone out there wants to do a better logo for me, go ahead !!! NOTE- No strange codes, just plain ASCII chars... Not even ASCII-E characters, because I use (and sometimes write) this in a UNIX machine, and I don't know how to configure the simbols on the screen... :( 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 two weeks, maybe more, probably less (Eheheheh). This is getting to an issue every month, 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. - BBS's : I don't know for sure what BBS's have or will have my magazine, but I will try to post it in the Skyship BBS. If you have a BBS and you want to receive 'The Mag', contact me. Skyship BBS numbers: (351)+01-3158088 (351)+01-3151435 - Internet : You can access the Spellcaster page and take the issues out of there in: http://alfa.ist.utl.pt/~l42686 1.4. 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. I'm also trying to start a new demo/game/utility group, and I need all sort of people, from coders (sometimes, one isn't enough), musicians (I can compose, but I'm a bit limited), graphics artists (I can't draw nothing) and spreaders... I mean, by a spreader, someone who spreads things, like this mag. 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 very_beutifull_but_not_so_tall_guy who spends ALL his time on campus... My adress is: Praceta Carlos Manito Torres, n§4/6§C 2900 Set£bal Portugal Email: dgan@rnl.ist.utl l42686@alfa.ist.utl.pt And if you want to contact me on the lighter side, get into one of the following talkers... To do that telnet to: Lost Eden -> Alfa.Ist.Utl.Pt : Port 1414 Talho -> Ubistc.Ubi.Pt : Port 7000 CItal -> 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.5. 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 ! Special hails go to: Ricardo 'Scorpio' Oliveira... Check out his page, it's great !! :) -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. Mail Room I've received a mail from a person that shall remain nameless (right, Greg ? :) ), asking were can we get information about coding on the Net. I'm no expert on InterNet, but I know a couple of sites were you can get stuff: - The Hornet Demo Archive: ftp.cdrom.com This site has _LOTS_ of stuff, including complete source code of some demos, and tutorials made by several demopeople. Besides source code and tutorials, you can get there all demos made by man... Well, almost all... :) - The Asphyxia Page: (don't remember the URL) This is the home site of the Asphyxia VGA Tutorials, made by Denthor... These are very recomendable tutorials, the best there are !! I love them, and it was they who got me into demo/game coding... They are similar to my Graphics tutorials, only better done... :) - Scorpio's Homepage: (I also don't remember this URL) This is the homepage of a friend of mine here in Portugal... He's a lot into DemoCoding, so he has LOTS and I mean LOTS of links to code related pages... Go get them !! - My home page: http://alfa.ist.utl.pt/~l42686 My home page doesn't really have much about coding, except for 'The Mag'... But you have there more links to other pages full of coding info... You can find the URL of the Asphyxia Page and of the Scorpio HomePage there... ----------------------------------------------------------------------------- Because of various letters, I'm posting here an errata to 'The Mag'... It seems that, when I gave the SetColor procedure (back in issue 3) I made I mistake when writing it... The header of that procedure is like this: Procedure SetColor(Col,R,B,G:Byte); and it should be like this: Procedure SetColor(Col,R,G,B:Byte); You must swap the B and G variables... If you do this, the procedure should work fine... Thanks to the people that spotted this error... -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. 'Unit GFX, Please Respond' - A true story For starters, let me just say that the title of the article has got nothing to do with the article itself... It's just one of those titles I _HAD_ to put... Don't ask me why... :) This article is about Pascal Units... 3.1. What the hell is a Unit ????? To put this simply, a unit is a bunch of routines and other stuff strung up together to be used by multiple programms... For example, imagine you made a procedure like this: Procedure StupidThing(A,B:Integer); Begin Writeln(A*B+B+A-B*B); { This is just a stupid calculus } End; And you use it in lots of programs you make... Well, so you would have to Copy/Paste it a lot, or perhaps re-write it... That would SUCK !!! So, the nice guys at Borland joined up, took some elements of another programming language (invented by the same guy that invented Pascal, N.Wirth) called Modula-2 and created UNITS ! How do you use a unit ? Well, you make one of the first lines of your program like this: Uses MyStUnit; And then, when you need procedure StupidThing, you don't have to write all the procedure in the procedure declaration area of your program... By using units, you make your program more organized, as you save precious time and fingertips... By using units, the following program would run: Program TestUnit; Uses MyStUnit; Begin StupidThing(1,2); StupidThing(3,4); StupidThing(5,6); End. Well, if you run out for the keyboard and wrote that program, and then you run it, you will get a 'File Not Found' error... :( That's because you haven't defined the unit !!! 3.2. Defining a Unit To define a Unit, you just make this: - The first line of code in a unit must be Unit <name>; - Then, the unit will be splited in three parts... The interface part, the implementation part and the startup-code part 3.2.1. Interface This is were you define what can be acessed by a program... You will tell what procedures, functions, variables and other stuff can be called from your program... This section starts with the word 'Interface'. So the next piece of code is a valid Interface section: Interface Const SpellConst=1000; Type SpellRec=Record Alfa:Word; Beta:Byte; End; Var SpellVar1:Integer; SpellVar2:SpellRec; Procedure StupidThing(A,B:Integer); Function SpellFunc(A:Integer):Word; Note that you don't include the code of the procedure MyStupidThing in the interface section... That goes in the implementation seccion. 3.2.2. Implementation This is were the code of the functions and procedures defined in the interface section, alongside with other variables, constants and types that are to be internally used by the procedures and functions, and that are not acessible by the main program. This section starts with the word 'Implementation'. This is a valid counterpart of the above Interface section: Implementation Const HiddenConst=255; Procedure IntProc; { This is an internal procedure } Begin Writeln('Hidden Constant=',HiddenConst); End; Procedure StupidThing(A,B:Integer); Begin Writeln(A*B+B+A-B*B); End; Function SpellFunc(A:Integer):Word; Begin SpellFunc:=Abs(A)*2; IntProc; End; 3.2.3. Startup-Code This is a small part of the unit, that is only usefull sometimes... It is a part of a program (like standart Pascal program), but it is only executed once, when the unit is linked (see the meaning of this down). This is a valid Startup-Code section, for the above examples: Begin StupidThing(10,20); End. 3.2.4. The final Unit The Unit I built in the example would become something like this: Unit MyStUnit; Interface Const SpellConst=1000; Type SpellRec=Record Alpha:Word; Beta:Byte; End; Var SpellVar1:Integer; SpellVar2:SpellRec; Procedure StupidThing(A,B:Integer); Function SpellFunc(A:Integer):Word; Implementation Const HiddenConst=255; Procedure IntProc; { This is an internal procedure } Begin Writeln('Hidden Constant=',HiddenConst); End; Procedure StupidThing(A,B:Integer); Begin Writeln(A*B+B+A-B*B); End; Function SpellFunc(A:Integer):Word; Begin SpellFunc:=Abs(A)*2; IntProc; End; Begin StupidThing(10,20); End. When you have the whole unit code, you save it to disk ( NOTE: You must use the same name for the Unit and for the file you store the unit). Now, you can run the first program I gave you in this article... 3.2.5. Valid and invalid instruction The folowing instructions are valid, using the above unit: - WriteLn(SpellConst); - Var A:SpellRec; - SpellVar1:=100; - SpellVar2.Alpha:=4362; - StupidThing(A,10); - A:=SpellFunc(-1002); And the following intructions are _NOT_VALID_ : - Writeln(HiddenConst); - IntPro; 3.3. Other Unit-related stuff Number One: YOU CAN'T RUN A UNIT !!! :) Units are to be integrated in a program (called the main program), and are of no use by themselves... Number Two: The header of a procedure/function must be the same in the interface and implementation sections. Number Three: When in the main program you call a Unit, that unit is compiled and integrated in the main program... This is called the linkage stage of compilation... Linking is getting the units and the program together. Number Four: When you compile a Unit, a file is generated in disk. That file has a TPU extension and the same name as the file of the unit. So, if you compile 'MyStUnit.PAS', you will get the file 'MyStUnit.TPU'. That file is in machine-code, and you shouldn't erase it, because it makes subsequent compilations faster. Don't worry, that Pascal is smart enough to change the TPU file when you alter the PAS file (as long as you compile it). Number Five: A Unit can call another Unit... Just add the Uses clause and you can use the procedures and other stuff of another unit. I generally place the Uses clause in the interface section, altough you can put it in the implementation section, as long as you don't use (for example) the records defined in the child-unit in the interface section... 3.4. The CRT Unit Just to end this article, I'll tell you about a GREAT GREAT GREAT Unit Pascal haves... It is called the CRT units... It is in the Unit directory of your copy of Turbo Pascal 6, and it has lots of routines to work with text mode. I'll just tell you of some, so that you can have some fun with them: - GotoXY(X,Y) -> This procedure get's the cursor at the position (X,Y). So, if you do: GotoXY(10,10); Write('ABC'); it will appear on the screen's position (10,10) the word 'ABC'... The upper left corner of the screen is (1,1). - TextColor(C) -> This changes the color of the text you write to color C... You can use constants that are defined in unit CRT, so you can write: TextColor(Yellow); The other colors available are: Black, Blue, Green, Cyan, Red, Magenta, Brown, LightGray, DarkGray, LightBlue, LightGreen, LightCyan, LightRed, LightMagenta and White. If you want the text to blink, add 128 to the number of the color... So, if you want text blinking in yellow, do: TextColor(Yellow+128); - TextBackground(C) -> This changes the background color of the text. It is similar to TextColor, and you can use the same constants and all, with the exception of the lighter colors, that can't be used for background (don't ask me why !!)... - Sound(Freq) -> Does a sound with a frequency of Freq. - NoSound -> Stops the sound played by the Sound Procedure (it is SOOOO valuable for my ears !!) - ReadKey -> Waits for a key to be pressed and returns the character corresponding to the key... So, if you do: C:=ReadKey; and you press the key A, variable C would hold the value 'A'... - KeyPressed -> Returns a boolean variable that states if a key as been pressed or not. These are the more important ones... To wrap this article, I'll just say that you should master the basics of Units, because they are one of the more important things in Pascal... I use them _ALWAYS_ !!!! -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. Boolean Algebra - Another look at conditions I've talked about conditions in issue two, in a basic manner... In this issue we'll expand the knowledge built in that issue. I've said in that issue that boolean algebra is an algebra that gives only TRUE or FALSE as results... But what I didn't said back then is that you have lots of operations that give boolean results. I talked about operation with two operators (binary operations). They were operations that compared two operands of the same type (were they characteres or numbers). Now, I will show the use of boolean operations, that is, operation in which the operands are of type boolean. There is one unary operation (operation that only uses one operand), and several binary operations... 4.1. NOT The NOT operation is fairly simple... Imagining you have a boolean variable or expression, if you NOTed it, it would inverse... Imagine this: A:=10; B:=5; C:=A<B; D:=NOT(C); Varibale C would have the value FALSE, but D would have the value TRUE... NOT inverts the operand... The truth table of this operation is the following: _________________ | A | NOT A | |---------------| | TRUE | FALSE | | FALSE | TRUE | ----------------- NOTE: The NOT operation is also know as negation, complement or first complement. 4.2. AND The AND operation is a binary operation... It's simple to think of what is it's function... It combines two operands, and if the both operands are TRUE, it returns the value TRUE, otherwise it returns FALSE. Example: A:=10; B:=5; C:=1; D:=A>B; -> This is TRUE E:=A<B; -> This is FALSE F:=C<A; -> This is TRUE G:=D AND E -> This is FALSE H:=D AND F -> This is TRUE Don't forget that: D:=A>B; E:=A<B; G:=D AND E; is equal to: G:=(A>B) AND (A<B); Don't forget the parentesis, or you will get an error message (try to figure out why... :) ). The truth table is this: ___________________________ | A | B | A AND B | |---------------|---------| | TRUE | TRUE | TRUE | | TRUE | FALSE | FALSE | | FALSE | TRUE | FALSE | | FALSE | FALSE | FALSE | --------------------------- 4.3. OR The OR operation is a binary operation... Like in AND, it's name is self- -expainatory. It's simple to think of what is it's function... It combines two operands, and if any one of the operands are TRUE, it returns the value TRUE, otherwise it returns FALSE. Example: A:=10; B:=5; C:=1; D:=A>B; -> This is TRUE E:=A<B; -> This is FALSE F:=C>A; -> This is FALSE G:=D OR E -> This is TRUE H:=E OR F -> This is FALSE The truth table is like this: ___________________________ | A | B | A OR B | |---------------|---------| | TRUE | TRUE | TRUE | | TRUE | FALSE | TRUE | | FALSE | TRUE | TRUE | | FALSE | FALSE | FALSE | --------------------------- 4.4. XOR (eXclusive OR) The XOR operation is almost equal to the OR operation, except in the case in which A and B are both TRUE... In that case, A XOR B is equal to FALSE. The truth table is like this: ___________________________ | A | B | A XOR B | |---------------|---------| | TRUE | TRUE | FALSE | | TRUE | FALSE | TRUE | | FALSE | TRUE | TRUE | | FALSE | FALSE | FALSE | --------------------------- 4.5. Rules of Boolean Algebra There are some rules that can help simplify some complex boolean expressions. They are fairly intuitive and easy to demonstrate... They are the following: - X OR FALSE = X - X OR TRUE = TRUE - X OR X = X - X OR NOT(X) = TRUE - X AND TRUE = X - X AND FALSE = FALSE - X AND X = X - X AND NOT(X) = FALSE - NOT ( NOT(X)) = X (Commutative) - X OR Y = Y OR X - X AND Y = Y AND X (Associative) - X OR (Y OR Z) = (X OR Y) OR Z - X AND (Y AND Z)= (X AND Y) AND Z (Distributive) - X AND (Y OR Z) = (X AND Y) OR (X AND Z) - X OR (Y AND Z) = (X OR Y) AND (X OR Z) (De Morgan) - NOT (X OR Y) = NOT(X) AND NOT(Y) - NOT (X AND Y) = NOT(X) OR NOT(Y) 4.6. Using Boolean algebra You can use boolean algebra in expressions in your If, While and Repeat..Until sentences. Just use the rules suplied and it is easy. Examples: If (A>B) AND (B<C) Then ...... If (A=B) OR (A>C) Then ....... If Not(A=B) Then ............. While Not(A<B) Do ............ Repeat .............................. Until (A=B) AND (D=E); Hope this article was good and usefull... As always, I recommend you to experiment, in order to get higher learning... Remember, only the wise and the fools don't experiment, thus, they don't change... :) -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- 5. Our friend, the pointer - Part II This article, as you may already guessed, is the follow-up to an article I made on issue one. If you don't recall, or if you didn't read it, get issue one and read it... The problem with the pointer structure of Part I of this article is that the size of the structure was rigid... It did had to be already defined. This is good for lookup tables, but it is no good for other uses... Check out this example: Lets imagine you wanted to make an image handling library, that could read images of various sizes and move them on the screen. You know that we should use pointers to store the images (because you can have the possibity that the combined sizes of the images is bigger than 64 Kb). But, using the method taught in Part I, all the images should have the same size... But we want to have images of several sizes, so the size of a pointer must be determined at run-time, rather than in compile-time. If you are adventerous, you already have investigated further the GetMem and FreeMem procedures, and you already know how to do this. I will explain for the rest of you out there. This is an (incomplete) example: Type Image=Record Xsize,Ysize:Word; Data:Pointer; End; .............. .............. .............. Procedure LoadImage(Filename:String;Var Pic:Image); Var F:File; Begin Assign(F,Filename); Reset(F,1); BlockRead(F,Pic.Xsize,2); { Read the X size of the image); BlockRead(F,Pic.Ysize,2); { Read the Y size of the image); GetMem(Pic.Data,Xsize*Ysize); BlockRead(F,Pic.Data^,Xsize*Ysize); Close(F); End; ............... ............... Procedure KillImage(Var Pic:Image); Begin Freemem(Pic.Data,Pic.Xsize*Pic.Ysize); End; In the theory side, just think about this. You read first the horizontal and vertical sizes and store them in variables. Then, you know you need (Xsize*Ysize) bytes to store an image of that size, so you allocate them with GetMem. In the end, when the image is no longer needed, you deallocate the space of the pointer with Freemem... Did you understand ? You can dinamically allocate space, using GetMem and FreeMem... I think this is fairly simple to grasp concepts, that with practice become part of your nature... To end this article, I will leave with this problem... Imagine you needed a enlarging procedure, that given an image, it would expand the image to double it's size... You can't change the size of a pointer without first destroying the contents... There are ways to get around this... One of them is not suited for this example, but it will be the one that I will teach in the Part III of this series of articles. The other way, that can really be used in this example is to use another pointer, that stores the enlarged image... -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. The Lens Effect To explain the lens effect, I must first define how to do a transformation of any kind... Then, I'll explain how to do the maths for a lens... Reading just the first part, you get a nice idea on how to do mirroring, and other effects like that... 6.1. Transformations Lets think of a transformation like this: you have a section of the screen on which you want to do a transformation. Consider the smallest rectangule that encompasses all the transformation. To increase speed, let's assume that the transformation is precalculated (this is different of an animation !). As the transformation is precalculated, you know the size of the blue rectangle. Let's call that size SIP (Size In Pixels). So, I need three arrays: ORIGIN[SIP], TRANS[SIP] and DEST[SIP]. Note: I will build up an example of real code in Pascal, during the explanation... It will be in between {}. Assuming the size is 30x30 const xsize=30; ysize=30; var ORIGIN, TRANS, DEST:array[1..xsize*ysize] of byte; Another note: When I talk about image, I'm talking about the part of the screen you want to process. ORIGIN stores the original image. You must update this array, everytime you change the image... Procedure UpdateOrigin(x,y:word); var a,b:integer; begin for a:=0 to xsize-1 do for b:=0 to ysize-1 do ORIGIN[b*xsize+1]:=getpixel(a+x,b+y); end; The parameters on this procedure are the x and y coordinates of the upper left corner of the image. Getpixel is a routine that returns the color of the point at the given coordinates. This obviously depends on the resolution. Now, we must operate the transformation... We want to convert the array ORIGIN in the array DEST, with the effect... So, DEST is ORIGIN transformed... So, you must calculate each of the pixels of DEST with base on ORIGIN. You must determine the new value for each pixel of ORIGIN and store it in DEST... To know what is the new value, we use the array TRANS, that is a constant, and therefore can be calculated in advance. For each effect, this array is different, so you can use the same basic routines for lots of effects, just changing the calculation of TRANS. This array says for each pixel: the pixel at position A in DEST, should be the same as the pixel at position TRANS[A] in ORIGIN. So the formula is DEST[A] = ORIGIN[TRANS[A]] for all elements. The transformation is fast, and does not depend on the resolution/video-mode. We now copy the array DEST to the screen, and the transformation is complete. If you want the transformation to move (i.e. the lens move), you must update ORIGIN for every change in position. Note: Don't forget to replace the transformed image with the original image before you move... Just as an example of transfomation, here it is the calculation of a transformation array that magnifies the upper left corner by a factor of MAG (MAG is a variable of type byte). Procedure CalcMatrix; Begin For A:=0 To ysize-1 do For B:=0 To dX-1 do TRANS[A*xsize+B]:=(A div MAG)*Xsize+(B div MAG); End; Note: You should use a virtual screen if you don't want your screeen to flicker like mad ! 6.2. How to make the lens transformation Calculating the transformation matrix for the lens, here is were the trouble begins... This envolves lots of maths and a bit of thinking... Consider a sphere S, intersecting with a plane A. This defines a circle C in the plane. Define Rc as the ray of the circle C, Rs as the ray of the sphere S, and d as the distance from the centre of the sphere to the plane A. We know then that: Rc^2 + d^2 = Rs^2 (1) If you want several lenses with the same radius, but different lens-strength, then you must preserve the same value for Rc. If the size of the transformed area is L by L pixels (the lens is circular, so the involved area is a square) then Rc should be L/2. The distance d determines the strength of the lens. So, knowing Rc and d, we can determine Rs. Suppose we're given L (so Rc = L/2), we made a choice for d, and we calculated the value of Rs. We now want to transform each pixel in the square (LxL). We simply check all pixels (2 loops: one for the X-axis and one for the Y-axis). We call the point we're transforming P. If P lies outside or on C, then the point isn't transformed. That is: TRANS(P)=P; if (x,y) are the coordinates of P than TRANS[x+y*L] := x+y*L. If P lies within the C, then the point will be transformed: the idea behind the effect is to project the points as if they are magnified, that is, the points within the circle must be projected from the center of the sphere to the sphere... EYE | \|/ _______ _- -q / /|\ / / | \ A -------------|---------i--p--|----------------- | d / | | ------O | <-- This is supposed to be circle... Don't blame me if my ASCII art sucks !! Ask me for a GIF if you want a clear picture.. Hence, for a given point p, we determine the point q (see picture). That point q is the projection of some point i within the circle C, so from the point q, we draw a line to the centre of the sphere, thus defining the point i, which is the intersection of that line whith the plane. That is the transformation of the point p: TRANS(p)=i, because: if the point i was to be projected on the sphere, it would end up at the point q. Since we're looking straight onto the plane, the points q and p are the same (that is: they end up at the same point on your screen). When implementing, you can use some shortcuts: Lets say L and d are given. Then Rc=L/2; Consider the origin of a rectangular system in the centre of the circle C. The X-axis and the Y-axis are parallel to the plane A, in such a way that the square that is to be transformed, is defined as: [-Rc,Rc] x [-Rc,Rc] The orientation of the Z-axis is such that the coordinates of the centre of the sphere are (0,0,-d). The sphere is given by: x^2 + y^2 + (z+d)^2 = Rs^2 (2) hence: [ (1) ] x^2 + y^2 + (z+d)^2 = Rc^2 + d^2 (3) Consider the point P within the circle C. Thus, its coordinates are: (Px,Py,0) and Px^2 + Py^2 < Rc^2 (4) The coordinates of the point Q can be found through this system: | x = Px A - defines the line, parrallel to the Z-axis | y = Py through the point P. | x^2 + y^2 + (z+d)^2 = Rc^2 + d^2 We immediatly find Qx and Qy (Qx=Px; Qy=Py), and for Qz we find: Px^2 + Py^2 + Qz^2 + 2*d*Qz + d^2 = Rc^2 + d^2 Qz^2 + 2*d*Qz - Rc^2 + Px^2 + Py^2 = 0 D = d^2 - (Px^2 + Py^2 - Rc^2) (5) since Rc^2 > Px^2 + Py^2 [ (4) ], we find D>0, and Qz = 1+-sqrt(D). We find 2 solutions (the line intersects the sphere twice !), but consider only the one with Qz>0. We find coordinates for Q: (Qx,Qy,Qz) == (Px,Py,1+sqrt(D)) <see (5) for D> (6) Finally, we find the coordinates of the point I through : (Sx,Sy,Sz) are the coordinates of the centre of the sphere (Sx,Sy,Sz) == (0,0,-d) | ( z - Sz) | x = --------- * (Qx-Sx) + Sx | (Qz - Sz) < defines the line through | < the centre of the sphere | ( z - Sz) < and the point Q | y = --------- * (Qy-Sy) + Sy | (Qz - Sz) | | z = 0 <- defines the plane A We find: ( 0 - (-d) ) Ix = ------------ * (Qx-0) + 0 ( Qz - (-d)) ( 0 - (-d) ) Iy = ------------ * (Qy-0) + 0 ( Qz - (-d)) Iz = 0 hence: d Ix = --------- Qx Qz + d d Iy = --------- Qy Qz + d Iz = 0 We know the coordinates of Q (they can be fully determined if the coordinates of P are known), and we know d, so I can be calculated. We could than say: TRANS[ (L/2+Px) + (L/2+Py) * L ] := (L/2+Ix) + (L/2+Iy) * L; Now, you can see (by just observing the drawing, or by checking the formulas) that if we find TRANS[p(Px,Py)]=i(Ix,Iy) then also TRANS[p( Px,-Py)]=i( Ix,-Iy) TRANS[p(-Px, Py)]=i(-Ix, Iy) TRANS[p(-Px,-Py)]=i(-Ix,-Iy) so that the entire transformation can be defined be calculating a quarter of the transformed area. So, get yourself coding... I don't have any working examples, and I didn't had time/pacience to code one, so try yourself... After the formulas, it should be fairly easy... This article is slightly different because I did it for a newsgroup, not for 'The Mag'... I apologize if you don't like it 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- 7. Graphics Part VI - Scrolling Part I This is the first of a two part article, dedicated to scrolling. There are so many ways to scroll, and so different concepts of scrolling that it is _VERY_ hard to write an article about it. But I'll do my best... NOTE: I have put _ALL_ the procedures given in previous numbers of 'The Mag' in a unit called MODE13h... This unit can be found with this issue of 'The Mag'. This units is supposed to grow from issue to issue, so make sure you have it up to date... If you don't know what a unit is, refer to the first article of this issue... Also, with this issue, you'll get two PCX files, for the example programms. One of them ('SPELL.PCX') was drawn by me (I know, crappy egocentric picture), and the other ('LANDSCAP.PCX') was also 'drawn' by me in Vista Pro (I know it's hard to do a bad Vista picture). 7.1. What is scrolling ? Scrolling is when you move the screen (or part of it) from one place to another... In other words, it is a translatation of the screen. Now, in computer terms, what's scrolling ? Scrolling is based on memory moves... You move the memory around in a way that it seems that the screen itself is moving... Confused already ? :) Tough this seams complicated, it is fairly easy... In the first part of this articles, I will explain you Wipe Scrolls, and other kinds of scrolls that I can't really define... I can't explain all about of scrolling, but I'll explain the concepts behind it, so if you think a little you can determine how to make the effect you require. Also, in this first part, I will only talk about full-screen scrolls... Partial scrolls will be discussed in part II of the Scrolling Saga !! :) 7.2. Wipe Scrolls A wipe scroll is a type of scrolling that is used for cleaning the screen. For example, let's imagine you have a very nice PCX image displayed on your screen, and you want it to scroll up, until it disappears. This is fairly easy to do... You simply copy all lines on the screen to the line above that one. You do it like this: Program Scroll_Up; Uses Mode13h; { This uses the Mode13h unit } Var A:Integer; Procedure ScrollUp; Begin WaitVbl; Move(Mem[VGA:320],Mem[VGA:0],63680); End; Begin Initgraph; LoadPCX('Spell.Pcx',VGA); For A:=0 To 200 Do ScrollUp; Readln; Closegraph; End. See the image going up until it disappears ? The Move instruction moves the second line to the first line, the third line to the second, and so forth, until the 200th line goes to the 199th line. The first line is discarted... If you still can't understand think about this: The address of the second line in mode 13h is A000:0320, and the address of the first line is A000:0000... So, to scroll up, you move the memory from A000:0320 to A000:0000... And, because you want scroll all lines from the second to the last, you move 64000-320 bytes, that is 63680 bytes. If you want to scroll down, you just move the memory from A000:0000 to A000:0320... Simple like this: Procedure ScrollDown; Begin WaitVbl; Move(Mem[VGA:0],Mem[VGA:320],63680); End; To scroll right or left isn't that easy. To scroll left, for example: A000:0001 moves to A000:0000 A000:0321 moves to A000:0320 And so forth... So, you can't move all the memory with just one command... To scroll left, you'll have to scroll each line individually... Like this: Procedure ScrollLeft; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A+1)],Mem[VGA:(320*A)],319); End; And to scroll right: Procedure ScrollRight; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A)],Mem[VGA:(320*A+1)],319); End; So far, so good, right ? WRONG !!! Try to scroll the other picture, LANDSCAP.PCX... Look at the trail it leaves behind. That's ugly, isn't it ? Well, why does it happen ? It happens because you don't change the first line (in the case of the ScrollDown), the last line (in the case of the ScrollUp), the first pixel of every line (in case of the ScrollRight) or the last pixel of every line (in case of the ScrollLeft). So, let's clear it to black... The new scroll procedures are like this: Procedure ScrollUp; Begin WaitVbl; Move(Mem[VGA:320],Mem[VGA:0],63680); Line(0,199,319,199,0,VGA); End; Procedure ScrollDown; Begin WaitVbl; Move(Mem[VGA:0],Mem[VGA:320],63680); Line(0,0,319,0,0,VGA); End; Procedure ScrollLeft; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A+1)],Mem[VGA:(320*A)],319); Line(319,0,319,199,0,VGA); End; Procedure ScrollRight; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A)],Mem[VGA:(320*A+1)],319); Line(0,0,0,199,0,VGA); End; The ideia is to put a black line in the certain place, so that it clears that part of the screen, so that the scroll won't leave a trail... 7.3. Other Scrolls If you re-think the last concept, the one about clearing a part of the screen, you will be able to do a lot more effects... For instance, type the following code: Program Landscape; Uses Mode13h,Crt; Var Height:Integer; Procedure ScrollLeft; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A+1)],Mem[VGA:(320*A)],319); Line(319,0,319,199,0,VGA); Height:=Height+Random(5)-2; If Height<2 Then Height:=2; Line(319,199-Height,319,199,1,VGA); End; Begin Randomize; Initgraph; SetColor(0,0,0,0); SetColor(1,0,63,0); Height:=50; Repeat ScrollLeft; Until Keypressed; Closegraph; End. See the landscape (YECH!) scrolling right in the screen ? Ok, it isn't very nice, but it's just to show you something... Notice that in the ScrollLeft procedure we've added the following lines: Height:=Height+Random(5)-2; If Height<2 Then Height:=2; Line(319,199-Height,319,199,1,VGA); These are the lines that really draw the landscape... They insert a new column in the screen... Then, that column is scrolled with the rest of the picture. That gives the illusion of a landscape comming to sight. You can do this with all directions... Check out the Starfield program. The program will scroll a starfield 200 pixels in every direction, including diagonals... Program Starfield; Uses Mode13h,Crt; Var A:Byte; Procedure SetUpStarfield; Var A:Word; Begin { Set up colors... Grey scales } For A:=0 To 15 Do SetColor(A,A*4,A*4,A*4); { Put random starfield in screen } For A:=0 To 500 Do PutPixel(Random(320),Random(200),Random(16),VGA); End; Procedure ScrollUp; Begin WaitVbl; Move(Mem[VGA:320],Mem[VGA:0],63680); Line(0,199,319,199,0,VGA); PutPixel(Random(320),199,Random(16),VGA); End; Procedure ScrollDown; Begin WaitVbl; Move(Mem[VGA:0],Mem[VGA:320],63680); Line(0,0,319,0,0,VGA); PutPixel(Random(320),0,Random(16),VGA); End; Procedure ScrollLeft; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A+1)],Mem[VGA:(320*A)],319); Line(319,0,319,199,0,VGA); PutPixel(319,Random(200),Random(16),VGA); End; Procedure ScrollRight; Var A:Byte; Begin WaitVbl; For A:=0 To 199 Do Move(Mem[VGA:(320*A)],Mem[VGA:(320*A+1)],319); Line(0,0,0,199,0,VGA); PutPixel(0,Random(200),Random(16),VGA); End; Begin Randomize; Initgraph; SetUpStarfield; For A:=0 To 199 Do ScrollUp; For A:=0 To 199 Do ScrollLeft; For A:=0 To 199 Do ScrollRight; For A:=0 To 199 Do ScrollDown; For A:=0 To 199 Do Begin ScrollUp; ScrollLeft; End; For A:=0 To 199 Do Begin ScrollUp; ScrollRight; End; For A:=0 To 199 Do Begin ScrollDown; ScrollRight; End; For A:=0 To 199 Do Begin ScrollDown; ScrollLeft; End; Readln; Closegraph; End. This article is far for completion, because there is so many thing to be said about scrolling, but I will leave that for part II... In that article, I'll discuss partial scrolls and tile scrolling, so look out for it !! :) I will also talk of paralax scrolling, tough that is just a matter of various scrolls moving at different speeds... That involves virtual screens, so it's quite complicated to talk, tough it's easy to implement... -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. Hints and Tips * - Begginners tip ** - Medium tip *** - Advanced tip - Var keyword in procedures (*) Sometimes, you want to change the content of a variable that is passed to a procedure. There are two reasons to do this, but I will explain it later. This is not so easy as you may expect... For example: Program Test_Twenty_Something; { I lost count !! :))) } Var B:Integer; Procedure Test(C:Integer); Begin Writeln('Before in the procedure B=',C); C:=10; Writeln('After in the procedure B=',C); End; Begin B:=5; Writeln('Before in the main program B=',B); Test(B); Writeln('After in the main program B=',B); Readln; End. If you run this, you will see that C:=10; in the procedure doesn't affect variable B in the main program, but only value C in the procedure. If you really want to change variable B, you must use the VAR keyword in the procedure declaration, like this: Procedure Test(Var C:Integer); If you test the above program with that alteration, you will get the desired effect. Why do you want to change a variable within a procedure ? Simple, because you need to in your program... Or that, or you want to make a procedure that returns an array. You can't return a whole array in a function, so you must use a procedure instead, passing the destination array as a parameter. -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. Points of View The other day, I was talking to a friend of mine, and he said: 'Spell, why don't you make you mag in C ?'. I turned to him and I said: 'Because I hate C !!!'... I found out that I'm equal to lots of people in the computerscene who hate something that don't use... This is very frequent: Subject A - So, what language/tracker/paint program do you use ? Subject B - Well, I use Pascal/OctaMed/Deluxe Paint IV... Subject A - Yech !! That's so lame !!! I use C/Scream Tracker/Animator, and they rule ! Your programs are so lame... I don't want people to see me talking with a lame person like you... Bye... I know this sounds stupid, but it happens... So, after I said that to my friend, I ammended saying: 'Really, I don't use C, because Pascal is easier and if I want speed or versatility I use 100% assembler...'. So, you should follow this simple rule: Just because someone dislikes a particular program/language/whatever, it doesn't mean that a person should be excluded because of that. Speaking only of languages... Lots of people don't regard me as a great programmer... They think I just plain SUCK because I use Pascal to code my utilities and to join things together... To that people, I just have one thing to say: Lucky you, because you don't have to enter a competition with me... Not because I would win, not at all, but because I would put a very big fight, using only my 'inferior' Pascal, with my 'useless' assembler code. And to those that think that assembler is stupid because it isn't portable, just think about this: Your dear little C and other languages alike were programmed in ASSEMBLER !!!! They must be... And you can't code a GOOD game or demo using only C++ or Visual Basic ! They aren't fast enough and they don't let you use the inner resources of the machine without needing a bit of assembly code... Just using the interrupt 10h you are using assembler ! So grow up and learn how to code !! You don't work with a machine, you make the machine work for you... Maybe one day, with the P7 or the likes you will have so much speed that you can code in Basic and still knock out Second Reality out of the Best_Demo_Of_The_World spot, but still that doesn't make you as good a programmer as Psi of Future Crew, or other of my heroes in coding... So, I'll never pay any attencion to those who think I'm a fool/stupid because I use Pascal or Assembler... In the bottom-line, anyone that can use assembler can use C or any other language, and not everyone that can use C can use assembler... Never forget this... Your precious C compiler was writen in assembler... And all good things in life are writen in assembler (or they just stink) ! I'm sorry this is agressive, but this is true... I'm not saying that C is crap, but I prefer Pascal... That is my choice, but I don't mind you using C... It's your choice... All I'm saying is that ASM rules ! Ok, for my next issue, I'll probably finish the series of articles dedicated to pointers, and I'll make part II of the Scrolling Tutorial. Also, I'm going to begin a begginer's tutorial on how to make a simple text adventure... it will explain everything, from the basics (creating a storyline and making maps and stuff) to the implementation of the ideias... I'm also thinking of including an article on 'How to make a cool starfield', and more hints and tips and the adventures of Spellcaster... :) I'm going to try this issue faster, but I don't garanty anything, because I must first create the text adventure, so that I can teach how it was done... ;) -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. The adventures of Spellcaster, the rebel programmer of year 2018. Episode 7 - Love in a Time of War We arrived at the Brotherhood's headquarters little after 8. There we could breath again... The escape has been a success. - Well, after this rather shacky start, I think you two need to be formally introduced... - said Gundsen, with a strange tone on his voice. For the first time since I opened the cells at the Sega Prison, I looked at Gundsen's sister eyes. I felt immediatly lost... My heart began to pound faster and faster. I thought I felt my legs trembling and drips of sweat beggining to flow in my whole body. I couldn't say or think anything... All I could see were those dark brown eyes, and the apparently soft and beutifull skin that was around them... Gundsen cough, as to call me to reality. It took me a couple more seconds to regain my self control, and to be able to look at rest of Gundsen's sister. She had straigh hair, with a very dark tone of brown, that runned down her head until it stopped near the neck. I tried to be discrete when I looked at her body. Her body was perfect in every aspect, with round curves and not a single piece of celulite, unusual for this time. I looked back at her face... Her eyes were glittering, and she had a faint smile on her beutifull lips. Something told me that I didn't was so discrete as I wanted... I smiled back. - Yuhhh !!! Spell !!! Diogo !!!! - shouted Karl, trying to get me back to reality. I didn't responded, and Karl kicked me in the ass, to wake me up. I lost my balance for a couple of seconds, for I wasn't expecting it, and I swinged my body, raising my leg, and halting it just before reaching Gundsen's face. - Lighten up, Spell !!! - he said. - I just wanted to wake you up... - Sorry, Karl... - I smiled. - I was a little distracted... - I continued, looking at the girl. - As I was saying, before you went to cybergroove, this is my sister, Kristie Gundsen, alias known as Excalibur... - Karl said to me. Then he looked at Kristie. - Kristie, this is Diogo 'Spellcaster' Andrade... He's helping me getting into Comptel system... - Pleased to meet you... - she said quickly to me, before looking again at Karl. - Are you still trying that stupid trick ?!!! I can't believe it ! You tried for almost five years, without success, and you nearly got yourself killed because of that! It almost got you and it got Rick dead ! And you are trying to do it again, this time with a guy that doesn't look like it can code jack-shit !!!! - she looked at me. - No offense intended... - None taken. - I said quitelly, leaving the room... I tought that I shouldn't get in the middle of a family's stuff... I went up to the top floor of the building were we had our HQ, and I looked at the sun, that was orange because of the pollution, and I said to myself: - Can it be ?... Could it happen to me ?... Is it possible... Love in a time of war ?! See you in the next issue Diogo "SpellCaster" Andrade