|★ APPLICATIONS ★ CREATION GRAPHIQUE ★ Sprite Designer ★|
|Sprite Designer|Computing With the Amstrad)||Applications Creation Graphique|
SO far we have looked at how the Mode 0 screen memory is organised and by the end of the last article we had developed quite a powerful sprite print routine. This isn't the end of the story, however — far from it. There's still a long way to go.
This month I'll list the final sprite routine plus a sprite editor to enable you to design your own sprites.
The editor is in Basic, but for the machine code you'll need an assembler. RAW, the assembler listed in the July issue of Computing with the Amstrad is perfectly adequate for the short programs that we are writing.
If you typed in the examples last time, you'll have a simple print routine that is capable of printing any size multicoloured character at any screen address.
This routine simply stores the sprite data directly in the screen memory to display the character. This has the unfortunate side-effect of destroying whatever is already there, which is not at all sprite-like. We need to modify it slightly to overcome this.
First, though, let's have a look at this month's programs and see what they do. It will be easier to follow the explanation once you've seen the machine code running.
Program I is an example sprite to use with the machine code program. You'll need to enter and run this before you run Program II. It's a man walking his dog on the beach by the sea on a sunny day. If you don't believe me, just run it and see!
It was created using the sprite editor - Program III - and is two characters wide, three high, and uses 10 different pens and inks.
It's not the sort of sprite you would normally have, but it does show the speed and power of the sprite routine. When you've seen it, imagine trying to print it and move it around from Basic. I wouldn't even attempt it.
Save Program I after running it and enter Program II. To run the sprite demonstration enter:
MODE 0:CALL &8000
and use the cursor keys to move the sprite round the screen. Pressing Escape at any time returns to Basic.
Take a look at the print routine and see how it differs from the one developed last time. It starts at &8087 in Program II and is in two parts.
The first erases the old sprite and starts at &8087. The second prints the sprite at the new position and is at &80AD. The second part is also used by the initialisation routine to put the character on the screen.
This final print routine uses the XOR method to print the sprite. Each item of sprite data is collected and exclusively ORed with whatever is in the screen memory. loop2 between &8098 to &809D in Program II shows how this is done.
The big advantage of the XOR method is that when the sprite is moved the screen is restored to whatever it was before.
Mike Bibby's Bits and Bytes in the September issue explored the use of logical functions such as XOR. If you're not sure what exactly it does, then it's well worth looking up.
Basically, when two bytes are XORed they are compared bit for bit and the result set according to the following rules:
1 XOR 1 = 0
If the screen byte is &00 and the sprite data is &CC then XORing the two will give &CC. When the sprite is moved the data is again XORed with the screen memory and as the screen memory is now &CC, XORing it again with &CC results in &00, which is what it was before the sprite was printed.
Suppose that the screen byte is &0C and the data byte &CC. When these are XORed we get &C0 which is not what we want. All it means is that the colours will be upset if the sprite is placed on a background which isn't zero.
When &C0 is XORed again with &CC we get &0C back again so the background is restored.
If that's confused you, try printing a few characters on the screen before calling the sprite routine. Then move the sprite over them and you'll see what I mean.
The colour problem is easily solved by redefining the inks so that they produce the right colours. Look up the simple Basic sprites article in the April issue for more information.
Before calling print, the new address of the sprite is placed in new and the old address in old. Note that the instruction at &808B is LD HL,0 which is a three-byte instruction, and that old is set to &808C, the second byte of it, at the start of Program II.
This means that the two zero bytes in the instruction will be replaced by the old address of the sprite.
This saves time as LD HL,nn is faster than LD HL,(nn). The same technique is used for some of the other variables.
The size of the sprite in columns and rows is passed in H and L. The sprite designer will tell you the size.
There are also a couple of extra variables to be set. These are olddata and newdata which make it possible to animate characters by pointing olddata to sprite 1 and newdata to sprite 2. The sprite at the old position is XORed with olddata and newdata is placed at new.
We saw in the first two articles that the screen is made up of rows of pixels split into groups of eight - the lines that we LOCATE and PRINT on, and that the address of each row of pixels in a group is separated by &800.
The print routine holds the address of the sprite in the HL register pair so &800 is added to get the next row. This is accomplished by passing H to A, adding &8 and passing the result back to the H register. It's faster than loading the BC pair with &800 and adding HL and BC.
The sprite print routine is fairly straighforward. When writing it I decided to go for speed rather than compactness, so it may seem longwinded in places, but it's the fastest routine I've come up with so far.
That's enough about the sprite print routine. Now we'll have a look at the routines to actually move the sprite.
There are four sections labelled up, down, left and right. Each section reads the keyboard and moves the sprite in the appropriate direction.
I'm not going to explain them all as they aren't that different from each other. I'll go through the up routine at &8039 which is the most complicated. If you can follow that, you can follow the others, hopefully.
One of the firmware calls to read the keyboard is at &BB1E. It expects the key number to be in the A register and returns with the Zero flag clear if the key was pressed.
The cursor up key is key 0 so the first instruction XOR A sets the A register to zero and is followed by a call to the firmware. The Zero flag is tested and if the key isn't being pressed control jumps to the next section.
The old address of the sprite is kept on the stack so this is POPped into HL. As there ace two horizontal pixels per byte in Mode 0, the address is decremented by &1000, (2#&800). This is two pixel rows so that the sprite moves up the same amount it moves when travelling horizontally.
To subtract &1000 from HL all that is necessary is to subtract &10 from H by transferring it to the A register. A bonus is that a copy of the high byte of the address is left in A.
The screen memory starts at &C000 so &C0 is subtracted from A, the high byte of the sprite address.
If the Carry flag is set the new address must be off the top of the screen so a correction factor is added to HL.
This situation occurs between groups of eight pixels because of the way the Amstrad maps the screen memory.
The program jumps back to the start again to print the sprite at the new position. Note that there is a call to &BD19 just before the call to the print routine. This prevents flicker by waiting for frame flyback.
Program III is the sprite editor. It contains full instructions and is quite easy to use. Two sprites, up to two characters wide and three high, can be held by the designer at the same time to enable you to work out an animated sequence.
There are commands to mirror sprites vertically and horizontally and a sprite can also be rotated through 90 degrees.
When you have completed the design, the sprite data is printed out as data statements with line numbers. Either the data can be saved directly, or the program ended and the data statements entered using the copy key. Replace Program I with your own data.