Circuit Board Scramble is a maze-navigation game in which the player guides a character around a circuit board diagram to reach target components such as the voltage regulator, ULA, RAM, ROM, CPU, and edge connector. The program loads machine code from tape at addresses 39694 and 51450, using USR calls throughout to handle joystick input, sound, scoring, and sprite movement. Player position and score are tracked via POKEs and PEEKs into specific machine code data areas around address 40000–40800, with the high score stored at addresses 39800–39802 across sessions using INPUT prompts. The keyboard control option lets the player define four custom keys, whose character codes are POKEd directly into the machine code routine at addresses 39844–39859. A scrolling ticker-tape message at line 3030–3070 is implemented in BASIC by printing successive 8-character substrings of a padded string.
Program Analysis
Program Structure
The program is divided into several functional regions that do not follow a simple top-to-bottom flow. The loader block (lines 8000–8090 in the first group) sets up the screen, loads a splash graphic and a main BASIC/code file, then saves a combined loader. The main game logic occupies lines 1–4660, with high-numbered lines (8000–8640) reserved for diagnostics, error handling, and keyboard configuration. Control flow is driven entirely by GO TO and GO SUB rather than structured loops.
Loading Sequence
On startup the loader displays a progress bar of colored block characters (line 2, using \:: █ graphics in six ink colors), then loads two machine code blocks: “pc” CODE at address 51450 for 14000 bytes, and “code” CODE at 39694 for 5106 bytes. An ON ERR GO TO 8200 at line 37 guards the second load. After loading, a flashing “TURN OFF TAPE PLAYER” notice is shown for about seven seconds (PAUSE 350).
Machine Code Integration
Almost all real-time activity is delegated to machine code via USR calls. The key entry points are:
USR 51470— screen/display refresh (called asc1=USR 51470orl=USR 51470)USR 39694— joystick read, returns direction code stored iniUSR 39840— keyboard handler (called whenINKEY$<>"")USR 40363— collision/pickup sound effectUSR 40381— component connection sound/score updateUSR 40420— timer encoding helper (packstoreinto a 16-bit POKE pair first)USR 44490,USR 44520— level-transition effectsUSR 58420— transition to circuit-board sub-game
The return value of most USR calls is assigned to a throwaway variable (l, c1, n, c, m) simply to satisfy BASIC’s expression syntax; the real effect is a side effect inside the machine code.
Game Phases
The game has two distinct phases. Phase 1 (lines 995–1080) is a countdown timer maze: the player navigates toward screen position x=14, y=11 while a timer t counts down. Phase 2 (lines 1990–2090 and 1500–1598) is a circuit-board component hunt: a randomly selected target component flashes on screen and the player must reach it. Transition between phases is managed by score thresholds checked at line 2705/2710 and by the co counter reaching 350 (line 2042).
Movement Dispatch
Player movement does not use a conventional IF i=... THEN block. Instead, the joystick/keyboard machine code routine at USR 39694 or USR 39840 appears to directly GO TO one of four handler lines by manipulating the BASIC interpreter’s program counter. The four directional handlers for Phase 1 are at lines 1101, 1141, 1181, and 1221; for Phase 2 at 2101, 2141, 2181, and 2221; for the circuit-board sub-game at 1600, 1640, 1680, and 1720. Each handler updates x or y, checks the cell attribute for the value 59 (INK 3, PAPER 7, BRIGHT 1 — a highlighted tile), prints the appropriate UDG character, erases the previous cell, then jumps back to the main loop.
UDG Characters
Four UDG characters are used for the player sprite, referenced as \a (left-facing), \b (down-facing), \c (right-facing), and \e (up-facing). Their bitmaps are defined by the machine code POKEd at startup — the BASIC listing never calls POKE USR directly in visible lines, implying the UDGs are set up inside the loaded machine code block.
Score and High Score Storage
The live score is maintained in machine code memory: address pair 40418/40419 holds the current score as a 16-bit little-endian value, read via PEEK 40418+256*PEEK 40419. The high score is stored at 39801/39800 (note reversed byte order for the high score vs. score) and the associated level at 39802. On game start the player is prompted to enter the previous high score, which is split into low and high bytes and POKEd manually (line 43).
Difficulty and Timer
Lines 131–136 map the chosen difficulty level (0–5) to a starting timer value t ranging from 550 to 800. At line 3500, each time the player completes 350 component connections (co>349), the timer is reduced by 50 to a minimum of 450, increasing difficulty progressively. The level value is also POKEd into a range of machine code addresses (40301 to 40391, step 9) at lines 100–120.
Scrolling Message Ticker
When a random component task is assigned (r < 10, line 2044), a subroutine at line 3000 builds a long string a$ describing the target. Lines 3030–3070 pad it with spaces and scroll it across 8 columns at row 8 by printing successive substrings a$(w TO w+7) with a PAUSE 10 between each, a clean software ticker-tape effect entirely in BASIC.
Keyboard Configuration
Lines 8600–8635 allow the player to define custom keys for all four directions. Each key’s CODE value is POKEd directly into the machine code at hardcoded addresses: left=39844, down=39849, up=39854, right=39859. This means the machine code reads key codes from RAM rather than having them compiled in, providing a simple but effective remapping mechanism.
Error Handling
Lines 8200–8230 implement a retry loop for tape-load errors using ON ERR: on error the system is reset via ON ERR RESET, pauses briefly, then re-arms the error trap and resumes with ON ERR CONTINUE. This guards the potentially unreliable tape load of “code” at line 37.
Anomalies and Notes
- Line
1340usesGO TO 1320, but line 1320 does not exist in the listing; this creates a tight busy-wait loop that effectively pollsINKEY$at lines 1330/1334 until a key is pressed, which is an intentional (if undocumented) technique. - Line
2700is targeted byGO TO 2700at lines 1075 and 1596, but the listing shows line2705as the first line in that region — line 2700 does not appear, so execution falls through to 2705, which is the intended behavior. - The diagnostic block at the second set of lines 8000–8030 (which conflicts with the loader’s 8000–8090 line numbers) prints raw PEEK values for addresses 30390–30460 and then STOPs — this appears to be a debugging fragment left in the listing.
- The 16-bit score is stored with the low byte at 40418 and high byte at 40419, but the high score uses reversed addressing (low byte at 39801, high byte at 39800), an asymmetry that would cause the high score display to be byte-swapped relative to the live score if both exceed 255.
Content
Source Code
8000 INK 0
8010 PAPER 0
8020 BORDER 0
8030 PRINT AT 15,0;" "
8040 CLEAR 39799
8050 LOAD "zman"SCREEN$
8060 PRINT INK 1;AT 16,9;"\::"
8070 LOAD "zman"
8080 PRINT AT 15,0;" "
8090 SAVE "cb" LINE 8000
1 GO TO 40
2 PRINT INK 2;AT 16,10;"\::"; INK 3;AT 16,11;"\::"; INK 4;AT 16,12;"\::"
13 LOAD "pc"CODE 51450,14000
16 PRINT INK 5;AT 16,13;"\::"; INK 6;AT 16,14;"\::"; INK 7;AT 16,15;"\::"
37 LOAD "code"CODE 39694,5106: ON ERR GO TO 8200
38 PRINT INK 0; PAPER 7; BRIGHT 1; FLASH 1;AT 15,8;"TURN OFF";AT 16,7;"TAPE PLAYER"
39 PAUSE 350
40 LET c1=USR 51470
42 INPUT "PREVIOUS HIGH SCORE ";hs
43 POKE 39801,hs-(INT (hs/256))*256: POKE 39800,INT (hs/256)
44 INPUT "LEVEL OF PLAY 0 - 5 ";lp
45 POKE 39802,lp
48 INK 2
50 PAPER 7
56 BORDER 1
59 CLS
60 LET c1=USR 51470
65 POKE 40418,0: POKE 40419,0
70 LET e=0
80 LET score=0
89 POKE 40403,1
90 INPUT "INPUT YOUR LEVEL 0 - 5 ";dur
100 FOR f=40301 TO 40391 STEP 9
110 POKE f,dur
120 NEXT f
131 IF dur=0 THEN LET t=550
132 IF dur=1 THEN LET t=600
133 IF dur=2 THEN LET t=650
134 IF dur=3 THEN LET t=700
135 IF dur=4 THEN LET t=750
136 IF dur=5 THEN LET t=800
141 IF dur>5 OR dur<0 THEN GO TO 90
150 LET time=t
160 LET retime=t
200 INPUT "ENTER JOYSTICK j KEYS k "; LINE m$
205 IF m$="j" THEN GO TO 250
208 IF m$="k" THEN GO TO 8600
210 GO TO 200
240 REM 1983 by Ken Frink
250 GO TO 950
900 LET t=retime
910 INK 2
912 PAPER 7
914 BORDER 1
916 CLS
920 LET l=USR 51470
930 LET score=0
940 POKE 40418,0: POKE 40419,0
950 FOR f=1 TO 200
960 NEXT f
970 GO SUB 4500
995 POKE 40793,0
997 POKE 40403,1
1000 LET y=12
1005 LET x=20
1010 PRINT AT y-2,x-2;"\c"
1020 LET t=t-1
1021 IF t<1 THEN GO TO 1250
1022 PRINT INK 0;AT 20,18;"\::";t;"\::"
1025 IF INKEY$<>"" THEN LET l=USR 39840
1030 IF m$="j" THEN LET i=USR 39694
1060 PRINT AT 20,4;PEEK 40418+256*PEEK 40419
1070 IF (PEEK 40418+256*PEEK 40419)-score>=287 THEN POKE 40793,1
1075 IF x=14 AND y=11 THEN GO TO 2700
1080 GO TO 1020
1101 LET x=x-1: IF ATTR (y-2,x-2)=59 THEN LET l=USR 40363
1105 PRINT AT y-2,x-2;"\a";AT y-2,x-1;" ": GO TO 1020
1141 LET y=y+1: IF ATTR (y-2,x-2)=59 THEN LET l=USR 40363
1145 PRINT AT y-2,x-2;"\b";AT y-3,x-2;" ": GO TO 1020
1181 LET y=y-1: IF ATTR (y-2,x-2)=59 THEN LET l=USR 40363
1185 PRINT AT y-2,x-2;"\e";AT y-1,x-2;" ": GO TO 1020
1221 LET x=x+1: IF ATTR (y-2,x-2)=59 THEN LET l=USR 40363
1225 PRINT AT y-2,x-2;"\c";AT y-2,x-3;" ": GO TO 1020
1250 FOR f=1 TO 20
1260 LET c1=USR 51470
1270 CLS
1280 NEXT f
1282 PRINT AT 5,5;"PREVIOUS HIGH ";PEEK 39801+256*PEEK 39800;AT 6,12;"LEVEL ";PEEK 39802
1285 PRINT AT 9,5;"YOUR SCORE IS ";PEEK 40418+256*PEEK 40419;AT 10,12;"LEVEL ";dur
1290 IF PEEK 40418+256*PEEK 40419>PEEK 39801+256*PEEK 39800 THEN POKE 39801,PEEK 40418: POKE 39800,PEEK 40419: POKE 39802,dur: PRINT AT 13,8;"CONGRATULATIONS"," YOU HAVE JUST BECOME"," CHAMP OF THE CHIPS": GO TO 1310
1310 PRINT AT 17,0;" PRESS THE G KEY FOR ANOTHER GO"
1315 PRINT AT 19,0;" PRESS THE B KEY TO CHANGE GAME"
1330 IF INKEY$="g" THEN GO TO 900
1334 IF INKEY$="b" THEN GO TO 48
1340 GO TO 1320
1350 GO TO 900
1400 REM CIRCUIT BOARD SCRAMBLE \* 1983 BY KEN FRINK
1500 PAPER 7
1510 BORDER 1
1520 LET l=USR 51470
1525 GO SUB 4600
1530 LET y=12: LET x=20
1535 PRINT AT 20,4;PEEK 40418+256*PEEK 40419
1540 PRINT AT y-2,x-2;"\c"
1542 POKE 40793,0
1545 PRINT INK 2; BRIGHT 1; FLASH 1;AT te-2,as-2;"*"
1550 LET e=e-1: IF e<1 THEN GO TO 1250
1560 PRINT INK 0;AT 20,18;"\::";e;"\::"
1570 IF INKEY$<>"" THEN LET l=USR 39840
1580 LET i=USR 39694
1595 IF x=as AND y=te THEN POKE 40793,1
1596 IF x=14 AND y=11 THEN GO TO 2700
1598 GO TO 1550
1600 LET x=x-1: PRINT AT y-2,x-2;"\a";AT y-2,x-1;" ": GO TO 1550
1640 LET y=y+1: PRINT AT y-2,x-2;"\b";AT y-3,x-2;" ": GO TO 1550
1680 LET y=y-1: PRINT AT y-2,x-2;"\e";AT y-1,x-2;" ": GO TO 1550
1720 LET x=x+1: PRINT AT y-2,x-2;"\c";AT y-2,x-3;" ": GO TO 1550
1990 LET p=1100
2000 POKE 40426,t-256*INT (t/256): POKE 40427,INT (t/256): LET m=USR 40420: LET t=0
2001 INK 2
2002 PAPER 6
2004 BORDER 3
2005 LET y=23: LET x=18
2007 POKE 40403,2
2010 PRINT INK 2;AT y-2,x-2;"\e"
2015 POKE 39869,240: POKE 39870,122: POKE 39901,126: POKE 39902,123: POKE 39960,18: POKE 39961,124: POKE 39931,166: POKE 39932,124: POKE 39990,58: POKE 39991,125: POKE 39998,41: POKE 39999,164
2030 PRINT PAPER 6; FLASH 1;AT 21,15;"\::"
2040 LET p=p-1: LET e=e-1: LET r=INT (RND*1500)
2041 IF p<1 THEN GO TO 1250
2042 IF co>349 THEN GO TO 3500
2044 IF r<10 THEN GO SUB 3000
2050 IF INKEY$<>"" THEN LET l=USR 39840
2060 IF m$="j" THEN LET i=USR 39694
2080 PRINT INK 0; PAPER 7; BRIGHT 1;AT 13,13;PEEK 40418+256*PEEK 40419
2085 IF y=23 AND x=17 THEN GO TO 2500
2090 GO TO 2040
2101 LET x=x-1: IF ATTR (y-2,x-2)=52 THEN LET n=USR 40381: LET co=co+1
2105 PRINT AT y-2,x-2;"\a";AT y-2,x-1;" ": GO TO 2040
2141 LET y=y+1: IF ATTR (y-2,x-2)=52 THEN LET n=USR 40381: LET co=co+1
2145 PRINT AT y-2,x-2;"\b";AT y-3,x-2;" ": GO TO 2040
2181 LET y=y-1: IF ATTR (y-2,x-2)=52 THEN LET n=USR 40381: LET co=co+1
2185 PRINT AT y-2,x-2;"\e";AT y-1,x-2;" ": GO TO 2040
2221 LET x=x+1: IF ATTR (y-2,x-2)=52 THEN LET n=USR 40381: LET co=co+1
2225 PRINT AT y-2,x-2;"\c";AT y-2,x-3;" ": GO TO 2040
2500 LET c=USR 44520
2530 GO TO 1500
2705 IF (PEEK 40418+256*PEEK 40419)-score>289 THEN LET c=USR 44490: GO TO 2730
2710 IF (PEEK 40418+256*PEEK 40419)-score<288 THEN LET c=USR 58420: LET co=0: GO TO 1990
2730 POKE 40426,e-256*INT (e/256): POKE 40427,INT (e/256): LET m=USR 40420: LET e=0
2740 GO TO 2001
3000 LET b$="*** GO TO CIRCUIT BOARD AND CHECK "
3001 IF r=0 THEN LET a$=b$+"VOLTAGE REGULATOR ***": LET as=33: LET te=18
3002 IF r=1 THEN LET a$=b$+"UNCOMMITTED LOGIC ARRAY ***": LET as=14: LET te=3
3003 IF r=2 THEN LET a$=b$+"32 K RANDOM ACCESS MEMORY ***": LET as=2: LET te=23
3004 IF r=3 THEN LET a$=b$+"READ ONLY MEMORY ***": LET as=29: LET te=3
3005 IF r=4 THEN LET a$=b$+"CENTRAL PROCESSOR UNIT ***": LET as=25: LET te=3
3006 IF r=5 THEN LET a$=b$+"TV MODULATOR ***": LET as=2: LET te=2
3007 IF r=6 THEN LET a$=b$+"LOUDSPEAKER ***": LET as=33: LET te=23
3008 IF r=7 THEN LET a$=b$+"9 VOLT INPUT ***": LET as=33: LET te=5
3009 IF r=8 THEN LET a$=b$+"EDGE CONNECTOR ***": LET as=31: LET te=2
3010 IF r=9 THEN LET a$=b$+"CASSETTE INPUT ***": LET as=9: LET te=2
3015 PRINT PAPER 7;AT 13,12;" "
3020 PRINT INK 2; PAPER 7; BRIGHT 1; FLASH 1;AT 13,14;p
3030 LET a$=" "+a$+" "
3040 LET e=150: LET b=LEN (a$)-8
3050 FOR w=1 TO b
3060 PRINT INK 2; PAPER 7; BRIGHT 1;AT 8,12;a$(w TO w+7): PAUSE 10
3070 NEXT w
3075 PRINT PAPER 7;AT 13,12;" "
3080 RETURN
3500 LET t=time-50
3505 LET time=t
3510 IF t<450 THEN LET t=450
3520 LET c1=USR 51470
3530 LET score=PEEK 40418+256*PEEK 40419
3540 INK 2
3550 PAPER 7
3555 BORDER 1
3557 GO SUB 4500
3560 GO TO 995
4500 POKE 39869,252: POKE 39870,110
4510 POKE 39901,190: POKE 39902,111
4520 POKE 39960,67: POKE 39961,112
4530 POKE 39931,200: POKE 39932,112
4540 POKE 39990,77: POKE 39991,113
4550 POKE 39998,129: POKE 39999,159
4560 RETURN
4600 POKE 39869,94: POKE 39870,118
4610 POKE 39901,185: POKE 39902,118
4620 POKE 39960,9: POKE 39961,119
4630 POKE 39931,89: POKE 39932,119
4640 POKE 39990,169: POKE 39991,119
4650 POKE 39998,129: POKE 39999,159
4660 RETURN
8000 FOR f=30390 TO 30460
8010 PRINT f;" ";PEEK f
8020 NEXT f
8030 STOP
8200 ON ERR RESET
8210 PAUSE 100
8220 ON ERR GO TO 8200
8230 ON ERR CONTINUE
8400 IF INKEY$<>"" THEN LET i=USR 39840
8500 DIM s(2)
8510 DIM d(11,14)
8530 STOP
8600 INPUT "KEY FOR UP MOTION ";U$
8605 POKE 39854,CODE U$
8610 INPUT "KEY FOR DOWN MOTION ";D$
8615 POKE 39849,CODE D$
8620 INPUT "KEY FOR LEFT MOTION ";L$
8625 POKE 39844,CODE L$
8630 INPUT "KEY FOR RIGHT MOTION ";R$
8635 POKE 39859,CODE R$
8640 GO TO 250
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


