.png)
Everything on screen so far has been the player. A game needs things that act on their own: a bullet that keeps going after the key is released, an enemy that patrols, a bonus that drifts past. The machinery for all of them is the same, and a bullet is its simplest case - it is born, it moves by itself, it dies at the edge, and the player's fingers have nothing to do with any of it after the first moment.
The pattern this section builds - variables, an update routine, a publish, and an active flag - is what "an object" means in a game. Learn it once with one bullet and you have learned enemies, pickups and explosions too.
The bullet gets exactly what the rocket got in S21: state in RAM, and nothing else is the truth.
bullX: DEFB 0
bullY: DEFB 224
bullA: DEFB 0
Two coordinates and a flag. bullA is the object's pulse: 1 means it is in
flight, 0 means there is no bullet right now - the sprite still exists, the
variables still exist, but the object does not. Games rarely create or
destroy anything; they flip flags on things that were always there.
While there is no bullet, it parks at Y = 224. That number is doing quiet work: an 8x8 sprite is completely off screen for any Y from 192 to 247 - below the last visible line, short of the wrap that would bring it back in at the top - except 208, which is not a position at all but the list-terminator from S19, and would switch off every sprite after it. Pick parking spots away from the trap, not next to it.
fire: LD A,(bullA)
OR A
RET NZ ; one is already flying
LD A,1
LD (bullA),A
LD A,(rockY)
LD (bullY),A
LD A,(rockX)
LD (bullX),A
RET
Raise the flag, copy the shooter's position, done. The guard at the top is the one-bullet rule: while a bullet is in flight, space does nothing. Reload happens by flying off the screen.
input gains one line to reach it - CP 32 - because the space bar arrives
from the key call as code 32, sitting in the character set right where S3's
appendix put it, before the digits and letters.
bullet: LD A,(bullA)
OR A
JR Z,bpub ; not flying - publish it parked
LD A,(bullY)
SUB 3 ; bullets outrun rockets
CP 8
JR C,bstop ; reached the top - done
LD (bullY),A
JR bpub
bstop: LD A,0
LD (bullA),A
LD A,224 ; park off the bottom, clear of 208
LD (bullY),A
bpub: ... ; publish sprite 2 from the variables
Read the first two lines again, because they are the section. An inactive object's update does nothing but publish. Skip that gate and the parked bullet keeps moving - invisibly at first, round and round through the coordinate space, until its path crosses the screen and a phantom shot sails up it with nobody firing. The flag is not bookkeeping; it is what stops the dead from walking.
Two more choices worth their sentence:
SUB 3 / CP 8 / JR C decides
on the candidate position, so a wrapped value is never published - the
same wrap-avoidance the clamps did in S21, pointed the other way. ORG 256
; --- four patterns: body, flame A, flame B, bullet
LD A,0
OUT (9),A
LD A,88
OUT (9),A
LD HL,rocket
LD B,32
pat: LD A,(HL)
OUT (8),A
INC HL
DJNZ pat
; --- close the sprite list after our three
LD A,12
OUT (9),A
LD A,123
OUT (9),A
LD A,208
OUT (8),A
main: CALL place
CALL pause
CALL input
CALL animate
CALL bullet
JR main
; --- draw the rocket from its variables
place: LD A,0
OUT (9),A
LD A,123
OUT (9),A
LD A,(rockY)
OUT (8),A
LD A,(rockX)
OUT (8),A
LD A,0
OUT (8),A
LD A,15
OUT (8),A
LD A,(rockY)
ADD A,8
OUT (8),A
LD A,(rockX)
OUT (8),A
LD A,(flameP)
OUT (8),A
LD A,11
OUT (8),A
RET
; --- the bullet: move it only while it is flying, publish it always
bullet: LD A,(bullA)
OR A
JR Z,bpub ; not flying - publish it parked
LD A,(bullY)
SUB 3 ; bullets outrun rockets
CP 8
JR C,bstop ; reached the top - done
LD (bullY),A
JR bpub
bstop: LD A,0
LD (bullA),A
LD A,224 ; park off the bottom, clear of 208
LD (bullY),A
bpub: LD A,8 ; sprite 2's slot in the attribute table
OUT (9),A
LD A,123
OUT (9),A
LD A,(bullY)
OUT (8),A
LD A,(bullX)
OUT (8),A
LD A,3 ; the streak pattern
OUT (8),A
LD A,15
OUT (8),A
RET
; --- fire: one bullet at a time
fire: LD A,(bullA)
OR A
RET NZ ; one is already flying
LD A,1
LD (bullA),A
LD A,(rockY)
LD (bullY),A
LD A,(rockX)
LD (bullX),A
RET
; --- the animation clock, as S22
animate:
LD A,(flick)
DEC A
LD (flick),A
RET NZ
LD A,8
LD (flick),A
LD A,(flameP)
XOR 3
LD (flameP),A
RET
; --- one key per poll - every path leaves by RET
input: RST 8
DEFB 181 ; ZKSCAN
OR A
RET Z
CP 32 ; space
JR Z,fire
CP 87 ; W
JR Z,up
CP 83 ; S
JR Z,down
CP 65 ; A
JR Z,left
CP 68 ; D
JR Z,right
RET
up: LD A,(rockY)
OR A
RET Z
DEC A
LD (rockY),A
RET
down: LD A,(rockY)
CP 175
RET Z
INC A
LD (rockY),A
RET
left: LD A,(rockX)
OR A
RET Z
DEC A
LD (rockX),A
RET
right: LD A,(rockX)
CP 248
RET Z
INC A
LD (rockX),A
RET
pause: LD BC,3000
dloop: DEC BC
LD A,B
OR C
JR NZ,dloop
RET
rockX: DEFB 124
rockY: DEFB 88
flameP: DEFB 1
flick: DEFB 8
bullX: DEFB 0
bullY: DEFB 224
bullA: DEFB 0
rocket: ; body
DEFB 00011000b
DEFB 00111100b
DEFB 00111100b
DEFB 01111110b
DEFB 01111110b
DEFB 11111111b
DEFB 11011011b
DEFB 10011001b
; flame frame A
DEFB 01011010b
DEFB 01111110b
DEFB 01111110b
DEFB 00111100b
DEFB 00111100b
DEFB 00011000b
DEFB 00010000b
DEFB 00001000b
; flame frame B
DEFB 00111100b
DEFB 01111110b
DEFB 00111100b
DEFB 00111100b
DEFB 00011000b
DEFB 00000000b
DEFB 00000000b
DEFB 00000000b
; the bullet - a two-pixel streak
DEFB 00011000b
DEFB 00011000b
DEFB 00011000b
DEFB 00000000b
DEFB 00000000b
DEFB 00000000b
DEFB 00000000b
DEFB 00000000b
W, A, S, D to fly, space to fire.
What you should see
The rocket, alive as in S22 - and on space, a white streak leaves it and climbs, fast, gone in under half a second off the top. Space during a flight does nothing; space after it fires again. Fly around while a bullet is up: the two move independently, because the loop updates each object from its own variables and neither knows the other exists.

OR A / JR Z,bpub gate at the top of bullet and watch the
screen for a while - moving nothing, pressing nothing. Explain the
interval between hauntings.SUB 3 to SUB 1, fly upward, and fire while moving. What is
wrong with a bullet that matches your speed?RET NZ to nothing - delete those two lines - and
hold space. What is the flame-thrower you accidentally built, and why
does it never show more than one bullet?fire, copy rockY but not rockX. Fire from a few places. What is
the bullet remembering?| Symptom | Cause |
|---|---|
| Phantom bullets cross the screen with nobody firing | The update is not gated on the active flag: the parked object is still being moved, and its path crosses the visible screen. Inactive means update does nothing. |
| The bullet fires once and never again | It never deactivates - the edge check is wrong or missing, the flag stays up, and the one-bullet guard blocks every later press for ever. |
| A bullet hangs frozen on screen | Parked somewhere visible. Park in 192-247 - and not 208. |
| Every sprite after the bullet vanished | Parked AT 208. That is the list terminator, not a position. |
| Fires, then the machine dies minutes later | The leak again. Every CALLed routine, every path, RET - S21. |
S24. When things touch. The bullet sails through everything, because nothing in the machine knows sprites are things. Touching is a meaning games add - four comparisons' worth - and it has to be exact to the pixel.