This program implements a scrolling starfield or “space” screen effect combined with a falling-object mechanic on a ZX81/TS1000. Lines 5–7 fill the screen with inverse-space characters to create a background, and lines 100–299 sweep through every print position in a double loop, drawing a moving marker that advances diagonally each frame. A keypress (detected via INKEY$) triggers a “bullet” or object that travels down and right, drawn with a block-graphic character (▖), until it reaches row 15 where a GOSUB resets it. After the main loop, lines 300–330 scan the bottom row for a specific character (PEEK value 128) to detect a collision or boundary condition, then the program pauses and restarts via RUN.
Program Analysis
Program Structure
The program is divided into five functional blocks:
- Screen initialisation (lines 5–11): Fills the screen with inverse-space characters and draws two decorative border rows using block graphics.
- Main animation loop (lines 100–299): A nested loop over rows
P(1–15) and columnsQ(1–30) sweeps a marker across every cell, while optionally tracking and advancing a secondary “projectile” object. - Bottom-row collision check (lines 300–340): Scans along row 16 (
Pretains value 16 after the loop) looking for a specific display-file byte value. - Reset and restart (lines 341–342): Clears the screen and restarts the whole program.
- Subroutine and save block (lines 400–450): A short GOSUB resets the projectile state; lines 430–450 handle
CLEAR/SAVE/RUNfor program persistence (never reached in normal execution).
Initialisation Detail
Lines 5–7 loop 15 times printing a full row of inverse-space characters (% ), effectively painting the upper screen region solid black. CLEAR at line 8 resets variables without clearing the display. LET B=VAL "0" initialises the projectile-active flag; using VAL "0" rather than a bare literal is a minor memory optimisation that stores a string constant instead of a floating-point number in the token stream. Line 10 prints a row of inverse O characters and line 11 prints 32 repetitions of the ▖ block graphic (\~~ escape) to form a decorative bottom border.
Main Loop and Sweep Marker
The nested loops at lines 100–299 iterate P from 1 to 15 and Q from 1 to 30. At each position, line 120 erases the previous cell with a space, and line 280 redraws it with an inverse-space pair (% ), creating a sweeping highlight effect across the screen.
Projectile / Object Mechanic
Flag variable B tracks whether a projectile is in flight. While B=0, line 130 watches for a keypress via INKEY$; if a key is pressed (and the sweep has not yet reached row 7), the current position is latched into R (row) and S (column) and B is set to 1. Each iteration of the inner loop then advances the projectile diagonally: R increments by 1 and S by 1, with S wrapping to 0 when it exceeds 30 (line 143). The projectile is drawn with the ▖ graphic (\. : escape, line 145). When R reaches 15 the subroutine at line 400 erases it and resets B=0.
There is a subtle anomaly on line 130: the condition P=7 forces a jump to line 150 (which does not exist), causing the program to fall through — a deliberate or accidental use of a non-existent line target as a no-op branch. Additionally, the condition INKEY$="" is logically inverted from what might be expected: the branch fires when no key is pressed, meaning the latch only triggers when the key is actually held down and the condition is false.
Collision / Boundary Detection
After the nested loops, P holds 16 (the post-loop value). Lines 300–330 iterate Q from 0 to 30, reading the ZX81 display file byte at the current cursor position via PEEK (PEEK 16398 + 256*PEEK 16399). System variables at addresses 16398–16399 hold the display-file base address (D-FILE pointer). A value of 128 (inverse space) is the test condition; if found the loop exits early via GOTO 340, otherwise it continues to the end. After the scan, the program pauses 250 frames, clears, and restarts.
Key Variables
| Variable | Role |
|---|---|
B | Projectile active flag (0 = idle, 1 = in flight) |
P | Outer loop row counter (sweep); also row index for bottom scan |
Q | Inner loop column counter (sweep and bottom scan) |
R | Projectile current row |
S | Projectile current column (wraps at 30) |
Z | Counter for initial screen-fill loop (lines 5–7) |
Notable Techniques
VAL "n"used in loop limits and initialisations to save tokenised number storage space.- Direct display-file PEEKing through the D-FILE system variable pointer (16398/16399) for hardware-level character detection without any collision array.
- Block graphic
▖used as a visually distinct projectile sprite within the constraints of character-cell graphics. - The
SAVE "1026%3"at line 440 (inverse3in the filename) sets the auto-run flag so the program restarts automatically when loaded. - Diagonal projectile movement using simultaneous row and column increments within the same loop iteration, with column wrap-around handled by a single conditional.
Bugs and Anomalies
- Line 130 branches to line 150 on
P=7, but line 150 does not exist; on a ZX81 this causes an error or unexpected behaviour rather than a clean skip. - The logic of line 130 means the keypress latch at lines 131–133 is only reached when
INKEY$is not empty (key held) andP≠7, which may be intentional but reads counter-intuitively given the combined OR condition. - Line 145 draws the projectile after line 140 may have already drawn an inverse-space at the same position; depending on execution order within the loop, the projectile could be overwritten by the sweep marker on the same pass.
Content
Source Code
5 FOR Z=VAL "1" TO VAL "15"
6 PRINT "% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
7 NEXT Z
8 CLEAR
9 LET B=VAL "0"
10 PRINT "%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O%O"
11 PRINT "\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~\~~"
100 FOR P=VAL "1" TO VAL "15"
110 FOR Q=1 TO 30
120 PRINT AT P,Q;" "
125 IF B THEN GOTO 140
130 IF INKEY$="" OR P=7 THEN GOTO 150
131 LET R=P
132 LET S=Q
133 LET B=1
140 PRINT AT R,S;"% "
141 LET R=R+1
142 LET S=S+1
143 IF S>30 THEN LET S=0
145 PRINT AT R,S;"\.:"
146 IF R=15 THEN GOSUB 400
280 PRINT AT P,Q;"% "
290 NEXT Q
299 NEXT P
300 FOR Q=0 TO 30
310 PRINT AT P,Q;" "
320 IF 128<>PEEK (PEEK 16398+256*PEEK 16399) THEN GOTO 340
330 NEXT Q
340 PAUSE 250
341 CLS
342 RUN
400 PRINT AT R,S;"% "
410 LET B=0
420 RETURN
430 CLEAR
440 SAVE "1026%3"
450 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
