--- title: "White Hole" id: 57596 type: "computer_media" slug: "white-hole" url: "http://localhost/computer_media/white-hole/" markdown_url: "http://localhost/computer_media/white-hole.md" published_at: "2024-10-05T22:55:24+00:00" modified_at: "2026-03-30T21:17:04+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/wht-hol-1.png" excerpt: "Guide your craft through drifting hazards toward a white hole." 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/" media_contents: - id: 56723 title: "Synchro-Sette February 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-february-1983/" media_type: "Program" mediadate: "February 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/wht-hol-1.png" media_type_tags: "Software" --- This program simulates a space navigation game in which the player steers a craft through a field of randomly drifting obstacles (represented by cursor-control characters) toward a white hole destination. The REM statement at line 1 contains decorative block-graphic characters, and line 10 calls machine code at address 16514 via USR to perform fast initialisation before switching to SLOW mode. Player input on keys “0” and “1” adjusts the horizontal position variable X using a compact Boolean arithmetic idiom in line 70. Collision detection at line 120 uses PEEK on the display file address (derived from system variables at 16398–16399) to check whether the character under the craft is a cursor-control code 8, triggering an animated explosion sequence of block-graphic characters at lines 140–150. *** ## Program Analysis ### Program Structure The program is a single-file game loop with five logical phases: 1. **Initialisation** (lines 2–20): clears state, calls machine code, sets SLOW mode. 2. **Game setup** (lines 30–45): derives screen coordinates from `PI` and starts the main loop of 100 iterations. 3. **Main game loop** (lines 50–126): scrolls a decorated border line, moves the player, scatters random obstacle characters, and checks for collision. 4. **Collision / explosion** (lines 130–190): animates a block-graphic explosion, updates the load score, and resumes the outer loop. 5. **End-of-run scoring** (lines 500–540): records the best score, displays results, pauses, and restarts. ### Machine Code Usage Line 10 executes `RAND USR 16514`. Address 16514 is in the ZX81/TS1000 ROM; `RAND USR` is the standard way to call machine code from BASIC. The `FAST` / `SLOW` bracketing (lines 5 and 15) ensures the MC routine runs without display interference and then returns to the normal display-enabled mode. ### Coordinate Initialisation via PI Rather than using plain integer literals, the starting coordinates are derived from the built-in constant `PI`. `X = 2*PI ≈ 6.28` and `Y = PI + X ≈ 9.42`. Because `PRINT AT` truncates to integer, these resolve to column 6 and row 9 — a compact way to encode starting positions that also saves a few bytes compared with storing integer literals directly. ### Player Input Idiom Line 70 uses the classic Boolean arithmetic trick: `LET X=X+(INKEY$="0" AND X<30)-(INKEY$="1" AND X>PI)` Each Boolean sub-expression evaluates to 1 (true) or 0 (false), so the single `LET` both moves right and moves left with built-in boundary clamping, avoiding any `IF` statements. ### Obstacle Generation Lines 100 prints four pairs of `AT` / `CHR$ 8` sequences in random screen positions. CHR$ 8 is the DELETE/backspace control code, which on the ZX81 display appears as a cursor-left character but visually leaves a distinctive mark used here as a hazard sprite. Line 90 uses `IF RND>.7 THEN GOTO 50` to skip obstacle placement 70 % of the time, keeping the field sparse. ### PEEK-Based Collision Detection Line 120 reads the display file address from system variables: `IF PEEK (PEEK 16398+256*PEEK 16399)=8 THEN GOTO 130` System variables at 16398 (low byte) and 16399 (high byte) hold the current `PRINT AT` cursor address within the display file. Peeking that address checks the character code directly in video RAM. A value of 8 (the obstacle character) triggers the collision branch. Line 110 positions the cursor at the player location before the PEEK so the correct cell is sampled. ### Block-Graphic Explosion Animation Lines 130–160 loop 10 times, alternating two 3×3 block-graphic patterns around the player position to create a flicker effect. The characters used are ZX81 block graphics forming a dense filled square and then an inverse/hollow square, simulating an explosion burst. ### Scoring and Persistence Variable `U` counts down by 1 each iteration (line 80) and gains 100 on a collision hit (line 166), making hits beneficial to score. The high score is preserved in `A` across restarts because the program `GOTO 5` (line 540) rather than `RUN`, so `A` retains its value. Line 2 (`LET A=0`) is only reached on a true `RUN` or first load. ### Notable Techniques Summary - `RAND USR` machine-code call with `FAST`/`SLOW` bracketing. - Boolean arithmetic for clamped single-line player movement. - Display-file PEEK for pixel-level (character-cell) collision detection. - PI-derived coordinate initialisation to save tokenised bytes. - High-score persistence via `GOTO` restart rather than `RUN`. ### Potential Anomalies The `FOR I` loop spans lines 45–190, but the `NEXT I` at line 125 is inside the body and the collision branch at line 130 jumps back to `NEXT I` at line 190, which continues the outer loop correctly. However, if the 100-iteration loop completes without reaching `GOTO 500` via line 126 in every iteration, control falls through to line 126 only after line 125 exhausts the loop — this is consistent with the intended flow. The variable `U` is reset to 1 only at line 20, so across `GOTO 5` restarts it retains whatever value it had; this may be intentional to carry a running load total, but could also cause unexpected initial conditions. ## Source Code ``` 1 REM Y%.\.'\. :%KNOT $TAB \@@RND\: TAB \'.RNDTAN 2 LET A=0 5 FAST 10 RAND USR 16514 15 SLOW 20 LET U=1 30 LET X=2*PI 40 LET Y=PI+X 45 FOR I=1 TO 100 50 PRINT AT Y,X;"." 60 SCROLL 65 PRINT "%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%.%." 70 LET X=X+(INKEY$="0" AND X<30)-(INKEY$="1" AND X>PI) 80 LET U=U-1 90 IF RND>.7 THEN GOTO 50 100 PRINT AT RND*15+5,RND*30;CHR$ 8;AT RND*15+5,RND*30;CHR$ 8;AT RND*15+5,RND*30;CHR$ 8;AT RND*15+5,RND*30;CHR$ 8 110 PRINT AT Y,X; 120 IF PEEK (PEEK 16398+256*PEEK 16399)=8 THEN GOTO 130 125 NEXT I 126 GOTO 500 130 FOR N=1 TO 10 140 PRINT AT Y-1,X-1;"\:'\''\':";AT Y,X-1;"\: .\ :";AT Y+1,X-1;"\:.\..\.:" 150 PRINT AT Y-1,X-1;"\ .\..\. ";AT Y,X-1;"\ :%.\: ";AT Y+1,X-1;"\ '\''\' " 160 NEXT N 166 LET U=U+100 170 PRINT AT 6,3;" PRESENT LOAD = ";U;" TONS ";AT 4,2;" ";100-I;" MILLION MILES LEFT TO GO " 180 PAUSE 200 190 NEXT I 500 IF U>A THEN LET A=U 510 PRINT AT 11,4;" FINAL = ";U;" BEST = ";A;" " 520 PAUSE 400 530 CLS 540 GOTO 5 998 SAVE "WHITE-HOL%E" 999 RUN ```