What Does It Do?
This isn’t all that complex, or probably even all that unique - there aren’t that many multiplayer games for the ZX Spectrum (or maybe just that I’ve not disassembled many!) Still, it gave me a bit of an “oh yeah, of course” moment when I saw how it’s been done.
Rampage uses three large main characters; George, Lizzie and Ralph. The sprites are organised with a “Sprite Information” table and each sprite has an ID. So, when a monster sprite is needed, the game calls on this routine:
Subroutine: Draw Monster Sprite
; Draw Monster Sprite
;
; ------------------------
; | Input |
; ------------------------
; |A |Sprite ID |
; |BC|Screen co-ordinates|
; ------------------------
;
; This is a wrapper around PrintSprite which simply takes the given sprite ID
; and applies a "modifier" if the monster is either Lizzy or Ralph. This
; then alters the sprite ID to be the correct value for the active
; monster; bit 6 for Lizzy, bit 7 for Ralph.
@label=DrawMonsterSprite
c$D9BB LD L,A ; #REGl=#REGa.
$D9BC LD A,($D251) ; #REGa=*MonsterSpriteModifier.
$D9BF OR L ; Set the bits from #REGl.
$D9C0 JP $D6C9 ; Jump to PrintSprite.
How Does It Work?
Keep in mind hexadecimal is used here
(Skoolkit
syntax is e.g. $4000
which is equivalent to address 16384
).
Also, the bits are referencd from bit 7 to bit 0 (left to right).
Each monster is processed in a loop and in this loop the following config is set:
g $D251 Active Monster Sprite Modifier
@ $D251 label=MonsterSpriteModifier
D $D251 When monster frames are drawn, this is used with an OR to set a bit
. which changes the sprite to the appropriate character.
. See DrawMonsterSprite.
. -----------------------------
. | Byte | Bits | Monster |
. -----------------------------
. | $00 | 00000000 | George |
. | $40 | 01000000 | Lizzy |
. | $80 | 10000000 | Ralph |
. -----------------------------
B $D251,$01
The modifier, as it says above, is used with a logical OR command to alter the given sprite ID, so it reflects the same frame but for the other characters.
The logical OR, at its very simplest, works like this:
Input A | Input B | OR Result |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
So if either input bit is set, then the resulting bit is also set.
So, if the frame sprite ID is $01
then the following applies:
Hexadecimal | Decimal | Character | Sprite |
---|---|---|---|
$01 | 1 | George | |
$41 | 65 | Lizzie | |
$81 | 129 | Ralph |
I’ve included the decimal notation here for reference but obviously it’s much easier to look at in hexadecimal.
- For George, the sprite ID is unchanged as his modifier has no set bits.
- For Lizzie, bit 6 is set - so the sprite ID becomes
$41
. - And for Ralph, bit 7 is set - so the sprite ID becomes
$81
.
Flow Diagram
block-beta columns 9 George:9 GeorgeBitsLabel["Bits"] block:GeorgeModifierBits:8 gm7["Bit 7"] gm6["Bit 6"] gm5["Bit 5"] gm4["Bit 4"] gm3["Bit 3"] gm2["Bit 2"] gm1["Bit 1"] gm0["Bit 0"] end GeorgeModifierLabel["Modifier"] block:GeorgeModifiers:8 gmod7["0"] gmod6["0"] gmod5["0"] gmod4["0"] gmod3["0"] gmod2["0"] gmod1["0"] gmod0["0"] end space:9 GeorgeOutputLabel["Output"] block:GeorgeBits:8 gbit7["0"] gbit6["0"] gbit5["0"] gbit4["0"] gbit3["0"] gbit2["0"] gbit1["0"] gbit0["0"] end gmod7--"Logical OR"-->gbit7 gmod6--"Logical OR"-->gbit6 gmod5--"Logical OR"-->gbit5 gmod4--"Logical OR"-->gbit4 gmod3--"Logical OR"-->gbit3 gmod2--"Logical OR"-->gbit2 gmod1--"Logical OR"-->gbit1 gmod0--"Logical OR"-->gbit0 space:9 Lizzie:9 LizzieBitsLabel["Bits"] block:LizzieModifierBits:8 lm7["Bit 7"] lm6["Bit 6"] lm5["Bit 5"] lm4["Bit 4"] lm3["Bit 3"] lm2["Bit 2"] lm1["Bit 1"] lm0["Bit 0"] end LizzieModifierLabel["Modifier"] block:LizzieModifiers:8 lmod7["0"] lmod6["1"] lmod5["0"] lmod4["0"] lmod3["0"] lmod2["0"] lmod1["0"] lmod0["0"] end space:9 LizzieOutputLabel["Output"] block:LizzieBits:8 lbit7["0"] lbit6["1"] lbit5["0"] lbit4["0"] lbit3["0"] lbit2["0"] lbit1["0"] lbit0["0"] end lmod7--"Logical OR"-->lbit7 lmod6--"Logical OR"-->lbit6 lmod5--"Logical OR"-->lbit5 lmod4--"Logical OR"-->lbit4 lmod3--"Logical OR"-->lbit3 lmod2--"Logical OR"-->lbit2 lmod1--"Logical OR"-->lbit1 lmod0--"Logical OR"-->lbit0 space:9 Ralph:9 RalphBitsLabel["Bits"] block:RalphModifierBits:8 rm7["Bit 7"] rm6["Bit 6"] rm5["Bit 5"] rm4["Bit 4"] rm3["Bit 3"] rm2["Bit 2"] rm1["Bit 1"] rm0["Bit 0"] end RalphModifierLabel["Modifier"] block:RalphModifiers:8 rmod7["1"] rmod6["0"] rmod5["0"] rmod4["0"] rmod3["0"] rmod2["0"] rmod1["0"] rmod0["0"] end space:9 RalphOutputLabel["Output"] block:RalphBits:8 rbit7["1"] rbit6["0"] rbit5["0"] rbit4["0"] rbit3["0"] rbit2["0"] rbit1["0"] rbit0["0"] end rmod7--"Logical OR"-->rbit7 rmod6--"Logical OR"-->rbit6 rmod5--"Logical OR"-->rbit5 rmod4--"Logical OR"-->rbit4 rmod3--"Logical OR"-->rbit3 rmod2--"Logical OR"-->rbit2 rmod1--"Logical OR"-->rbit1 rmod0--"Logical OR"-->rbit0