--- title: "Invader" id: 57508 type: "computer_media" slug: "invader" url: "http://localhost/computer_media/invader/" markdown_url: "http://localhost/computer_media/invader.md" published_at: "2024-10-05T21:14:29+00:00" modified_at: "2026-03-30T21:17:38+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/263_Inv.png" excerpt: "This game uses a scrolling scene where the player attempts to shoot objects in space." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56737 title: "Timex Sinclair Public Domain Library Tape 1006" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1006/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/263_Inv.png" media_type_tags: "Game, Software" --- 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: 1. **Screen initialisation (lines 5–11):** Fills the screen with inverse-space characters and draws two decorative border rows using block graphics. 2. **Main animation loop (lines 100–299):** A nested loop over rows `P` (1–15) and columns `Q` (1–30) sweeps a marker across every cell, while optionally tracking and advancing a secondary “projectile” object. 3. **Bottom-row collision check (lines 300–340):** Scans along row 16 (`P` retains value 16 after the loop) looking for a specific display-file byte value. 4. **Reset and restart (lines 341–342):** Clears the screen and restarts the whole program. 5. **Subroutine and save block (lines 400–450):** A short GOSUB resets the projectile state; lines 430–450 handle `CLEAR`/`SAVE`/`RUN` for 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 (inverse `3` in 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) and `P≠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. ## 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 ```