Resource centre for ZX Spectrum games
using Manic Miner and Jet Set Willy game engines
Archive of the
Manic Miner & Jet Set Willy Yahoo! Group
messages
|
|
||
|
|
Message: 4406
Author: john_elliott_uk
Date: 21/08/2004
Subject: Re: Comments from Geoff Eddy re. Patch Vectors
--- In manicminerandjetsetwilly@yahoogroups.com, "Alexandra"
> Thanks for the pointers guys, though most of it is still over my
One way to do this is to look at the existing JSW disassemblies, and
> head. I think I need to learn about opcodes, or something. Basic
> stuff like how to PEEK and POKE in machine code etc,,..
try to understand how something that you're already familiar with, works.
Before I start, I'll mention processor registers. The processor can
access any byte in memory, but it also has a number of registers,
which act something like BASIC variables. In general, processor
instructions work on registers rather than memory - you get some data
into a register, fiddle with it, and put the result back. Registers
aren't interchangeable - you need to choose the right register for the
job at hand.
For the purposes of fiddling with JSW, the registers are:
A, B, C, D, E, F, H, L, IX and IY.
The registers with one-letter names hold one byte (0-255) and the
ones with two-letter names hold two (0-65535). It's possible to pair
up B and C (as BC), D and E (as DE) and H and L (as HL). It's also
possible to split up IX and IY into two one-byte registers, but this
isn't very common (though it turns up in the JSW code).
The F register is usually treated as 8 separate bits rather than a
byte. Arithmetic operations set bits in this register, and the jump
instructions (JP and JR) can check these bits and jump depending how
they're set. The two most useful bits are Carry and Zero.
All that out of the way, let's let rip with a disassembler, or the
opcode table in the Spectrum manual. The vertical guardian movement
code goes (in part):
ld a,(ix+3)
add a,(ix+4)
ld (ix+3),a
cp (ix+7)
jr nc,R91AE
cp (ix+6)
jr z,R91A8
jr nc,R91B6
R91A8:
ld a,(ix+6)
ld (ix+3),a
R91AE:
ld a,(ix+4)
neg
ld (ix+4),a
R91B6:
ld de,8
add ix,de
It helps to know at this point that IX is the address of the current
guardian. An approximation to the above in BASIC would be:
10 LET a = PEEK(ix + 3)
20 LET a = a + PEEK(ix + 4)
30 POKE (ix + 3), a
40 IF a >= PEEK(ix + 7) THEN GOTO 90
50 IF a = PEEK(ix + 6) THEN GOTO 70
60 IF a >= PEEK(ix + 6) THEN GOTO 120
70 LET a = PEEK(ix + 6)
80 POKE (ix + 3), a
90 LET a = PEEK(ix + 4)
100 LET a = -a
110 POKE (ix + 4),a
120 LET de = 8
130 LET ix = ix + de
We can then apply knowledge of the vertical guardian format. Byte 3
is the Y-coordinate, and byte 4 is the speed. So the first three lines
(10-30 in the BASIC version) are adding the current speed to the
guardian's Y-coordinate.
Note that in BASIC you could do this in one line:
POKE (ix+3), PEEK (ix+3) + PEEK(ix+4)
But this isn't possible in Z80; you have to use the A register to
hold the value you're working on.
The next few lines are to do with comparing the new Y-coordinate
(which is still in the A register) to the values at (ix+6) and (ix+7)
- the guardian bounds. And this is where my BASIC diverges a little
from my assembly. In BASIC, there are three comparisons (three IF
statements); but in the Z80, there are only two. This is because of
the way the "if" and "then" parts of the comparison are done.
The first comparison is
cp (ix+7)
jr nc,R91AE
After the CP instruction, the Carry flag is set if the value in A is
less than the value at PEEK(ix+7). JR NC means jump if Carry is not
set - ie, A is greater than or equal to PEEK(ix+7).
The next comparison is
cp (ix+6)
jr z,R91A8
jr nc,R91B6
After the CP, Carry is set if A is less than PEEK(ix+6) and the Zero
flag is set if A is equal to PEEK(ix+6). So there are three possibilities:
1. A is equal to PEEK(ix+6). The first jump takes effect.
2. A is greater than PEEK(ix+6). The second jump takes effect.
3. A is less than PEEK(ix+6). Neither jump takes effect.
In this case, steps 1 and 3 end up in the same place; but that isn't
necessarily the case.
On we go:
ld a,(ix+6)
ld (ix+3),a
is left as an exercise to the reader. It's also left as an exercise
why JSW does this with the top bound of vertical guardians, but not
the bottom bound.
ld a,(ix+4)
neg
ld (ix+4),a
Again, the Z80 needs the number to be loaded into the A register to
make it negative. It can't just do NEG (IX+4). As for what it's doing
- since byte 4 of the guardian is its speed, this makes it negative,
reversing the guardian.
ld de,8
add ix,de
This adds 8 to IX so it's pointing at the next guardian. IX is an
address (two bytes) so we have to add another 2-byte value to it. In
this case, Matthew Smith chose DE, but he could have used BC.
