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,$01The 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


