Let us say you are generating a complex screen numerous times on the fly (for example scrolling up the screen then redrawing all the sprites for each frame of a game you are writing). Do you have any idea how awful it would look if the user could actually see you erasing and redrawing each sprite for each frame? Can you visualize the flicker effect this would give off? Do you realize that there would be a “sprite doubling” effect (where you see two copies of the same sprite next to each other)?

DENTHOR, coder for ...
_____   _____   ____   __   __  ___  ___ ___  ___  __   _____
/  _  \ /  ___> |  _ \ |  |_|  | \  \/  / \  \/  / |  | /  _  \
|  _  | \___  \ |  __/ |   _   |  \    /   >    <  |  | |  _  |
\_/ \_/ <_____/ |__|   |__| |__|   |__|   /__/\__\ |__| \_/ \_/
smith9@batis.bis.und.ac.za
The great South African Demo Team! Contact us for info/code exchange!  

Grant Smith, alias Denthor of Asphyxia, wrote up several articles on the creation of demo effects in the 90s. I reproduce them here, as they offer so much insight into the demo scene of the time.

These articles apply some formatting to Denthor's original ASCII files, plus a few typo fixes.

In the sample program I have included a part where I do not use virtual screens to demonstrate these problems. Virtual screens are not the only way to solve these problems, but they are definitely the easiest to code in.

A virtual screen is this: a section of memory set aside that is exactly like the VGA screen on which you do all your working, then “flip” it on to your true screen. In EGA 640x350x16 you automatically have a virtual page, and it is possible to have up to four on the MCGA using a particular tweaked mode, but for our purposes we will set one up using base memory.

Setting up a virtual screen

As you will have seen in the first part of this trainer series, the MCGA screen is 64,000 bytes big (320x200 = 64000). You may also have noticed that in TP 6.0 you aren’t allowed too much space for normal variables. For example, saying:

VAR Virtual : Array [1..64000] of byte;

would be a no-no, as you wouldn’t have any space for your other variables. What is the solution? I hear you enquiring minds cry. The answer: pointers! Pointers do not use up the base 64k allocated to you by TP 6.0, it gets space from somewhere else in the base 640k memory of your computer. Here is how you set them up:

Type Virtual = Array [1..64000] of byte;  { The size of our Virtual Screen }
     VirtPtr = ^Virtual;                  { Pointer to the virtual screen }

VAR Virscr : VirtPtr;                     { Our first Virtual screen }
    Vaddr  : word;                        { The segment of our virtual screen}

If you put this in a program as it stands, and try to access VirScr, your machine will probably crash. Why? Because you have to get the memory for your pointers before you can access them! You do that as follows:

Procedure SetUpVirtual;
BEGIN
  GetMem (VirScr,64000);
  vaddr := seg (virscr^);
END;

This procedure has got the memory for the screen, then set vaddr to the screen’s segment. DON’T EVER LEAVE THIS PROCEDURE OUT OF YOUR PROGRAM! If you leave it out, when you write to your virtual screen you will probably be writing over DOS or some such thing. Not a good plan ;-).

When you have finished your program, you will want to free the memory taken up by the virtual screen by doing the following:

Procedure ShutDown;
BEGIN
  FreeMem (VirScr,64000);
END;

If you don’t do this your other programs will have less memory to use for themselves.

Putting a pixel to your virtual screen

This is very similar to putting a pixel to your normal MCGA screen, as discussed in part one… here is our original putpixel:

Procedure PutPixel (X,Y : Integer; Col : Byte);
BEGIN
  Mem [VGA:X+(Y*320)]:=col;
END;

For our virtual screen, we do the following:

Procedure VirtPutPixel (X,Y : Integer; Col : Byte);
BEGIN
  Mem [Vaddr:X+(Y*320)]:=col;
END;

It seems quite wasteful to have two procedures doing exactly the same thing, just to different screens, doesn’t it? So why don’t we combine the two like this:

Procedure PutPixel (X,Y : Integer; Col : Byte; Where : Word);
BEGIN
  Mem [Where:X+(Y*320)]:=col;
END;

To use this, you will say something like:

Putpixel (20,20,32,VGA);
PutPixel (30,30,64,Vaddr);

These two statements draw two pixels… one to the VGA screen and one to the virtual screen! Doesn’t that make you jump with joy! ;-) You will have noticed that we still can’t actually see the virtual screen, so on to the next part…

How to “Flip” your virtual screen on to the true screen

You in fact already have to tools to do this yourselves from information in the previous parts of this trainer series. We will of course use the Move command, like so:

Move (Virscr^,mem [VGA:0], 64000);

Simple, eh? You may want to wait for a vertical retrace (see part 2) before you do that, as it may make the flip much smoother (and, alas, slower).

Note that most of our other procedures may be altered to support the virtual screen, such as Cls etc. (see part 1 of this series), using the methods described above (I have altered the CLS procedure in the sample program given at the end of this Part).

We of ASPHYXIA have used virtual screens in almost all of our demos. Can you imagine how awful the SoftelDemo would have looked if you had to watch us redrawing the moving background, text and vectorballs for EACH FRAME? The flicker, doubling effects etc would have made it awful! So we used a virtual screen, and are very pleased with the result. Note, though, that to get the speed we needed to get the demo fast enough, we wrote our sprites routines, flip routines, palette routines etc. all in assembly. The move command is very fast, but not as fast as ASM ;-)