Abe's Demoschool
Part I, mod 13h


Welcome to part I of the Demoschool. In the demoschool I'll guide you through 
different demoeffects such as paletterotation, plasma, fire, sprites, 3d-
vector-graphics, shading, warping, mode-X, Soundblaster and whatever else 
that might show up. Most parts of the demoschool will be in mode 13h or MCGA-mode 
as it is also called. That immediatly brings up the first question:

What is mode 13h?
Well, the answer is easy, mode 13h is the graphic mode of the PC that most games 
and demos are written for. The resolution of mode 13h is 320 pixels horizontally 
and 200 pixels vertically. that makes 320*200 = 64000 pixels totally on the 
screen. The pixels on the screen can have 256 different colours (each pixel can 
actually only have one colour at a time, but that colour can be one out of 256 colours in the palette). As you should know, one byte (8 bits) can take on 
256 different values. This means that each pixel in mode 13h could be represented 
by one byte, and that is also the case. 
Like I mentioned mode 13h has 320*200 = 64000 pixels which is 64000 bytes. 
The screen-memory in mode 13h begins at adress A000:0000h, and ends at adress 
A000:FFFFh. This is 65535 bytes which leaves about 4 invisible rows below the 
bottom of the screen. 
To write a pixel to the screen, first set segmentregister ES to A000h and 
calculate an offset and put it in di then write one colorbyte to es:di. 
The offset is calculated like this:

Offset = 320*y + x

where x is the x-coordinate and y is the y-coordinate. (0,0) is at the upper 
left corner of the screen and (319,199) is at the bottom right corner of the 
screen. Here is a table of the coordinates and their respective offsets.

	(0,0)   (1,0)   (2,0)   (3,0)   .       .       .       (319,0)
0       0       1       2       3       .       .       .       319     X-axis
1       320     321     322     323     .       .       .       639 (319,1)
2       640     641     642     643     .       .       .       959 (319,2)
.       .       .       .       .       .       .       .       .
.       .       .       .       .       .       .       .       .
.       .       .       .       .       .       .       .       .
199     63680   63681   63682   63683   .       .       .       63999
	(0,199) (1,199) (2,199) (3,199) .       .       .       (319,199)
Y-axis

Mode 13h is said to be in a linear memory model that means that the bytes 
follow each other one after another. 
To write a pixel with colour 1 (default blue) at (3,2) you should write value 
1 to A000:(320*2 + 3) = A000h:643. Try it, it works. 
Here comes an unoptimized example in C:

void putpixel(int x,int y,char color)
{
 int offs;

offs=320*y + x;                 //calculate the offset using the given formula
asm     mov     ax,0a000h       //you cant move a value directly to es
asm     mov     es,ax           //move the screenadress to es
asm     mov     di,offs         //and the calculated offset to di
asm     mov     al,color        //and the colorbyte to al
asm     mov     [es:di],al      //finally move the pixel to the screen
}

Before putting a pixel to the screen you've gotta be in the MCGA-mode 13h.
Switching into mode 13h is done like this:

	mov     ax,13h  ;move the number of the mode to ax
	int     10h     ;and enter the mode using the videointerrupt 10h

Switching back to textmode 3 is done like this

	mov     ax,3    ;mode 3 is the dos standard mode 80*25 16 color textmode
	int     10h     ;enter textmode 3

In C you could write a function to change the mode like this:

void setmode(int mode)
{
asm     mov     ax,mode
asm     int     10h
}

To switch into mode 13h you would write setmode(0x0013); at the 
beginning of main and to switch back into textmode write setmode(3);

To optimize the putpixel routine you could replace the multiplication, 
which is very slow, with shifts and adds. As you should know, shifting left 
1 bit is the same as multiplicating by 2, shiftleft 2 bits is the same as 
multiplicating by 4 and so on. The trick is to split 320 up into powers of 2, 
320 is the same as (256 + 64). Y*320 is the same as (y leftshift 8) + 
(y leftshift 6). Look at the sourcecode for an implementation of the optimized 
putpixelroutine using inline-assembler in C.

What colour the pixel will have depends on two things: the colourvalue you 
write to the screen (0-255) and what colour that value represents in the 
palette. The palette has 256 entrys (0 to 255). Each colour in the palette 
is represented by three bytes, one each for the Red, Green and Blue component of the colour. Each value can
vary between 0 and 63. The value gives the intensity of the colour, 0 no 
intensity, 63 strong intensity. This gives 64^3 possible colours.

Examples of common colours

	Red     Green   Blue    Colour
	______________________________
	0       0       0       BLACK
	63      0       0       RED
	20      0       0       DARK RED
	0       63      0       GREEN
	0       0       63      BLUE
	63      63      0       YELLOW
	63      32      0       ORANGE
	63      0       63      PURPLE
	0       63      63      CYAN
	63      63      63      WHITE
	32      32      32      GRAY
	10      10      10      DARKGRAY
	And     So      On

The MCGA mode 13h is always started with the same palett (0 black, 1 blue . . .). 
But it is easy to change the palett by your own. It is done by writing to 
ports 3c8h and 3c9h. This is not as hard as it sounds. You just write the 
indexnumber of the first color you intend to change to port 3c8h. And then 
write the Red Green and Blue values to port 3c9h. After writing 3 bytes to 
port 3c9h the index automatically increments itself.

If the palett is stored in an array pal (3*256 bytes long). Let's say that 
colour 0 is black, colour 1 is blue and colour 2 is gray, then pal will 
start like this: 0,0,0, 0,0,63, 32,32,32, . . .

Here is a C-function which sets the palette to the values of the array pal:

void setpal(char*pal)
{
 int i;
 outp(0x3C8,0);         //begin with colour 0
 for(i=0;i<256*3;i++)
 outp(0x3C9,pal[i]);    //send the palette-data to port 3c9h
}

The most common way of handling the palette is to make the array pal in a 
specially written program for the purpose and save the palette to a file on 
disk. This palette file is easy to recognise because it is 3*256 = 768 bytes big.
In the demo you just load the palette file from disk to an array and sets it 
with the function above.

In assembler you can use a very fast instruction, rep outsb, to set the palette, 
rep outsb sends CX number of bytes from DS:SI and sends them to port DX. 
SI is incremented after each byte. 
Here is an assembler routine to set the entire palette.

	mov     dx,03c8h        ;move portnumber to dx
	xor     al,al           ;reset ax
	out     dx,al           ;send 0 to port 3c8h
	lds     si,pal          ;setup ds:si to point to pal
	mov     cx,3*256        ;move number of bytes to cx
	inc     dx              ;dx = 3c9h
	rep     outsb           ;send 768 bytes from pal to port 3c9h

A common demoeffect is to repeatidly rotate the array pal and calling 
setpal(pal). Among other effects you could use this technique to make a static 
plasma effect. First setup the array pal to contain smooth colour runs between 
some nice colours. Then draw a nice picture on the screen using the sine and 
cosine functions and at last rotate the palette and set it over and over again.

The picture doesn't have to be a plasma picture you could draw just any picture 
and watch how it looks when the palette starts rotating. To rotate the array 
pal just save the first colour (the first three bytes) and then shift all 
colours down one step in the array (three bytes) and at last set the last colour 
to the saved colour. 
Here's the C-code for rotating the palette. 
(it actually rotates colours first to last in the palette)

void rotpal(char*pal,int first, int last)
{
 char r,g,b;
 int i;

 r=pal[first*3 + 0];    //set r,g,b to the first colour
 g=pal[first*3 + 1];
 b=pal[first*3 + 2];

 for(i=first*3;i<(last+1)*3;i++) //move all colours down one step
 pal[i]=pal[i+3];

 pal[last*3+0]=r;       //set the last colour to the saved first colour
 pal[last*3+1]=g;
 pal[last*3+2]=b;

 wtsync();      //wait until the electron beam finishes this frame
 setpal(pal);   //set the rotated pal
}

You don't have to send first and last as parameters to the function but it has 
some advantages. In the example program I use an array that has more than 256 
colours and each time I rotate the entire palette, except colour 0, but I only 
set the 256 first colours. I don't rotate colour 0 because I dont want the 
backgroundcolour to change. Try including colour 0 in the rotation to see what 
I mean.
The wtsync function should always be called before changing the palette to avoid 
"snow" due to bad syncronization with the electron beam. 
Wtsync waits for the  vertical retrace which is when the electron beam has 
finished one frame and jumps back to draw the next.

You could say that the example program is halfway to a real plasma effect, 
I'll show you a real plasma in a later part of the demoschool.

That sould be all for now, view the C-source for comments and implementation.

951213 Abe Raham

mail:dat94avi@bilbo.mdh.se

s-mail: Albert Veli
	Spisringsg. 9
	724 76 Vsters
	SWEDEN

