--- title: "Racer 2" id: 57422 type: "computer_media" slug: "racer-2" url: "http://localhost/computer_media/racer-2/" markdown_url: "http://localhost/computer_media/racer-2.md" published_at: "2024-10-05T13:13:23+00:00" modified_at: "2026-03-30T21:18:04+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/223_Racr.png" excerpt: "Guide your car along a randomly weaving road using two keys — one wrong move and a clever display-file PEEK ends the race instantly." 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: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" - 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" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/223_Racr.png" media_type_tags: "Arcade, Game" --- # Racer 2 Racer 2 is a ZX81/TS1000 scrolling racing game in which the player steers a car left and right along a road while the road itself weaves randomly up the screen. The road is drawn each frame as a row of block-graphic characters at a horizontally drifting position, and the player’s car is printed one row above as an inverse-H character. Collision detection is performed via a PEEK of the display file: the program reads the two-byte D_FILE pointer from system variables at addresses 16398–16399 and checks whether the character under the car’s position equals 128 (a block graphic), halting the game on impact. Road drift is controlled by a pair of conditional nudges using RND, keeping variable D (column) within roughly 7–17 to constrain the road to a playable corridor. *** ## Program Analysis ### Program Structure The program is a short main loop with initialisation at lines 20–50, a rendering and input phase at lines 60–130, a collision check at line 140, and a loop-back at line 150. Lines 160–170 are utility lines (save and auto-run) that are never reached during normal play. 1. **Initialisation (lines 20–50):** Sets `A=10` (car row), `B=A` (car column, starts at 10), `C=A+A=20` (road row, near the bottom), `D=9` (road column). 2. **Road drawing (line 60):** Prints a road segment of block-graphic characters at row `C` (20), column `D`. 3. **Car drawing (line 70):** Prints a block-graphic at the car’s current position row `A`, column `B`. 4. **Scroll and input (lines 80–100):** Scrolls the screen up one line, then reads `INKEY$` twice to move the car right (M) or left (Z). 5. **Car sprite (line 110):** Redraws the car as an inverse-H character (`%H`) at the updated position. 6. **Road steering (lines 115–130):** Nudges `D` upward if below 17, downward if above 7, using `2*RND` fractional increments. Line 120 positions the print cursor without printing anything, possibly as a cursor-placement side-effect. 7. **Collision detection (line 140):** Peeks the display file to detect a crash. ### Display and Graphics The road is rendered using ZX81 block-graphic characters (`\##` escapes, solid block character 128) surrounded by spaces, giving a short road segment roughly eight characters wide. The player’s car is a single inverse-H character (`%H`, i.e. character code for H in inverse video), visually distinct from the road graphics. The `SCROLL` command at line 80 moves all rows up, creating the illusion of forward movement. ### Collision Detection Technique Line 140 implements a display-file collision check, a classic ZX81 technique. `PEEK 16398` and `PEEK 16399` read the low and high bytes of the D_FILE system variable (the pointer to the display file in RAM). The combined 16-bit address `PEEK 16398 + PEEK 16399 * 256` gives the start of the display file, and `PEEK` of that address checks the first character in the file. A value of `128` (the solid block graphic) indicates the car has hit the road and `STOP` ends the game. There is a subtle anomaly here: the PEEK always reads the very first byte of the display file (the top-left cell), not the byte at the car’s current position. This means the collision check only triggers when a road block graphic has scrolled all the way to the top-left corner of the screen, rather than detecting a true overlap between car and road. As a result the game ends somewhat unpredictably rather than precisely on contact. ### Road Drift Algorithm The road column `D` is kept within a soft corridor of 7–17 by two opposing nudges per frame: - Line 115: if `D < 17`, add `2*RND` (a random value in [0,2)). - Line 130: if `D > 7`, subtract `2*RND`. Because both conditions can be true simultaneously (when 7 < D < 17), both nudges apply each frame, producing a random walk bounded loosely between the two thresholds. The road therefore drifts smoothly but erratically across the screen. ### Key BASIC Idioms | Line | Idiom | Purpose | | --- | --- | --- | | 80 | `SCROLL` | Shifts display up one row to simulate movement | | 90–100 | Back-to-back `INKEY$` tests | Single-frame key polling without PAUSE | | 110 | `%H` inverse character | Visually distinct player sprite using inverse video | | 140 | `PEEK(PEEK lo + PEEK hi * 256)` | Indirect address read via system variable pointer | | 115, 130 | `2*RND` fractional drift | Smooth stochastic road steering | ### Bugs and Anomalies - The collision check at line 140 reads the first byte of the display file rather than the byte at the car’s screen position. A correct check would need to offset the pointer by the car’s row and column within the display file (accounting for the newline bytes that separate rows on the ZX81). - Line 120 (`PRINT AT 11,B;`) positions the print cursor at row 11 without outputting any character. This may be an artefact of an earlier version of the program or intended to pre-position the cursor, but it has no visible effect in the current flow. - The car column `B` is never range-checked, so pressing M or Z repeatedly can drive the car off the edges of the 32-column display, causing a potentially confusing wrap or error. ## Source Code ``` 10 REM "RACER 2" 20 LET A=10 30 LET B=A 40 LET C=A+A 50 LET D=9 60 PRINT AT C,D;"% \##\##\##\##% " 70 PRINT AT A,B;"\##" 80 SCROLL 90 IF INKEY$="M" THEN LET B=B+1 100 IF INKEY$="Z" THEN LET B=B-1 110 PRINT AT A,B;"%H" 115 IF D<17 THEN LET D=D+2*RND 120 PRINT AT 11,B; 130 IF D>7 THEN LET D=D-2*RND 140 IF PEEK (PEEK 16398+PEEK 16399*256)=128 THEN STOP 150 GOTO 60 160 SAVE "1022%3" 170 RUN ```