--- title: "Runway" id: 57500 type: "computer_media" slug: "runway" url: "http://localhost/computer_media/runway/" markdown_url: "http://localhost/computer_media/runway.md" published_at: "2024-10-05T21:07:06+00:00" modified_at: "2026-03-30T21:17:43+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/255_RW.png" excerpt: "A runway landing game with embedded machine code scrolling, live display-file collision detection, and a two-phase keyboard control system to bring your aircraft safely home." 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/255_RW.png" media_type_tags: "Game, Software" --- This is a two-phase runway landing game where the player guides an aircraft symbol along the ZX81 display file using keyboard controls. The program embeds a machine-code routine in the REM statement at line 1 that performs a fast block-copy of display data, called repeatedly via USR to scroll the runway. The game uses direct PEEK and POKE operations on the display file (base address from system variables at 16396–16397) to detect collisions with obstacles (character codes 3 and 52) and to place the plane sprite (character code 23). Obstacles are injected at calculated positions using modular arithmetic tied to the score variable S, and the game transitions from a horizontal taxiing phase (Z/M keys) to a vertical landing phase (5/8 keys) at score 250. *** ## Program Analysis ### Program Structure The program divides into four logical sections: 1. **Initialisation (lines 1–6):** Machine code stored in the REM, variables set up, display file base address computed. 2. **Phase 1 – Taxiing (lines 10–50):** Player moves horizontally along the runway using Z/M keys; obstacles are periodically placed; score increments each loop. 3. **Phase 2 – Landing (lines 250–320):** Triggered at `S=250`; control switches to 5/8 keys for vertical movement; score continues to increment. 4. **End states (lines 100–140, 400–430):** Collision leads to a “TOO BAD YOU’RE DEAD” prompt; reaching `S=270` leads to a “WELL DONE” prompt. Both offer Y/N replay via `RUN`. ### Machine Code in the REM (Line 1) The REM statement at line 1 contains a hand-assembled Z80 routine. Called from BASIC via `USR (T)` where `T=16514` (two bytes past the REM token at the start of the BASIC area), it acts as a scrolling engine that copies display-file rows to produce the runway movement effect. The trailing `\32` bytes (ASCII spaces) pad the REM to provide a clean source buffer for the copy loop. The routine ends with `C9` (RET), returning control cleanly to BASIC, and its return value is captured in `A` (though unused). Decoded Z80 bytes of interest: - `\2A\0C\40` – `LD HL,(4012H)`: loads display file address - `\11\72\02` – `LD DE,0272H`: sets destination offset (one row down) - `\E5` / `\D1` / `\E1` – stack-based register juggling for block copy - `\10\FD` – `DJNZ` inner loop; `\0D\20\F5` – `DEC C; JR NZ` outer loop - `C9` – `RET` ### Display File Addressing `W` (line 5) is computed as `PEEK 16396 + 256*PEEK 16397`, the standard ZX81 method for reading the system variable D_FILE (address 16396). All subsequent character placement and collision detection is performed by adding offset `V` to `W`. The modular expression `W+V-33*INT(V/33)` at line 22 converts the flat offset into a column position within a 33-byte-wide display row (32 characters + newline), used to inject obstacles at the correct screen column. ### Key BASIC Idioms - **Boolean arithmetic for movement:**`V=V+(INKEY$="M")-(INKEY$="Z")` (line 30) and `V=V+(INKEY$="8")-(INKEY$="5")` (line 260) exploit the ZX81’s 0/1 boolean results to update the plane position in a single expression without IF statements. - **Collision detection by character code:**`PEEK(W+V)=3` tests for the newline character (column boundary / wall) and `=52` tests for the obstacle character, implementing boundary and hazard detection purely through display memory reads. - **Score-driven level progression:** Milestones at `S=100`, `150`, `200` (lines 41–43) subtract multiples of 33 from `V` to jump the plane up one or more display rows, simulating approach descent; `S=250` triggers phase 2. ### Obstacle Generation Line 22 checks whether the units digit of `S` (computed as `S-R*10` where `R=INT(S/10)`) equals 9, 6, or 4, and if so POKEs character 52 at a display position derived from `V` modulo 33 offset by 99 (three rows ahead). This creates a deterministic but varied obstacle pattern timed to the score counter rather than real time. ### Plane and Obstacle Characters | Code | Character | Role | | --- | --- | --- | | `23` | Inverse-video block graphic | Player plane sprite | | `52` | Block graphic / obstacle | Runway hazard | | `3` | Newline (display file) | Edge/wall boundary | | `61` | Explosion/crash glyph | Crash marker (line 100) | ### Notable Anomalies - Lines 440–460 (`CLEAR`, `SAVE "1025%5"`, `RUN`) appear after all reachable game logic and are never executed during normal play; they are likely a loader/auto-save remnant left over from development. - The replay prompt loop (lines 120–140 and 410–430) uses `INPUT Y$`/`INPUT X$`, which on the ZX81 requires the user to press ENTER after the key, making single-keypress replay impossible without modification. - `LET A=USR(T)` is called in both phase loops (lines 20 and 310) but the return value in `A` is never subsequently read, suggesting the call is made purely for its side-effect of running the scroll routine. ## Source Code ``` 1 REM \2A\0C\40\11\72\02\19\E5\06\21\23\10\FD\E5\D1\E1\0E\13\06\21\7E\12\1B\2B\10\FA\0D\20\F5\C9\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32 2 LET S=0 3 LET T=16514 4 LET V=411 5 LET W=PEEK 16396+256*PEEK 16397 6 CLS 7 PRINT AT 20,9;"**%R%U%N%W%A%Y**" 10 PRINT AT 1,10;"\'' \. \''" 20 LET A=USR (T) 21 LET R=INT (S/10) 22 IF S-R*10=9 OR S-R*10=6 OR S-R*10=4 THEN POKE W+V-33*INT (V/33)+99,52 30 LET V=V+(INKEY$="M")-(INKEY$="Z") 35 IF PEEK (W+V)=3 OR PEEK (W+V)=52 THEN GOTO 100 40 POKE W+V,23 41 IF S=100 THEN LET V=V-66 42 IF S=150 THEN LET V=V-33 43 IF S=200 THEN LET V=V-99 44 IF S=250 THEN GOTO 250 45 LET S=S+1 50 GOTO 10 100 POKE W+V,61 110 PRINT AT 21,0;"%T%O%O% %B%A%D% %Y%O%U%R% %D%E%A%D REPLAY Y/N ?" 120 INPUT Y$ 130 IF Y$="Y" THEN RUN 140 GOTO 120 250 PRINT AT 1,10;" " 260 LET V=V+(INKEY$="8")-(INKEY$="5") 270 IF PEEK (W+V)=3 OR PEEK (W+V)=52 THEN GOTO 100 280 POKE W+V,23 290 LET S=S+1 300 IF S=270 THEN GOTO 400 310 LET A=USR (T) 320 GOTO 250 400 PRINT AT 21,0;"%W%E%L%L% %D%O%N%E REPLAY Y/N ?" 410 INPUT X$ 420 IF X$="Y" THEN RUN 430 GOTO 410 440 CLEAR 450 SAVE "1025%5" 460 RUN ```