Last month we discussed sprite routines and the associated problems such as flicker and shear. One solution to the problems came in the form of a routine designed around two ‘workspace’ screens which were used in preparing our updated frame before updating the main screen. This system has been used in many games for the Spectrum in one form or another. Let us just review the system before examining a full demo routine.
The two workspace screens are byte for byte equivalents of the normal Spectrum screen stored at 16334 (6144 bytes long not including the colour attributes). I say byte for byte equivalents but because they are internal and not displayed, they can be a more sensible layout. In the demo routine following, the two workscreens are arranged as 192 lines each of 32 bytes per line. In order to step down 1 pixel row on the normal Spectrum screen we have to do a fairly slow calculation. To step down 1 pixel row in our workscreens we only have to add 32 to our pointer — anywhere on the workscreen. To summarise then, the two workspace screens are just temporary stores for the sprite routine.
At game initialisation, workspace A is initialised with a copy of the background picture information. Workspace B is ignored at this stage. At the start of each game loop, the contents of workspace A are copied as quickly as possible to workspace B. This effectively erases workspace B. Now we draw in to workspace B our sprites and other dynamic features. Finally, we copy as fast as possible the contents of workspace B to the visible screen where our eyes can see the newly updated frame.
1 WORKSPACE A — WORKSPACE B
2 DRAW SPRITES TO WORKSPACE B
3 WORKSPACE B — SCREEN!
The system is very easy to use and very simple. The negative points are that a) There is a time penalty with moving data and b) We need more memory for the workcreens, which is wasteful. Speed is always a problem but the memory being wasted is not so much of a problem with 128K available. On the plus side, we can do some clever tricks quite easily with this system such as scrolling the background. The demo program does just this. Type in the machine code program with your assembler (what do you mean, you haven't got one yet?!) and assemble it to the ORG address supplied i.e. 45056 (0B000H). Save the assembled code with a SAVE "M-CODE" 45056,3287. To run the code type in RANDOMIZE USR 45056 from BASIC and just watch. To return to BASIC you press Shift + Space or Break.
On entry to the routine, the sub-routine INIT_DIR sets the direction flags for each of the 12 sprites into random directions. Bit 7 of the flag bytes controls the Up/Down direction while Bit 0 controls the Left/Right direction. The next routine — MOVE_SCRN — copies the Spectrum screen to workspace A. For this reason, do not run the routine following a CLS — or you will not see the sprites running over a background. Preferably do a LIST of some BASIC text before running the routine. At the label LOOP, we call the BREAK key test routine in the ROM. This returns No Carry if it is being pressed. If it is, then the program will return to BASIC.
Next comes the copying of workscreen A to workscreen B coupled with the scrolling of the background. For this demo, I am doing the scroll in a slight cheat — I am copying the data from workscreen A from a varying base address each loop — see if you can work it out for yourself how it scrolls! We now call the routines MOVE_SPR and DRAW_SPR, which jointly update the new sprite positions (random of course) and draw them into workscreen B. Finally, the contents of workscreen B are copied to the Spectrum screen at MOVE_WKSP.
With the program are a few points of interest. I have often received letters from people asking ‘how do you get several sprites on the screen all at the same time?’ In a routine as simple as this, the principle is still the same as a larger complicated game. We have a set of variables for each sprite (in this case 3 bytes per sprite) and we execute the same routine 'n' times with different variables each time. The sub-routine in this case is MOVE_SPR. It uses IX as a variable pointer and the ‘B’ register as a counter. There is no mystery as to how we have one sprite or in this case a dozen of them. In a typical game, the sprites may have 40 bytes of variables with various parameters but as I state above, the principle is the same.
Previous article in series (issue 91)
Next article in series (issue 93)