Welcome to the TJ Software first ever VGA-Tutor. This is a contribution for the x2ftp tutorial contest 97. This is a simple vga-tutor for beginners, for those who know Virtual screens, sprites and the basic vga stuff I recommend Denthor Smith's tutor. Those can be found at ftp://x2ftp.oulu.fi/pub/msdos/programming/docs/ I expect some knowledge in Pascal to understand this tutorial. I'm a 16 year old boy in Sweden so excause me for the misspellings. Let's begin *THE MCGA ($13) MODE* The most common VideoMode today is still mode 13, MCGA. MCGA features a resolution of 320*200 pixels with 256 different colors. But you can achive higher resolution with a method called Mode-X. But we won't talk about that today. The 320*200 resolution means that the screen consists of 320 pixels, or points, horisontally and 200 pixels vertically. 320*200=64000 bytes. 64000 bytes=64 kb. The screen takes only 64 kb of memory so you can program the graphics easily without needing to use EMS or XMS. *STARTUP THE MCGA MODE* To startup the mcga mode or $13 mode is very easy. What the f*** is $13 mode, I hear you cry. Well, that is the Dos interrupt for the MCGA mode. I can't explain this because I don't undetstand it myself. I think the esaiest way to startup is to use a simple Assembler routine. It goes like this: Procedure InitMCGA; begin asm { Startup asm use } mov ax,$13 { Tells the computer that we'll use $13 mode } int 10h { This does something with the interrupt 10h, end; that is the interrupt that sets graphics modes } end; { There good are lists of all the graphics mode, available on the net, check 'em out } The procedure above use have to use at the beginning of the program. This routine you use at the end of the program to set the $3 mode. That is videomode the DOS uses, probably the Videmode you are looking at when you're reading this. If you're not in Windows of course. Procedure InitText; begin asm mov ax,$3 int 10h end; end; *PIXEL PLOTTING* Now you know how to initialize the graphic modes. But with only this knowledge you can't do any cool games or programs. The first routine you must have is the PutPixel routine. There are many ways of plotting a pixel on the screen, some good and better. The first routine I learned was this Procedure PutPixel(x,y:integer; c:byte); begin mem[$A000:(y*320)+x]:=c; { This calculates AND plots the pixel } end; There are lots of better routines than this, Denthor's tutorials explains this much better. You might wonder what $A000 is, well, that is the position of the VGA memory ,( You know the 64000 bytes ), in the conventional memory. The conventional memory is the memory the is used when you use GetMEM and those procedures. If you have DOS you can type MEM at the prompt to see how much memory you have. Anyway, you might want tell the computer what color a pixel has. This is done with the GetPixel routine. This is really the PutPixel routine, only it's twisted. Function GetPixel(x,y:integer): byte; begin GetPixel:=mem[$A000:(y*320)+x]; end; Now you have two routines that you can use to write simple programs, maybe a starfield screensaver. But you will soon find out with these two routines you can't do the cool games that ID and EA does. First we'll talk about colors. *COLORS* As I said, there are 256 different color. Each color is a mixture of three values, Red, Green and Blue. Each of these values can be from 0 to 63. These values together makes the palette. With Pascal language it looks like this: Type RGB=record r,g,b:byte; end; Palette=Array[0..255] of RGB; So if Color[1] is (63,0,0) then it's all red, if it's (0,63,0) it's all Green and if it's (0,0,0) it all black. In the beginning you don't have to worry about the palette so much but when are making more advanced games you might want to change one color to another to make a cool effect. Again, Denthor's tutorials pay a lot more attention to this subject. But you can have a routine changing the values of a specific color. This procedure I stole from Denthor, please forgive me Denthor. Procedure Pal(Col,R,G,B : Byte); assembler; asm mov dx,3c8h mov al,[col] out dx,al inc dx mov al,[r] out dx,al mov al,[g] out dx,al mov al,[b] out dx,al end; *SPRITES* A sprite is a chunk of graphics, it might be a Man that is moving across the screen or a IMP in the DOOM game. The Sprites in Doom and Duke Nukem 3d are 3d-sprites. We won't talk about those here, but there are tutorials on this too. The sprites we will talk about is plain 2d sprites. A sprite is actually a array of byte, that is put onto the screen. You might have done a starfield screensaver and then you used a pixel for each star. Now, you want more good-looking star that are lighter in the middle and a little darker on the corners. This is easily done with sprites. As I said the sprite is an array of byte, so lets do one. Let's say our stars should be 5 pixels wide and 5 pixels high. Type Star=Array[1..5,1..5] of byte; For small sprites like this we can't use a constant for the stars graphics. Const Star=Array[1..5,1..5]of byte= ((0,23,23,23,0), (23,26,26,26,23), (23,26,29,26,23), (23,26,26,26,23), (0,23,23,23,0)); The numbers is the numbers of the colors, in the standard palette these are different shades of gray. Now to put the sprite to the screen is easy. Procedure DrawStar(x,y:integer); var i,j:integer; begin for i := 1 to 5 do { Five pixels wide } for j := 1 to 5 do { Five pixels high } if Star[i,j]<>0 then { Check if color is 0 then don't draw, called masking } PutPixel(i+x,j+y,Star[i,j]);{ Puts the pixel at the right position } end; This routine is the very basic routine for drawing sprites. If you draw this star outside the borders of the screen (0,0,320,200) strange things might happen. You can avoid this by clipping the sprites. But I'm not the right person to teach you that. After you draw the star you have to erase it at some point. You can do this by draw the star again but change the drawing routine, like this. PutPixel(i+x,j+y,0); Pretty simple, huh? But this is not a good way of erasing and it doesn't look very good. At least not on slow computer. There is another way of erase the stars, this is with Virtual Screens. *VIRTUAL SCREENS* I told you about the conventional memory (640kb) and this memory can be used for a lot of fun things. As I said, the $13 screen took 64000 bytes of memory. Now, we want to make an fake screen that is invisible. Type VirtScr=Array[0..64000]of byte; {This is acutally 64001 bytes big but this is for safety } VirtPtr=^VirtScr; { Point to the Virtual screen } Var VirScr=VirtPtr; We also have to have another variable that contains the segment of the Virtual Screen. Var Vaddr:Word; To initialize the screen do this. Procedure InitVirt; begin Getmem(VirScr,SizeOF(VirScr^)); Vaddr:=seg(VirScr^); end; It is very important to Initialize the Virtual Screen before touching it. Otherwise you system probably will crash. Now we can use the PutPixel routine and modify it a little. Procedure PutPixelVirt(x,y:integer; c:byte); begin mem[Vaddr:(y*320)+x]:=c; { This calculates AND plots the pixel to Vaddr } end; The GetPixel routine can also be changed this way. When you are finished with the Virtual screen you must free the memory of the VirScr, otherwise other programs won't be able to use it until you restart your computer. Freemem(Virscr,SizeOF(Virscr^)); Now you will notice that the pixels that are draw to the Virtual Screen won't be visible. To make the virtual screen visible you have to flip it to the standard VGA $13 screen. This is also very simple. procedure flip; assembler; asm push ds mov ax, $A000 mov es, ax mov ax, Vaddr mov ds, ax xor si, si xor di, di mov cx, 32000 rep movsw pop ds end; Now after you have flipped the you can erase the Virtual Screen. Procedure ClearVirt; var i:longint; begin for i := 0 to 64000 do VirScr^[i]:=0; end; Of course you can have more than one Virtual Screen. Now you know the basics of the VGA graphics programming. With these routines you can do simple games, like my Street Race 1.0. You can also find that at X2FTP.OULU.FI /pub/msdos/programming/contest97/game_src/F1.ZIP If you want more advanced graphics programming check out Denthor's tutorials and TeleMachos tutorials. TeleMachos is also available at x2ftp. { Cut here } Program Starfield; Uses Dos,Crt; Const Star:Array[1..5,1..5]of byte= ((0,23,23,23,0), (23,26,26,26,23), (23,26,29,26,23), (23,26,26,26,23), (0,23,23,23,0)); MaxKlutts=50; type klutt=record x,y,deg:real; c:byte; end; kluttarea=Array[1..MaxKlutts]of klutt; RGB=record r,g,b:byte; end; Palette=Array[0..255] of RGB; VirtScr=Array[0..64000]of byte; {This is acutally 64001 bytes big but this is for safety } VirtPtr=^VirtScr; { Point to the Virtual screen } Var VirScr:VirtPtr; Vaddr:Word; i,j:integer; ball:kluttarea; Procedure InitMCGA; begin asm { Startup asm use } mov ax,$13 { Tells the computer that we'll use $13 mode } int 10h { This does something with the interrupt 10h,} end; { that is the interrupt that sets graphics modes } end; { There good are lists of all the graphics mode,} { available on the net, check 'em out } Procedure InitText; begin asm mov ax,$3 int 10h end; end; Procedure InitStars; var i,j:integer; begin for i := 1 to MaxKlutts do begin ball[i].x:=Random(318)+1; ball[i].y:=Random(199)-5; ball[i].deg:=Random(6)+1; end; end; Procedure ClearVirt; var i:longint; begin for i := 0 to 64000 do VirScr^[i]:=0; end; procedure flip; assembler; asm push ds mov ax,$A000 mov es, ax mov ax,Vaddr mov ds, ax xor si, si xor di, di mov cx, 32000 rep movsw pop ds end; Procedure PutPixelVirt(x,y:integer; c:byte); begin mem[Vaddr:(y*320)+x]:=c; { This calculates AND plots the pixel to Vaddr } end; Procedure InitVirt; begin Getmem(VirScr,SizeOF(VirScr^)); Vaddr:=seg(VirScr^); end; Procedure ShutVirt; begin Freemem(VirScr,SizeOF(VirScr^)); end; Procedure Pal(Col,R,G,B : Byte); assembler; asm mov dx,3c8h mov al,[col] out dx,al inc dx mov al,[r] out dx,al mov al,[g] out dx,al mov al,[b] out dx,al end; Procedure DrawStarVirt(x,y:integer); var i,j:integer; begin for i := 1 to 5 do { Five pixels wide } for j := 1 to 5 do { Five pixels high } if Star[i,j]<>0 then { Check if color is 0 then don't draw, called masking } PutPixelVirt(i+x,j+y,Star[i,j]);{ Puts the pixel at the right position } end; Procedure MoveKlutts; var i:integer; begin for i := 1 to MaxKlutts do begin ball[i].x:=ball[i].x+(ball[i].deg/2); if ball[i].x>319 then ball[i].x:=0; if ball[i].y>199 then ball[i].y:=0; end; end; Procedure DrawKlutts; var i:integer; begin for i := 1 to MaxKLutts do begin DrawStarVirt(Round(ball[i].x),Round(ball[i].y)); end; end; begin InitMCGA; InitVirt; ClearVirt; InitStars; repeat ClearVirt; MoveKlutts; DrawKlutts; Flip; until keypressed; ShutVirt; InitText; end.