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 3 4-10-95 þ 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. Resolution of your homework 3. Procedures and functions 3.1. What are they ? 3.2. How to use 3.3. Global and local variables 3.4. Parameters 3.5. Functions 4. Introduction to assembly 4.1. Segments and offsets 4.2. Registers 4.3. The stack 4.4. The flags 4.5. More about segments 4.6. Basic instructions 5. Graphics, part 2 : The palette 5.1. Introduction 5.2. What's the palette 5.3. How to set and get the palette ? 5.4. The retrace 5.5. Palette effects 5.5.1. Rotations 5.5.2. Fades 5.5.3. The static screen 6. Points of View 7. The adventures of Spellcaster, part 3. þ Introduction þ About the magazine So, we've finally reached issue 3... A small step to me, but a great step to computerkind... This issue is packed with information... We have the second part of our mode 13h tutorial (dedicated to palettes), plus an article on procedures and functions for the begginers out there, besides other stuff. This issue also has a special bonus... You probably have noticed that when you decompress 'The Mag', you don't get only this text file, but a lot of other files... They are a part of a small demo I did in two days, especially for the mag. To run the demo, type in 'CBLIND' at the DOS prompt. I made it to acompany this week's mode 13h tutorial, to demonstrate the potencial of the palette, and the wonderfull effects you can achieve with it, if you think for a bit... The hardest thing to do in the demo was to think of the effects. Coding them is easy (except the cross-fade). The effects will be explained in the Graphics section of 'The Mag'. This magazine is dedicated to all the programmers and would-be programmers out there, 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. When you read this magazine, I'll assume some things. First, I assume you have Borland's Turbo Pascal, version 6 and upwards. 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). 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. þ 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 Set£bal, 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 Set£bal 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). 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 even could learn something with it. Also, give me a greet in your program/game/demo... I love seeing my name. þ 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. 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... You can also contact me personally, if 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 occupying one of the X-terminals... I recommend you to contact me personally, if you can, especially if you are a member of the opposite sex (I'm a man, for those of you who are wondering). My adress is: Praceta Carlos Manito Torres, n§4/6§C 2900 Set£bal Portugal Email: dgan@rnl.ist.utl þ Hellos and greets I'll say hellos and thanks to all my friend, especially for those who put up with my constant whining (you know who you are). 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), Alex "Darkfox" (thanks for letting me use your BBS), Jo„o Neves and Henrique Craveiro for sugestions, and all the demo groups out there. I also want to say hi to my idols (I know they don't read this, but...), Chris Roberts, Fran‡ois Lionet, Archer MacLean, everybody at ID Software and Apogee, Sierra On-Line, Lucas Arts and Team 17, for showing me what programming is all about. þ Resolution of your homework In last issue, I chalenged you to make an improvement in the 'Maths' program I gave in Issue 1. Well, here's one of the possible improvements. This gives you the hability to choose the operation you want to execute. Program Maths(Input,Output); Var A,B:Integer; C,OP:Integer; Begin Writeln('The Maths Version 2.0'); writeln; Writeln('1 - Add'); Writeln('2 - Subract'); Writeln('3 - Multiply'); Writeln('4 - Divide'); Writeln('5 - Remainder'); Writeln; Write('Type in the number of the operation:'); Readln(OP); Write('Type in number A : '); Readln(A); Write('Type in number B : '); Readln(B); If OP=1 Then C:=A+B; If OP=2 Then C:=A-B; If OP=3 Then C:=A*B; If OP=4 Then C:=A Div B; If OP=5 Then C:=A Mod B; Writeln; Writeln('The result is ',C); Writeln; Readln; End. This is quite simple... Depending on what operation you type in, the program executes the relevant calculation and prints out the result. There is still room from improvement in the program, but I leave to you decide what to do. You already know, if you have questions, mail them to me and I will answear them, or by mail or by the magazine. þ Procedures and functions These are two of the more powerfull of Pascal's resources. These give you the hability to create new commands and functions. They are almost identical in the way they work, so I will explain deeply the procedure and then I will pass to the function, teaching you the diferences. þ What are they ? Think of procedures like new commands, defined by you to do whatever you want. They are very usefull, as you will see. Let's say you have a program that writes the string 'Ok !' lots of time, and you don't want to spend time always writing "WRITELN('OK !')", what do you do? You use a procedure !! þ How to use In the above example, you would do something like this: before the start of the main program block, write this: Procedure Writeok; Begin Writeln('Ok !'); End; Then, everytime you would write "Writeok" in your program, the program would write 'OK !' in the screen. Let's see a working example: Program Test_8; Procedure Writeok; Begin Writeln('Ok !'); End; Begin Writeln('How are you ?'); Writeok; Readln; End. The above example is stupid, but it's just for you to get the picture. Notice that you can do ANYTHING you want in a procedure, just like in the main block. You can even call other procedures from it. A procedure is like an independant program, with it's one set of instructions and variables... þ Local and global variables The procedure can have it's own variables, independants from the main program. Look at the next example: Program Test_9; Var B:Integer; Procedure Something; Var B:Word; Begin B:=10; Writeln('In the procedure, B=',B); End; Begin B:=5; Something; Writeln('In the main program, B=',B); Readln; End. See what I mean? The main program ignores the changes made to variable B inside the procedure. The variable B of the procedure is diferent of variable B in the main program. So, you come to a new concept. The variables defined outside the procedures are called GLOBAL VARIABLES, and those who are created inside of a procedure are called LOCAL VARIABLES. The diference between them is that global variables can be used in all program, not just in the main block, but in all procedures and functions, while local variables can only be used inside the place they are defined. þ Parameters Imagine now that you wanted to make a procedure to do a complex calculation between the numbers 5 and 7. You would now use something that is called PARAMETERS. They are used like this: Procedure DoCalc(A,B:Integer); Var C:Integer; Begin C:=A+B*B; Writeln('The result is ',C); End; Now, you called the procedure like this: DoCalc(5,7); This works like this. The var A would get the value 5, and var B would get the value 7. Then, the procedure's main code would be executed and the result would be printed to the screen. A parameter can be anything, from a number to a string. You can even have different types of parameters, like this: Procedure Print(X,Y:Byte;S:String); You just use a semi-colon between them. I know this is a little confusing at first, because you're dealing with new concepts, but after you master this, you will get a priceless resource. þ Functions The function is defined in the same way as the procedure, except that you use the keyword FUNCTION instead of the PROCEDURE keyword. They work the same way, but they have a diference. The function returns a value. This can be a number, a char or even a string. The sintax is a little diferent, because of this: Function DoCalc(A,B:Integer):Integer; { This defines function DoCalc, } { that requires two integers as } { parameters, and that returns } { and integer value } Var C:Integer; { Defines local variable C } Begin C:=A+B*B; { This executes the calculation } { and places the result in C } DoCalc:=C; { This tells the function to } { return the value in C. } End; Do call this, you would do something like this: D:=DoCalc(5,7); { D is a global variable, defined as an integer } Writeln(D); or Writeln(DoCalc(5,7); { This saves a variable } There isn't a rule for what to use, procedure or function. It depends on the program you are making. You can mix them up, having a procedure that calls a function or vice-versa. You can even call a procedure with a function as a parameter. There's a lot I haven't told you about procedures and functions, but I will leave that for a future issue. In next issue I will talk about loops and cicles. These are an almost essencial part of a program. þ Introduction to assembly Somebody requested me to do an article about assembly, so here it is. This is a very simplistic view of it, but it should be enough to make you start coding some simple routines in Pascal with assembly. Don't forget that assembly language is very complicated, and only people that had a previous programming experience with some kind of high-level language should try to venture themselves in it. In the previous issue I explained some simple concepts of assembler, things like the what is the BIOS, interrupts and other things like that. I even explained how Pascal works with assembly. In the first issue, I explained what are bytes and bits and their relatives, so I will assume you already know that. So, without further due, I will talk about... þ Segments and offsets This is the more annoying and complicated thing on the PC. Back in the time when dinossaurs ruled the Earth, the guys that designed the original 8088 thought that nobody would ever need to address more than a meg (short for megabyte) of memory, so they decided to built a machine that couldn't access more than one meg of memory. But, for addressing one meg of memory, there was the need for a 20 bit number. Because this wasn't pratical, they've came up with a 'brilliant' way to do it. They would use two 16 bit registers. I know this adds up to a 32 bit number, but that's not the way it works. It works like this: Segment 0010100010001001 Offset 0010101010100101 20 bit adress: 00101011001100110101 !!!! Here you have it... Segments and offsets combine to produce an absolute address of memory. The standart notation for the memory addresses is: Segment:Offset So, if somebody says that somethings in the address 6F1A:3652, that means that it is in the segment 6F1A (in hex, of course), offset 3652 (still in hex). Think of segments and offsets like this. The segment is the number of the page, and the offset is how far in the page the data is... Now, that you know what segments and offsets are, I can move on to... þ Registers Registers are a special kind of variables, that are available to the processor. They perform various functions inside the CPU, and they are a very important part of assembly language. A list of their names, alongside with their uses follows: þ AX AX (also called the accumulator) is a general purpose 16 bit register. It can be acessed as a word, or as two bytes, using AL or AH (the low and the high part). Examples: AX=10 => AL=10 AH=0 AX=256 => AL=1 AH=1 General rule, anything you do to AL or AH will influence AX, because you're manipulating it's low and high bytes. Before I forget: AX = AH * 256 + AL Do you understand it ? AX has some special uses. General rule it is the destination for memory grabs and multiplications. It's also plays a big part in port accessing. þ BX (BH/BL) BX is just like AX... You can also access its lower and higher bytes. BX's special use is usually as an offset register. þ CX (CH/CL) CX is just like AX and BX. It is used as a counter in loops. þ DX (DH/DL) DX is like AX, BX and CX. It is used as destination is calculations and as a port index. þ DI DI is a index register, a 16 bit register that is used for offsets. You can't access it's higher and lower bytes. þ SI Same as DI, but usually DI is paired with the ES segment register, while SI is paired with the DS register. þ DS This is a segment register, and it usually points to the Data Segment. þ ES This is a segment register, that points to the Extra Segment. þ FS This is like ES, but it only exists in the 386+. þ GS Same as FS. The next registers you SHOULDN'T mess, unless you want your computer to crash. þ BP Base pointer. This is an offset to use with the Stack Segment. þ SP Stack pointer. This is an offset to use with the Stack Segment. þ SS This is a segment register, that points to the Stack Segment. þ CS This is a segment register, that points to the Code Segment. þ IP This is the Intruction Pointer, that is used with CS. þ The stack The stack is a temporary storage place, where data doesn't stay for long. It has the same principle of a stack of plates, except that the stack starts in the roof. This follows the order first in, last out, so, you must take the things in the reverse order that you put them in. I know this is confusing, but after some examples, you will feal right at home. þ The flags The flags are like status indicators. They have all sort of indications for you to read and interpret. They only have the value true or false, and you can't access them directly. There are 17 flags, but you only have to know some of them: þ Carry Flag (CF) The carry flag is used for many things. It's like a general purpose flag. It is one of the more used flags. þ Parity Flag (PF) This flag is active when the value returned from last operation is even. þ Zero Flag (ZF) This flag is set when the last operation returned a zero. þ Overflow Flag (OF) This flag is set when the last operation went out of bounds. These are the ones mostly used. There are others, as stated before, but they aren't very used. þ More about segments In pure assembly, you can define the segmentation of your program, but has we are using assembly with Pascal, we must use the Pascal's default segmentation, that is called Dos Segmentation. This means that there are four principal segments. There are addressed by CS, SS, DS, ES. They are called Code Segment, Stack Segment, Data Segment and Extra Segment. þ Code Segment This is where your program resides. You probably now already that a segment can only have 64Kb of size, so you only get 64Kb for the code of your program. Now figure out why is it dangerous to change the CS register. One more thing about the CS register. It pairs up with the IP register to give the computer the address of the current instruction. þ Stack Segment This is the place of the stack. þ Data Segment This is where the data normally goes... It can go to other places, using pointers, but that's another story. þ Extra Segment An extra segment for data. þ Basic instructions Next, it follows a list of the essencial assembly instructions, the ones you should know from back to forth. þ Mov This is the single more important instruction in Asm (short for assembly). The sintax is: Mov to,from The to and from parameters can be almost anything you want. Let's show some examples: Mov AX,40 This puts the imediate value 40 in AX Mov AL,20 This puts the imediate value 20 in AL Mov DH,10 This puts the imediate value 10 in DH Mov CX,BX This puts what's in BX in CX Mov AX,[DS:SI] This puts the word that is at DS:SI in AX Mov [ES:DI],BX This puts what's in BX in ES:DI Mov [ES:DI+5],CX This puts what's in CX in ES:DI+5 Mov DX,[x] This puts what's in var x in DX This is easy enough. þ Int I already talked about this command in last issue. This command calls up an interrupt. Sintax: Int n Examples: Int 10h This calls interrupt 10 (in hex) Int 10 This calls interrupt 10 (in dec) þ Stosb This stores what's in AL in destination ES:DI, and then increments DI by one. It hasn't got any parameters. þ Stosw Just like Stosb, but is transfers whats in AX and increments Di by two. þ Movsb This moves the byte that is in DS:SI to ES:DI, and then increments SI and DI by one. þ Movsw This moves the word as DS:SI to ES:DI, and then increments SI and DI by two. þ Add Sintax: Add dest,source This adds source to dest and stores the result in dest. Dest can't be and imediate value, as obvious. þ Sub Sintax: Sub dest,source This subtracts source from dest and stores the result in dest. þ Mul Sintax: Mul source This multiplies source by AX (if source is a word) or by AL (if source is a byte). The result is stored in AX (if source is a byte) or in DX:AX (if source is a word). þ Push This pushes something onto the stack. The sintax is: Push source Source can be a register or an immediate value. þ Pop This takes off something off the stack. The sintax is: Pop dest Dest is where you want to put the value you take. Remember what I said about first in, last out ? Take a look at these examples: Mov ax,5 AX=5 Push ax Puts AX in the stack Mov ax,10 AX=10 Pop ax Takes the last value put in the stack, in this case, AX=5 Mov ax,10 Mov bx,5 Push ax Push bx This piece of code would swap the values of AX and Pop ax BX. Because first in is the last out. Pop bx So, that's about it... There's lots of other things, but this should cover up the basic. Get some source codes and experiment with them. If you have any doubts, drop me a line and I'll try to explain myself better. If you want some source code, feel free to ask me some... þ Graphics, part II : Palettes þ Introduction The palette is a mistery to most programmers, even professional ones, and often overlooked as beeing not too important. But I'm of those who think that the palette and effects derived from it have a great potencial. To see what I'm saying, just execute the 'Color Blind' demo I made especially for the magazine. I made it in a couple of days, and I think it's fairly good, considering that it pratically just uses the palette. þ What's the palette A palette is a place where colors are stored. Has you should already now, the mode 13h palette has 256 colors. Each one has a number, ranging from 0 to 255, and an intensity value for each of it's components. In the PC, as in real life, colors are the result of the mixture of primary colors. You should have learned about it in the 5th grade. But, in real life, the primary colors are blue, yellow, magenta, black and white. In computerland, there are only three basic colors: red, green and blue. Each one of them can be mixed together in diferent ammounts. But (there's always a but everywhere), NEVER forget that two colors can look just the same in the screen and be completely different. Also, don't forget that when you alter the palette, all the pixels in the screen drawn with that color will be changed. For example, imagine you just set color 53 and 80 with the same RGB (short for red,green and blue components), and filled the screen with pixels color 53 and 80. Well, when you looked at the screen, it would look like it was filled with the same color, but when you changed color 80, the entire screen would change the look entirely. þ How to set and get the palette This is easy. You just have to remember that everything concerning the palette is done through the ports 3C7h, 3C8h and 3C8h. So, let's get the palette of a specific color. All you have to do is to enter the number of the color you want into port 3C7h and then read in the values of the red, green and blue components from port 3C9h. For acessing the ports, you use a Pascal defined array that is called PORT. It is something like the MEM array. The sintax is: Port[number of the port] So, a procedure to read in the RGB values of one color would be like this: Procedure GetColor(Col:Byte;Var R,G,B:Byte); Begin Port[$3C7]:=Col; R:=Port[$3C9]; G:=Port[$3C9]; B:=Port[$3C9]; End; To set the palette, it's almost the same... You put the number of the color you want to change into port 3C8h and then put the values in port 3C9h, in the order red, green and blue. Remember that the intensity is a number from 0 to 63. Procedure SetColor(Col,R,B,G:Byte); Begin Port[$3C8]:=Col; Port[$3C9]:=R; Port[$3C9]:=G; Port[$3C9]:=B; End; This is very easy, tough it has some quirks... Now, let's go to something harder... Setting the whole palette !! The way I do it is like this: first I define two records, one to store the RGB value of one color and the other to store the whole palette. Type RgbItem=Record R,G,B:Byte; End; RgbList=Array[0..255] of RgbItem; Then, you define the procedure to read the palette into a var of the predefined type: Procedure GetPalette(Var Pal:RgbList); Var A:Byte; Begin For A:=0 To 255 do GetColor(A,Pal[A].R,Pal[A].G,Pal[A].B); End; You just get the colors of the 256 colors and store them into an array, where you can change them at you will. To set the whole palette, just do the same, like this: Procedure SetPalette(Pal:RgbList); Var A:Byte; Begin For A:=0 To 255 do SetColor(A,Pal[A].R,Pal[A].G,Pal[A].B); End; As you see, it's fairly easy... But, if you are brave, you'll probably just went and tried the routines. If you do so, you will be disapointed with the results. You'll probably get a screen full of "snow". The "Snow" is the dots that appear in your screen when you change colors. þ The retrace So, how can you stop this? First, you must understand something about monitor theory. As you may already know, the image in your screen is updated by an electron beam, that sweeps the screen, from left to right. When it reaches the end of a line, it goes to another line. When it reaches the end of the screen, the electron beam resets to it's initial position in the upper left corner. This period, while the beam goes from the lower right corner to the upper left one, is called the Vertical Retrace, or Vertical Blank. How is this helpfull? Well, during this period, anything you do to the screen won't show imediatly, so, you can change the palette, draw sprites, or anything else, and it would only show in the next retrace. But, everything has a downside, and the VBL (Vertical BLank) is no exception. The period is very, very short. So that you'll know, it occurs a VBL almost 70 times a second, so, anything you want to do you'll have to do it fast... You should use frequenty the retrace, especially when you do lot's of thing on the screen. But this is enough for setting the whole palette without fuzz. The following procedure waits first for the retrace to begin. It is all in assembler, and it's quite complicated. To the tech-minded out there, we simply get a byte from port 3DAh. If the third bit of the byte is set, that means that VBL in occuring. Procedure WaitVBL; Assembler; Label A1,A2; Asm Mov DX,3DAh A1: In AL,DX And AL,08h Jnz A1 A2: In AL,DX And AL,08h Jz A2 End; So, here you have it... A procedure to test the VBL. Now, the SetPalette procedure should be like this: Procedure SetPalette(Pal:RgbList); Var A:Byte; Begin WaitVBL; For A:=0 To 255 do SetColor(A,Pal[A].R,Pal[A].G,Pal[A].B); End; Easy, isn't it ?... þ Palette effects Now, I will talk about two types of effects, and the application of one of them. The palette by it's own is capable of some nice effects, as I think I've shown in my Color Blind demo. þ Rotations Rotating the palette is a good effect to start coding, and a very effective one. Most of the effects in Color Blind are diferent types of rotations. Rotating the palette is just a matter of making color 0 be color 1, color 1 be color 2, and so forth, until color 255 be color 0. You can also do a reverse rotation, just by changing the direction. Code follows: Procedure RotatePal(Var Pal:RgbList); Var Temp:RgbItem; A:Byte; Begin Temp:=Pal[255]; For A:=254 DownTo 0 Do Begin Pal[A+1]:=Pal[A]; End; Pal[0]:=Temp; End; Study this piece of code, I think it's very simple. This rotates the colors of a palette buffer (type RgbList), not the colors of the screen. After you rotate the buffer, you'd still have to set them, so you'll notice the effect. It can be done with something like this: Var Pal1:RgbList; ....... ....... GetPalette(Pal1); Repeat RotatePal(Pal1); SetPalette(Pal1); Until Keypressed; ....... ....... ....... This would rotate the palette of the screen until some key is pressed. Now, let's move forth to a tougher effect: þ Fades If you watch TV (and who doesn't ?), you'll see that most of the times, the images don't just appear out of nowhere. They slowly fade in or out, the colors slowly transforming to do the image. Well, it's fairly easy to do so in the PC, and it is a bonus, because it's more impressive to see the image slowly appearing than throwing it to the screen altogether. The theory behind a fade is fairly simple. You have a source pallete (usually the screen palette) and a target palette. Than, you compare each color and increase or decrease their RGB values. You compare the RGB values of color 0 of the source palette with the RGB values of color 0 of target palette, and change their values in face of that. The next piece of code was the first fade I've ever made, and it is very badly done. It can be done a lot faster and prettier, but I leave that to you. Procedure Fade(Target:RgbList); Var Tmp:RgbList; Flag:Boolean; Loop:Integer; Begin Repeat Flag:=True; GetPalette(Tmp); For Loop:=0 To 255 Do Begin If Tmp[Loop].R>Target[Loop].R Then Begin Dec(Tmp[Loop].R); Flag:=False; End; If Tmp[Loop].G>Target[Loop].G Then Begin Dec(Tmp[Loop].G); Flag:=False; End; If Tmp[Loop].B>Target[Loop].B Then Begin Dec(Tmp[Loop].B); Flag:=False; End; If Tmp[Loop].RTarget[Loop].R Then Begin Dec(Tmp[Loop].R); Flag:=False; End; If Tmp[Loop].G>Target[Loop].G Then Begin Dec(Tmp[Loop].G); Flag:=False; End; If Tmp[Loop].B>Target[Loop].B Then Begin Dec(Tmp[Loop].B); Flag:=False; End; If Tmp[Loop].R