--- title: "Surge" id: 57417 type: "computer_media" slug: "surge" url: "http://localhost/computer_media/surge/" markdown_url: "http://localhost/computer_media/surge.md" published_at: "2024-10-05T12:55:15+00:00" modified_at: "2026-03-30T21:18:08+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/218_Surg.png" excerpt: "A tiny machine-code collision detector hidden in a REM statement powers this scrolling one-button arcade game — how high can you surge?" 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/" indiv: - name: "Tim Rogers" slug: "tim-rogers" taxonomy: "indiv" url: "http://localhost/indiv/tim-rogers/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56736 title: "Timex Sinclair Public Domain Library Tape 1005" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1005/" media_type: "Program" programmers: - name: "Tim Rogers" slug: "tim-rogers" taxonomy: "indiv" url: "http://localhost/indiv/tim-rogers/" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/218_Surg.png" media_type_tags: "Game" --- # Surge Surge is a ZX81/TS1000 one-screen arcade game in which the player steers a character (“V”) upward through a scrolling field of obstacles while a machine-code routine checks for collisions. The program uses a 7-byte machine-code stub embedded in REM line 1 (loaded via POKE 16517 at line 10), which is called with USR 16514 to perform a character-code check at the player’s screen position. Scrolling is achieved with the SCROLL command, and the player’s horizontal movement is driven by INKEY$ with boundary clamping. The score accumulates as the number of rows the player climbs (line 120: S=S+U decrements as U approaches zero), and a high-score variable H is preserved across restarts. *** ## Program Analysis ### Program Structure The program divides into three logical phases: 1. **Initialisation (lines 1–25):** Machine code is embedded in `REM` line 1, the POKE at line 10 patches the program area, and game variables are reset. 2. **Main game loop (lines 30–130):** Moves the player, scrolls the screen, checks for collision via `USR`, prints obstacles, and accumulates score. 3. **Game-over / restart (lines 200–230):** Updates high score, displays result, pauses, and loops back to line 15 for a new game without re-running the machine-code setup. ### Machine Code in REM Line 1 Line 1 holds the bytes `\2A\0E\40\4E\06\00\C9` (7 bytes). In Z80 mnemonics: | Offset | Hex | Mnemonic | Notes | | --- | --- | --- | --- | | 0 | 2A 0E 40 | LD HL,(400Eh) | Load HL with the word at address 400Eh (CHARS sysvar area) | | 3 | 4E | LD C,(HL) | Read byte at that address into C | | 4 | 06 00 | LD B,0 | Set B=0, so BC = character code read | | 6 | C9 | RET | Return BC as the USR result | `POKE 16517,78` (line 10) writes `4Eh` into the fifth byte of the REM payload, ensuring the `LD C,(HL)` opcode is correctly placed. The routine is called at `USR 16514` (the start of the REM data area). It reads a byte from the display file address held at `400Eh` and returns it as a numeric result, allowing the BASIC at line 80 to compare it with `CODE "% "` (the inverse-space character) to detect a collision with an obstacle. ### Variable Usage | Variable | Purpose | | --- | --- | | `H` | High score (initialised to 1 at line 5; persists across restarts) | | `S` | Current game score | | `U` | Player’s row (counts down toward 0 = top of screen) | | `T` | Right boundary / screen width limit (20) | | `P` | Player’s column position | ### Key BASIC Idioms - **Boolean arithmetic for movement:** Line 50 uses `P=P-H/H*(P>H/H)` — since `H` is always ≥1, `H/H` equals 1, so this reads `P=P-1*(P>1)`, clamping `P` to a minimum of 1 without an `IF` statement. - **INKEY$ horizontal movement:** Line 60 adds 2 to `P` whenever any key is held and `PH/H) 60 LET P=P+(INKEY$<>"")*2*(P