--- title: "Space Background" id: 57149 type: "computer_media" slug: "space-background" url: "http://localhost/computer_media/space-background/" markdown_url: "http://localhost/computer_media/space-background.md" published_at: "2024-10-04T00:53:05+00:00" modified_at: "2026-03-30T21:19:28+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/98_Spc.png" excerpt: "A four-directional scrolling starfield demo uses hand-assembled Z80 machine code hidden in a REM statement to blast the screen left, right, up, or down at full speed." 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: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" media_contents: - id: 56733 title: "Timex Sinclair Public Domain Library Tape 1002" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1002/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/98_Spc.png" media_type_tags: "Demo" --- # Space Background This program implements a scrolling starfield game on the ZX81/TS1000, using machine code routines embedded in the REM statement at line 1 to perform fast screen scrolling in four directions. The machine code, called via USR at addresses 16514–16588, manipulates the display file directly using Z80 block-move instructions (LDIR/LDDR) and fills vacated rows or columns with spaces (0x80 in ZX81 display format). The BASIC layer handles keyboard input (keys 0, Q, Z, 8 for right, up, down, left scrolling), prints a randomised starfield background, and maintains a status bar at row 22 showing FUEL, COORDS, and ENEMY values. Screen attributes are temporarily modified via POKEs to 16418 (the ZX81’s RAMTOP-adjacent system variable controlling the display) to suppress scroll during the status bar setup. *** ## Program Analysis ### Program Structure The program is organised into four logical sections: 1. **Line 1 (REM):** Houses the Z80 machine code payload — four separate routines for scrolling the display in each cardinal direction. 2. **Lines 5–60 (Main loop):** Calls the initialisation subroutine, then enters a polling loop reading `INKEY$` and dispatching to the appropriate machine code routine via `USR`, then calls the star-spawning subroutine. 3. **Lines 100–107 (Initialisation):** Fills the screen with spaces and random inverse-dot characters to create a starfield, then draws a status bar at row 22. 4. **Lines 200–204 (Star spawning):** Adds a new star character (`%.` — inverse dot) at the leading edge of the scroll direction after each move. ### Machine Code Routines The REM data at line 1 contains four Z80 routines. Their entry points (relative to the start of the display file pointer at address 16396) are called from BASIC via `USR`: | Key | Direction | USR Address | Z80 Technique | | --- | --- | --- | --- | | 0 | Right (scroll left) | 16588 | Uses `LD HL,(16396)`, adjusts with display offset, LDIR block copy, fills right column | | Q | Up | 16538 | LDDR-based block move, fills bottom row with 0x80 (ZX81 space) | | Z | Down | 16514 | Loop over rows filling with 0x80, reverse direction copy | | 8 | Left (scroll right) | 16567 | Similar to right-scroll but using `LDIR` with adjusted pointer | Each routine terminates with `C9` (RET), returning control cleanly to BASIC. The byte `0x80` is the ZX81’s display-file encoding for a space character, used to blank the newly exposed row or column after scrolling. ### Display File Manipulation The routines reference address `16396` (0x400C), which is the ZX81 system variable `D_FILE` — the pointer to the display file. By reading this dynamically, the code remains position-independent with respect to where BASIC has placed the display file in RAM. The constant offset `0x0043` (67 decimal) appears in the scroll routines, corresponding to the 33-byte width of a ZX81 display row (32 characters + 1 newline byte), used to advance or retract the HL/DE register pairs between rows. The scroll loop uses `DJNZ` with B=0x16 (22 decimal) to iterate over the 22 printable rows of the ZX81 display. ### Key BASIC Idioms - `LET Z=USR 16588` — the return value of the machine code is discarded into a dummy variable; this is a standard ZX81 idiom for calling `USR` without needing the result. - `PRINT AT A,INT (RND*32);"%."` — prints an inverse dot at a random column to seed the starfield. - `IF D$="x" THEN ...` chains (lines 25–40 and 200–203) replace a `SELECT/CASE` structure absent from ZX81 BASIC. - Line 20 uses `LET D$=INKEY$` rather than a `PAUSE`/`INKEY$` idiom, so the loop runs continuously even with no keypress (D$ becomes empty string, no USR call fires, but a star is still spawned at a random edge via subroutine 200’s guards). ### Status Bar and SCROLL Suppression Lines 104 and 106 POKE address 16418 to values 0 and 2 respectively. Address 16418 is `DF_SZ`, the display-file size variable that controls how many lines are reserved at the bottom for the lower screen. Setting it to 0 before printing the status bar prevents the display from auto-scrolling, and restoring it to 2 afterwards reinstates normal behaviour. The status bar (line 105) displays inverse-video text reading `FUEL=9872`, `COORDS:34,19`, and `ENEMY=5` — these appear to be static placeholder values rather than live game state, suggesting the program is a demonstration or early prototype. ### Trailing Bytes in REM The final bytes of the REM data (`1D 1E 1F 20 21 22 23 24 25 1C`) fall after the last `C9` (RET) instruction and are never executed. They may represent leftover data, a lookup table, or simply padding artefacts from the original assembly process. ### Anomalies and Notes - Lines 300–301 (`SAVE` / `RUN`) are development utilities and are never reached during normal execution. - The starfield spawning in subroutine 200 only adds a star when a key is held; with no keypress, `D$=""` matches none of the conditions, so no star is drawn that cycle. This means the star density slowly decreases during inactivity. - The `GOSUB 100` at line 5 is called only once, so the starfield is initialised once and then maintained incrementally by subroutine 200. ## Source Code ``` 1 REM \2A\0C\40\23\E5\11\21\00\19\D1\01\B5\02\ED\B0\EB\06\20\36\80\23\10\FB\C9\2A\10\40\11\43\00\ED\52\E5\11\21\00\ED\52\D1\01\B5\02\ED\B8\EB\06\20\2B\36\80\10\FB\C9\2A\0C\40\06\16\C5\06\20\3E\80\23\4F\7E\71\10\FA\23\C1\10\F1\C9\2A\10\40\11\43\00\ED\52\06\16\C5\06\20\3E\80\2B\4F\7E\71\10\FA\2B\C1\10\F1\C9\1D\1E\1F\20\21\22\23\24\25\1C 5 GOSUB 100 20 LET D$=INKEY$ 25 IF D$="0" THEN LET Z=USR 16588 30 IF D$="Q" THEN LET Z=USR 16538 35 IF D$="Z" THEN LET Z=USR 16514 40 IF D$="8" THEN LET Z=USR 16567 50 GOSUB 200 60 GOTO 20 100 FOR A=0 TO 21 101 PRINT AT A,0;"% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % " 102 PRINT AT A,INT (RND*32);"%." 103 NEXT A 104 POKE 16418,0 105 PRINT AT 22,0;"\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..% %F%U%E%L%=9872% %C%O%O%R%D%S%:34,19% %E%N%E%M%Y%=5% " 106 POKE 16418,2 107 RETURN 200 IF D$="0" THEN PRINT AT INT (RND*22),31;"%." 201 IF D$="Q" THEN PRINT AT 0,INT (RND*32);"%." 202 IF D$="Z" THEN PRINT AT 21,INT (RND*32);"%." 203 IF D$="8" THEN PRINT AT INT (RND*22),0;"%." 204 RETURN 300 SAVE "1009%8" 301 RUN ```