--- title: "Labyrinth 2" id: 57651 type: "computer_media" slug: "labyrinth-2" url: "http://localhost/computer_media/labyrinth-2/" markdown_url: "http://localhost/computer_media/labyrinth-2.md" published_at: "2024-10-06T01:30:30+00:00" modified_at: "2026-04-03T07:58:09+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/labyrinth-2.png" excerpt: "A block-graphics maze scroller with embedded machine code scrolling—navigate left and right as the corridor closes in and your score climbs." 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: "Gene G. Buza" slug: "gene-g-buza" taxonomy: "indiv" url: "http://localhost/indiv/gene-g-buza/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56728 title: "Synchro-Sette September 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-september-1983/" media_type: "Program" programmers: - name: "Gene G. Buza" slug: "gene-g-buza" taxonomy: "indiv" url: "http://localhost/indiv/gene-g-buza/" mediadate: "September 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/labyrinth-2.png" media_type_tags: "Game" --- # Labyrinth 2 This program implements a scrolling maze/labyrinth game where the player navigates a character horizontally along a corridor filled with obstacles. Line 1 contains a REM statement housing machine code: a LDIR-based routine that shifts screen memory, providing smooth vertical scrolling of the playfield. The player position is tracked via a direct PEEK of screen memory at address A, with INKEY$ “0” and “1” used for left/right movement. Obstacles (block graphic characters) are randomly distributed across each new row generated by the GOSUB 200 subroutine, and the score increments each cycle until a collision is detected. *** ## Program Structure The program is organized into a main loop and three subroutines: 1. **Initialization (lines 10–20):** Calculates the player’s screen address, inputs difficulty `C`, draws the initial screen, and sets the score counter `B` to `-18` (offsetting the pre-fill rows). 2. **Main loop (lines 50–130):** Generates a new row string, prints it, calls the machine code scroller, checks for collision, updates player position, then repeats from line 30 (note: line 30 does not exist — this is a bug; see below). 3. **Row generator subroutine (lines 200–240):** Copies the template string `B$` into `A$`, then randomly places `C` wall characters and `C` space characters within it. 4. **Screen fill subroutine (lines 300–340):** Fills all 22 rows with the border/corridor template `B$` at startup. 5. **Game over handler (lines 1000–1030):** Clears screen, prints final score, pauses, then restarts. ### Machine Code Usage Line 1 is a `REM` statement containing 18 bytes of Z80 machine code. The routine is called via `RAND USR 16514` at line 75 (16514 = address of the first byte after the `REM` token). Disassembled: | Bytes | Mnemonic | Notes | | --- | --- | --- | | `01 D6 02` | LD BC, $02D6 | Byte count = 726 (22 rows × 33 bytes) | | `2A 0C 40` | LD HL, ($400C) | Load display file start address | | `09` | ADD HL, BC | HL → one row down from top | | `54` | LD D, H | | | `5D` | LD E, L | DE = source (one row down) | | `01 B5 02` | LD BC, $02B5 | Byte count = 693 (21 rows × 33) | | `2A 0C 40` | LD HL, ($400C) | HL = destination (top of screen) | | `09` | ADD HL, BC | (unused intermediate — likely HL points to end of copy region) | | `ED B8` | LDDR | Block copy downward (scroll up) | | `C9` | RET | | This LDDR-based scroll moves the entire display buffer up by one text row each game cycle, giving smooth vertical scrolling without redrawing the whole screen from BASIC. ### Key BASIC Idioms - `LET A=645+PEEK 16396+256*PEEK 16397` — computes a screen address relative to the display file base (system variable at 16396/16397), placing the player 645 bytes (about 19 rows × 33 + offset) into the display. - `LET A=A+(INKEY$="0")-(INKEY$="1")` — classic Sinclair idiom using Boolean arithmetic (+1 or -1) to update position in a single expression. - `LET A$(1+F)="%."` and `LET A$(1+F)="% "` — two-character string slice assignment used to place block graphic pairs (wall/space) into the row template. - Score is initialized to `-18` so that it reads 0 only after the 18 pre-scrolled setup rows have passed. ### Display Template The string `B$` (line 16) is 33 characters wide. It consists of border characters (`\:` = ▌ and `\ :` = ▐ style blocks) at each end, with 28 interior positions filled with `\@@` (inverse spaces / solid blocks), forming a solid wall corridor. The row generator (lines 200–240) punches `C` gaps and `C` walls randomly into this template to create the navigable maze rows. ### Collision Detection Collision is detected at line 80 by `PEEK A`: the player’s current screen cell is read directly from display memory. Character code 136 represents an inverse space (solid block — a wall). If the cell is not 136, the game-over subroutine at line 1000 is called. Line 90 then writes 151 (the player character) to that address, and lines 100–104 write wall characters to the three adjacent cells (offsets +32, +33, +34 — i.e., the row below), effectively drawing the player sprite and the approaching wall simultaneously. ### Bugs and Anomalies - **LDDR direction:** LDDR copies from high to low address. The setup places HL at (base + 693) and DE at (base + 726), but both source and destination adjustments in the machine code warrant careful scrutiny — the exact scroll direction and pixel-row vs. character-row granularity depends on whether the display file is linear (as on the TS2068 in certain modes). - **Score display condition:** Line 72 only prints the score when `B>0`, consistent with the `-18` initialization, so the score appears clean from zero. ## Source Code ``` 1 REM 1D622AC409545D1B522AC409EDB8C9 10 LET A=645+PEEK 16396+256*PEEK 16397 15 INPUT C 16 LET B$="% : @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ :% " 17 GOSUB 300 20 LET B=-18 50 GOSUB 200 70 PRINT AT 0,0;A$ 72 IF B>0 THEN PRINT AT 18,30;B;AT 19,30;" :% " 75 RAND USR 16514 80 IF PEEK A<>136 THEN GOSUB 1000 90 POKE A,151 100 POKE A+33,136 102 POKE A+32,136 104 POKE A+34,136 110 LET B=B+1 120 LET A=A+(INKEY$="0")-(INKEY$="1") 130 GOTO 30 200 LET A$=B$ 205 FOR N=1 TO C 206 LET F=INT (RND*28)+2 210 LET A$(1+F)="%." 215 LET F=INT (RND*28)+2 220 LET A$(1+F)="% " 230 NEXT N 240 RETURN 300 FOR N=0 TO 21 310 PRINT AT N,0;B$ 330 NEXT N 340 RETURN 1000 CLS 1010 PRINT AT 10,4;"GAME OVER - FINAL SCORE = ";B 1020 PAUSE 1000 1030 RUN 9998 SAVE "LABYRINTH-%2" 9999 RUN ```