--- title: "RAM Runner" id: 51764 type: "computer_media" slug: "ram-runner" url: "http://localhost/computer_media/ram-runner/" markdown_url: "http://localhost/computer_media/ram-runner.md" published_at: "2023-08-13T23:14:31+00:00" modified_at: "2026-04-04T20:18:01+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2022/04/20230809-041332.jpg" excerpt: "A multi-round maze runner with machine code movement, 26 skill levels, high-score initials entry, and fully remappable keys — all bootstrapped from hex strings in BASIC." 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: "Timex Computer Corporation" slug: "timex-computer-corporation" taxonomy: "post_tag" url: "http://localhost/tag/timex-computer-corporation/" - 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_type: "Cassette" download_url: "https://archive.org/download/timex-sinclair-software-archive/RAM Runner (1983)(Timex)(US)(TS1000)(Cassette).zip" mediadate: "1983" producer_company: - id: 11401 title: "Timex Computer Corporation" type: "company" url: "http://localhost/company/timex-computer-corporation/" related_products: - id: 13741 title: "RAM Runner" type: "product" url: "http://localhost/product/ram-runner/" media_type_tags: "Game" --- RAM Runner is a maze-navigation game in which the player steers a character through a corridor, avoiding walls across multiple rounds and skill levels. The program embeds two large machine code payloads, encoded as hexadecimal strings in variables named `Q$`, which are decoded with a `FOR`/`NEXT` loop that converts each pair of hex-digit character codes into bytes and POKEs them into memory starting at fixed addresses (16516, 16756, 16938, 17113, 17413, and related locations). A custom key-configuration routine at line 6500 stores user-chosen key codes at a set of dedicated memory addresses and supports a blinking cursor effect during entry. Large character rendering for titles and scores is handled by a machine code routine called via `RAND USR`, which draws oversized glyphs by POKEing character data through the routine at address 16756+17. The game tracks a high score across sessions, prompts for three-character initials on a new high score, and offers 26 selectable skill levels mapped to a difficulty byte POKEd directly into the machine code region. *** ## Program Analysis ### Program Structure The program divides into several functional blocks: 1. **Lines 10–60:** REM statements containing encoded machine code data (the REMs are not documentation; their content is the actual binary payload stored as tokenized BASIC). 2. **Lines 100–462:** Machine code installation — hex strings in `Q$` are decoded and POKEd into six memory regions. 3. **Lines 500–565:** Screen template string construction (`S$`, `W$`, `F$`) using block graphics. 4. **Lines 700–820:** Title screen display, big-font rendering, and initial keypress wait. 5. **Lines 980–2040:** Main game loop — initialisation, countdown, machine code game execution, round management. 6. **Lines 4000–4830:** Score display, high-score check, and initials entry (alternate path). 7. **Lines 5000–5999:** Post-game score screen, new high-score initials entry, and loop back. 8. **Lines 6000–6999:** Level selection and key configuration screens. 9. **Lines 8000–8060:** Big-font character subroutine (drives machine code renderer). 10. **Lines 9000–9030:** “Rounds remaining” status line printer. ### Machine Code Installation Six separate machine code blocks are decoded and installed during startup. Each uses the same idiom: a hex string is stored in `Q$`, then a `FOR G=1 TO LEN Q$ STEP 2` loop reads pairs of characters, subtracts 28 from each `CODE` value to recover the nibble, multiplies the high nibble by 16, adds the low nibble, and POKEs the result to a base address plus offset `L`. | Lines | Base address | Purpose (inferred) | | --- | --- | --- | | 100–250 | 16516 | Core movement / display engine | | 260–370 | 16756 | Big-font / character renderer | | 375–420 | 16938 | Game logic / collision detection | | 421–444 | 17113 | Maze generation / REQ counter | | 445–462 | 17413 | Supplementary graphics routines | The subtraction of 28 from `CODE` exploits the fact that ZX81/TS1000 character codes for digits 0–9 are 28–37, so subtracting 28 yields 0–9, and the letters A–F have codes 38–43 after the same offset, giving the hex nibble directly. ### Big-Font Rendering The subroutine at line 8000 iterates over each character of string `P$`, POKEs its `CODE` value into address 16756, then calls `RAND USR (16756+17)` to invoke the machine code renderer. The result is positioned on screen using `PRINT AT BL,BC+(G-1)*4`, spacing glyphs four columns apart to accommodate oversized characters. This approach is used for the title words “RAM” and “RUNNER”, score labels, level labels, and key-config headings. ### Key Configuration Line 6511 dimensions array `A(4)` and lines 6512–6515 set its elements to addresses 16641, 16649, 16657, and 16665 — four slots within the machine code region where the up/down/left/right key codes are stored. During configuration (lines 6610–6670), a blinking-cursor effect is achieved by printing `CHR$ (BK*128)` and toggling `BK` with `LET BK=NOT BK` while polling `INKEY$`. The chosen key’s `CODE` is then POKEd directly into the appropriate address. ### Screen Template Construction Lines 510–565 build two full-screen string templates, `S$` (plain corridor) and `W$` (corridor with stipple fill `▒`), each 704 characters long (22 rows × 32 columns). Two rows of solid block graphics form the top and bottom borders; 18 middle rows have solid walls on columns 0–1 and 30–31 with open space (or stipple) between. `F$` is derived from `W$` with rows 10–19 filled solid and a “PRESS ANY KEY TO BEGIN” message embedded at offset 614. ### Game Loop The main loop (lines 1200–2040) re-initialises machine code parameters via a series of POKEs each round, prints the play-field string `S$`, shows a countdown from 5 to 0, then enters an inner loop at line 2022 calling `USR 16940` in a tight `GOTO` loop. The return value `L` is zero when the round ends (collision or completion). On completion, `REQ` (the required-actions counter, seeded by the machine code at 17113 via line 1557) is decremented; when it reaches zero a new round starts. Lives are tracked in `R`, initialised to 3; losing all lives falls through to the score screen at line 5000. ### Score Handling The raw score is maintained as a two-byte little-endian value in addresses 16514–16515, read back with `PEEK 16514+PEEK 16515*256`. At line 5006, it is scaled by `INT (SC*(LE/3))*2` where `LE` is the difficulty level (0–25), so higher skill levels multiply the score. A cap check at line 5007 sets `SC=-1` (displayed as `*WOW*`) if the value exceeds 999999. The high score persists in the BASIC variable `HIGH` across games within the same session. ### Initials Entry Two separate initials-entry paths exist: an older one at lines 4540–4550 using `INPUT I$`, and the main path at lines 5160–5290 using a character-by-character loop. The latter stores each keypress code in numeric array `I(3)`, handles the NEWLINE key (code 118) as a space via line 5274, POKEs the code into 16756, and calls the big-font renderer via `RAND USR (16756+17)` to display each initial in oversized form. ### Notable Techniques and Anomalies - The `("S" AND INT (R-1)<>1)` expression at line 9010 conditionally appends an “S” for pluralisation of “ROUND/ROUNDS” — a common ZX81 BASIC string-boolean trick. - The `("HIGH " AND NA)` expression at line 4015 conditionally includes “HIGH ” in the score label string using the same boolean-string idiom. - The level selection loop (lines 6080–6110) uses a single `PRINT AT` to highlight the current selection by printing its inverse-video equivalent from `R$`, then loops back to redraw on each keypress — a simple but effective menu technique. - Line 5007 uses `IF SC>999999 THEN LET SC=-1` as a sentinel; `-1` is then tested by line 5075 to substitute the display string. - The subroutine at line 4800 is a one-line keypress detector returning boolean `KEY`, called from the score display loops to allow early exit. - Lines 3010–3040 are a developer memory-dump utility (reachable only by direct `GO TO 3000`) that was left in the final listing. - The REM blocks at lines 10–60 are not comments — they store machine code as raw token data, a well-known technique for protecting or compressing binary payloads within a BASIC program. ## Source Code ``` 10 REM [.] E£RND7) U#RND#;)5 U#RND#;( CLEARTAN LN ▜RNDQ*TAN LN ▜RND# TAN E▙RND)▌ ;6▙RNDTAN GOSUB ##RNDU5RND RETURN 4▀▌/? RETURN▘4▀▖/▒ RETURN▝4▀£/▘$# RETURN 4▖▞▘/▞ RETURN+4▝▞=# RETURN 4▀£/▌ RETURN34▘$ GOSUB ##RNDTAN LN [V]▝###=Y COS LN [X]▛# RETURN74▖Y /+ RETURN64▖Y▘/$ RETURN54▖Y▀/▌ RETURN84▌Y▝M5RNDTAN LN [.]RNDLN PAUSE RNDLN [O]RNDLN [D]RNDLN [5]RND RETURN ""▘ COPY COPY"#[L]4 CLS/ SLOW................................................ 20 REM R ▗▖▄▝▐▞▟▘▚▌▙▀▜▛█5#INKEY$ #[B]*****- ACS >#5 2;:▖▞▖#7#7 FAST[J]ACS >*ACS >*ACS <*ACS <*5#INKEY$ ▐##E:RND#76:RND( STOPSTR$ )1 ;6:RNDSGN LPRINT $4INT )█ COPYE:RND;6:RND#TAN .............................................................................. 30 REM > LN [.]RNDLN PAUSE RNDLN [O]RNDLN [D]RNDLN #PI RETURN[R]▘▘ COS ▘ RETURN[E]C. RETURN ""EEPIF6EPI#[P]4CHR$ E:RNDQ[E]5# 6EPI/TAN LN ▜RNDQ E£RND▘/▀7Y*[Y]4▝Q "#[L]4 NEXT /[J]LN [5]RND PRINT ▘ COPY "#[L]4 CLS LET TAN ........................................................................ 40 REM ▞[R][▒]█▞0LN 6#LEN ▝#STR$ ▞>LN 6#SGN LEN ▝## GOSUB ##RND▞▀LN 6#5 AND PI#▞ ,,# RETURN 4▞ FAST5 OR PIO LPRINT # PRINT LN ▜RND LET #TAN 5 OR PIQ ▞(VAL LN <>PIAT ( RAND TAN #▞ VAL GOSUB #MRNDLN 4+ LOAD [5]?KR-▖K█INKEY$ █I▝[5]▀HOLN [~~]+ GOSUB #MRND#[B]C▀CHR$ (#AT LN 4+ LOAD ▖8OLN [~~]+#TAN E£RND) ▘NOT ▝#7 RETURN[R]4▘<"#[L]4 POKE #M OR PITAN █.................................................................................█....................................................... 50 REM E(RND7776*#LN ## GOSUB #▌# GOSUB ##RNDLN ▜RNDQ 5▌#)" ;<>" 1515 FOR S=5 TO 0 STEP -1 1520 PRINT AT 11,16;S 1530 NEXT S 1535 LET A$=STR$ 0 1540 PRINT AT 10,12;" " 1550 PRINT AT 11,16;" " 1555 RAND USR 17243 1557 LET REQ=PEEK 17113 2000 REM 2020 PRINT AT 12,15;"[E]";AT 12,15; 2021 PRINT AT 12,15; 2022 LET L=USR 16940 2023 IF L=0 THEN GOTO 2033 2024 LET REQ=REQ-1 2025 IF REQ THEN GOTO 2022 2027 LET R=R+1 2029 GOSUB 9000 2031 GOTO 2021 2033 LET R=R-1 2034 IF R>=1 THEN GOTO 1200 2035 FOR D=1 TO 10 2036 NEXT D 2040 GOTO 5000 3000 REM 3010 INPUT A 3020 FOR V=0 TO 1000 3030 PRINT A+V,PEEK (A+V) 3040 NEXT V 4000 REM SCORE ROUTINES 4010 LET SC=PEEK 16514+PEEK 16515*256 4015 LET K$="<<"+("HIGH " AND NA)+"SCORE="+STR$ SC+">>" 4017 LET O=LEN K$ 4020 PRINT AT 0,0;S$ 4025 FOR X=1 TO 8 4030 PRINT AT 10,15-O/2;K$ 4040 IF NA THEN PRINT AT 11,15-LEN I$/2;I$ 4060 FOR D=1 TO 10 4065 GOSUB 4800 4067 IF KEY THEN RETURN 4070 NEXT D 4080 PRINT AT 10,15-O/2;" "( TO O) 4090 IF NA THEN PRINT AT 11,15-LEN I$/2;" "( TO LEN I$) 4100 FOR D=1 TO 10 4105 GOSUB 4800 4106 IF KEY THEN RETURN 4110 NEXT D 4120 NEXT X 4130 RETURN 4200 REM 4500 REM 4510 LET NA=0 4520 GOSUB 4100 4530 IF SC<=HIGH THEN GOTO 4600 4540 PRINT AT 18,3;"ENTER YOUR INITIALS:" 4550 INPUT I$ 4600 LET NA=1 4610 GOSUB 4000 4620 IF KEY=0 THEN GOTO 4610 4630 GOTO 1000 4800 REM 4810 LET Z$=INKEY$ 4820 LET KEY=(Z$<>"") 4830 RETURN 5000 REM SCORE 5005 LET SC=PEEK 16514+PEEK 16515*256 5006 LET SC=INT (SC*(LE/3))*2 5007 IF SC>999999 THEN LET SC=-1 5010 PRINT AT 0,0;W$ 5020 LET BL=6 5030 LET BC=6 5040 LET P$="SCORE" 5050 GOSUB 8000 5060 LET BL=BL+6 5070 LET P$=STR$ SC 5072 LET BC=16-LEN P$*2 5075 IF SC=-1 THEN LET P$="*WOW*" 5080 GOSUB 8000 5081 FOR D=1 TO 50 5082 IF SC>HIGH THEN GOTO 5100 5083 IF INKEY$ <>"" THEN GOTO 1000 5084 NEXT D 5090 IF SC<=HIGH THEN GOTO 5300 5100 PRINT AT 0,0;W$ 5110 FOR V=3 TO 7 5120 PRINT AT V,3;" " 5130 NEXT V 5140 PRINT AT 4,4;"ENTER";AT 5,4;"YOUR";AT 6,4;"INITIALS" 5160 DIM I(3) 5200 FOR I=1 TO 3 5260 LET K$=INKEY$ 5270 IF K$="" THEN GOTO 5260 5274 IF CODE K$=118 THEN LET K$=" " 5276 POKE 16756,CODE K$ 5278 PRINT AT 11,9+(I-1)*5; 5280 RAND USR (16756+17) 5285 LET I(I)=CODE K$ 5290 NEXT I 5300 REM 5305 IF SC>HIGH THEN LET HIGH=SC 5310 PRINT AT 0,0;S$ 5315 PRINT AT 11,0;"██~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~██" 5320 LET P$="HIGH" 5330 LET BL=2 5340 LET BC=8 5350 GOSUB 8000 5360 LET BL=BL+4 5370 LET BC=6 5380 LET P$="SCORE" 5390 GOSUB 8000 5400 LET BL=BL+6 5410 LET BC=10 5420 LET P$=CHR$ (I(1))+CHR$ (I(2))+CHR$ (I(3)) 5430 GOSUB 8000 5440 LET P$=STR$ HIGH 5450 LET BL=BL+4 5460 LET BC=16-(LEN STR$ HIGH)*2 5470 GOSUB 8000 5480 FOR D=1 TO 50 5485 IF INKEY$ <>"" THEN GOTO 1000 5490 NEXT D 5500 GOTO 5000 5999 STOP 6000 REM 6005 SLOW 6010 PRINT AT 0,0;W$ 6020 LET P$="LEVEL" 6025 LET Q$="ABCDEFGHIJKLMNOPQRSTUVWXYZ" 6026 LET R$="[A][B][C][D][E][F][G][H][I][J][K][L][M][N][O][P][Q][R][S][T][U][V][W][X][Y][Z]" 6030 LET BL=6 6040 LET BC=6 6050 GOSUB 8000 6052 PRINT AT BL+5,4;" USE THE 5 OR 8 KEY TO " 6054 PRINT TAB 5;" SELECT SKILL LEVEL. ";TAB 11;TAB 19;TAB 6;" THEN PRESS ENTER. " 6060 LET Z$=" " 6065 LET LE=DEFLEV 6070 PRINT AT 16,2;Z$;AT 17,2;Z$;AT 18,2;Z$ 6080 PRINT AT 17,3;Q$ 6095 PRINT AT 17,2+LE;R$(LE) 6100 LET LE=LE-(INKEY$ ="5" AND LE>1)+(INKEY$ ="8" AND LE<26) 6105 IF INKEY$ =CHR$ 118 THEN GOTO 6120 6110 GOTO 6080 6120 LET DEFLEV=LE 6125 LET LE=26-LE 6130 POKE 17027,LE 6140 RETURN 6500 REM KEYS 6505 SLOW 6510 PRINT AT 0,0;S$ 6511 DIM A(4) 6512 LET A(1)=16641 6513 LET A(2)=16649 6514 LET A(3)=16657 6515 LET A(4)=16665 6520 LET P$="KEYS" 6530 LET BL=4 6540 LET BC=8 6550 GOSUB 8000 6555 PRINT AT BL+4,0;"██▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄██" 6560 LET BL=BL+4 6565 LET S1=12 6567 LET S2=18 6570 PRINT AT BL+2,S1;"UP";TAB S2;CHR$ PEEK A(1);TAB S1;"DOWN";TAB S2;CHR$ PEEK A(2);TAB S1;"LEFT";TAB S2;CHR$ PEEK A(3);TAB S1;"RIGHT"; 6575 PRINT TAB S2;CHR$ PEEK A(4);TAB S1;TAB 4;"ARE THESE SATISFACTORY:";TAB 9;"PRESS Y OR N" 6580 LET K$=INKEY$ 6590 IF K$="Y" THEN RETURN 6600 IF K$<>"N" THEN GOTO 6580 6605 PRINT AT BL+2,S2;" ";TAB S2;" ";TAB S2;" ";TAB S2;" " 6607 PRINT AT BL+7,4;" ENTER YOUR CHOICES ";TAB 9;" " 6610 FOR V=1 TO 4 6620 LET BK=0 6630 PRINT AT BL+1+V,S2;CHR$ (BK*128) 6640 LET BK=NOT BK 6650 LET K$=INKEY$ 6655 IF K$="" THEN GOTO 6630 6660 PRINT AT BL+1+V,S2;K$ 6665 POKE A(V),CODE K$ 6670 NEXT V 6675 GOTO 6570 6685 FAST 6690 RETURN 6999 STOP 8000 REM 8010 LET A=16756 8020 FOR G=1 TO LEN P$ 8030 PRINT AT BL,BC+(G-1)*4; 8040 POKE 16756,CODE P$(G) 8045 RAND USR (A+17) 8050 NEXT G 8060 RETURN 9000 REM 9010 LET O$=".."+STR$ (INT R-1)+" ROUND"+("S" AND INT (R-1)<>1)+" REMAINING.." 9020 PRINT AT 21,16-LEN O$/2;O$ 9030 RETURN ```