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:
- 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).
- Lines 100–462: Machine code installation — hex strings in
Q$are decoded and POKEd into six memory regions. - Lines 500–565: Screen template string construction (
S$,W$,F$) using block graphics. - Lines 700–820: Title screen display, big-font rendering, and initial keypress wait.
- Lines 980–2040: Main game loop — initialisation, countdown, machine code game execution, round management.
- Lines 4000–4830: Score display, high-score check, and initials entry (alternate path).
- Lines 5000–5999: Post-game score screen, new high-score initials entry, and loop back.
- Lines 6000–6999: Level selection and key configuration screens.
- Lines 8000–8060: Big-font character subroutine (drives machine code renderer).
- 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 ATto highlight the current selection by printing its inverse-video equivalent fromR$, then loops back to redraw on each keypress — a simple but effective menu technique. - Line 5007 uses
IF SC>999999 THEN LET SC=-1as a sentinel;-1is 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.
Content
Image Gallery
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▌#)" ;<<STR$ FOR #7# FOR F GOSUB ##RND▘: GOSUB [S]E*## RETURN▌C=76*#M5RND GOSUB ##RND LPRINT #7#/[V]TAN 5▌#▞▒LN ## FAST5;#F#- ;#E+## LPRINT ( GOTO #7#7 GOSUB ##RND FASTLN ▜RND6+# LPRINT TAN ................................................................................................................................................................................
60 REM ......................................................................................................................................................................................................................................................................................................
100 LET Q$="2A0C40231100003A7B405F191121003A7C40471910FDC9"
110 LET Q$=Q$+"CD84403617C9"
120 LET Q$=Q$+"CD84407E000000C92A824011050019228240C9"
130 LET Q$=Q$+"ED4B7B403A2140FE00200305180FFE012003041808FE0220030C18010D"
140 LET Q$=Q$+"78FE00200406011806FE152002061479FE0020030C1805FE1F20010DED437B40C9"
150 LET Q$=Q$+"CDBB02444D51143E00C8CDBD077EFE3620043E001815FE2620043E01180DFE3420043E031805FE3520053E02322140C9"
160 LET Q$=Q$+"CD9B40CDF240CDB440CDA940CDA140FE00C001FFFF0B78B120FB18E4"
190 LET L=0
200 FOR G=1 TO LEN Q$ STEP 2
210 LET B1=CODE Q$(G)-28
220 LET B2=CODE Q$(G+1)-28
230 POKE 16516+L,B1*16+B2
240 LET L=L+1
250 NEXT G
260 LET Q$="00008704830285068101860582038407802174417EA71717D8171600CB125F21001E190E04060456235E23E5AFCB1217CB1217CB1317CB1317217541856F7E2A0E407723220E4010E3D5111D"
270 LET Q$=Q$+"0019220E40D1E10D20CF1180FF2A0E4019220E407EC9"
300 REM
305 LET L=0
310 FOR G=1 TO LEN Q$ STEP 2
320 LET A1=CODE Q$(G)-28
330 LET A2=CODE Q$(G+1)-28
340 LET A=A1*16+A2
350 POKE 16756+L,A
360 LET L=L+1
370 NEXT G
375 LET Q$="0000CD9B40CDF240CDB440CDA940CD7D42FEB7010100C8010000FEAA281BFE00C02A2A422B222A427CB5"
377 LET Q$=Q$+"20D62A0E4036AA212000222A4218C9CD844036002A0C40011803233E17BE200236000B78B120F318AF"
379 LET Q$=Q$+"CDA140F501FF000B78B120FBF1C9"
398 LET L=0
400 FOR G=1 TO LEN Q$ STEP 2
405 LET A1=CODE Q$(G)-28
407 LET A2=CODE Q$(G+1)-28
410 LET A=A1*16+A2
415 POKE 16938+L,A
417 LET L=L+1
420 NEXT G
421 LET Q$="00B78880061CCD2243C6025FD50612CD2243D1C6024B47ED437B400603CD224321DA424F06000979FE002006E521D942"
422 LET Q$=Q$+"34E17EF5CD8440F177C921D94236000610C5CDDD42C110F9C9"
423 LET Q$=Q$+"480600C5ED4B3240CD2015EFA10F303716043080410000802E02A1032D34"
424 LET Q$=Q$+"CD8A15ED4332407EA72803D61077C1CD2015EF042434CD8A1579C9"
425 LET Q$=Q$+"2A0C4011000001D7027E23FEB72001130B78B120F47B32D942C9"
430 LET L=0
432 FOR G=1 TO LEN Q$ STEP 2
434 LET A1=CODE Q$(G)-28
436 LET A2=CODE Q$(G+1)-28
438 LET A=A1*16+A2
440 POKE 17113+L,A
442 LET L=L+1
444 NEXT G
445 LET Q$="00000000000000000000000000000000000000003526392D3C263E38"
446 LET Q$="2A1040232323221744CD6544ED4B0544ED437B40CD84403600210544110B00191313D5EB4E2346EB2BED437B40010E00EDB82A17447EFE05281423221744322140ED4B7B40E171237018BBC9"
447 LET Q$=Q$+"2105440608CD7E44E52119442B581600197E2A154477E110EC4E234623ED437B40E5CD8440221544E1C9"
450 LET L=0
452 FOR G=1 TO LEN Q$ STEP 2
454 LET A1=CODE Q$(G)-28
456 LET A2=CODE Q$(G+1)-28
458 POKE L+17413,A1*16+A2
460 LET L=L+1
462 NEXT G
500 REM
510 LET Z$="████████████████████████████████"
520 LET S$=Z$+Z$
525 LET W$=Z$+Z$
530 FOR V=1 TO 18
540 LET S$=S$+"██ ██"
545 LET W$=W$+"██▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒██"
550 NEXT V
560 LET S$=S$+Z$+Z$
565 LET W$=W$+Z$+Z$
600 LET F$=W$
620 FOR H=10 TO 19
630 LET F$(H*32 TO H*32+31)="████████████████████████████████"
640 NEXT H
650 LET F$(614 TO 614+21)="[P][R][E][S][S]█[A][N][Y]█[K][E][Y]█[T][O]█[B][E][G][I][N]"
700 REM
705 SLOW
710 PRINT USR 8192;"SAVE:P:RAM-RUNNER:"
720 PRINT AT 0,0;F$
721 PRINT AT 1,0;"████████████████████████████████"
730 LET BL=4
740 LET BC=10
750 LET P$="RAM"
760 GOSUB 8000
770 LET P$="RUNNER"
780 LET BL=12
790 LET BC=4
800 GOSUB 8000
810 LET K$=INKEY$
820 IF K$="" THEN GOTO 810
980 LET HIGH=0
990 LET DEFLEV=16
1000 REM
1005 LET REQ=0
1010 LET DIR=0
1020 LET CX=15
1030 LET CY=10
1040 POKE 16417,DIR
1050 POKE 16507,CX
1060 POKE 16508,CY
1065 GOSUB 6500
1070 LET PLACE=16539
1080 LET CHECK=16545
1090 LET SCORE=16553
1100 LET NXTLOC=16564
1110 LET KB=16626
1120 POKE 16514,0
1130 POKE 16515,0
1140 GOSUB 6000
1150 LET R=3
1200 REM
1205 POKE 16988,100
1210 POKE 16507,CX
1220 POKE 16508,CY
1230 POKE 16417,DIR
1240 POKE 16938,32
1250 POKE 16939,0
1285 SLOW
1290 PRINT AT 0,0;S$
1295 GOSUB 9000
1300 REM
1310 LET L=USR 17171
1312 POKE 16507,CX
1314 POKE 16508,CY
1330 LET Q$=" "
1340 PRINT AT 9,10;Q$;TAB 10;Q$;TAB 10;Q$
1500 REM
1510 PRINT AT 10,12;"<<[R][E][A][D][Y]>>"
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.