This program is a BASIC implementation of the arcade game Q*bert, where the player navigates a character across a diamond-shaped board while avoiding enemies named Sammy and the Snake. Movement is controlled via Q/W/A/S keys or a joystick (read via the STICK function), with diagonal movement dispatched through a computed GOSUB using PEEK 23560. The program makes extensive use of machine code routines loaded at addresses 65200–65330 for animation and sound effects, called via USR. A ten-entry high-score table with name entry is maintained in a string array, and UDGs (user-defined graphics) are used for all character sprites.
Program Analysis
Program Structure
The program is organized into several well-defined functional blocks:
- Lines 999, 5800–5840: Entry point; sets up border/colors and displays a title/credits screen.
- Lines 6710–6890: Initialization — plays intro music, dimensions the high-score array, defines sprite strings using UDG escape sequences, and falls through to the attract loop.
- Lines 5500–5790: Attract/menu loop — cycles through character showcase, controls help, scoring info, and the leaderboard, returning to
1000on keypress. - Lines 1000–1100: Main game loop — initializes variables, draws HUD, plays level start tune, then repeatedly reads input, dispatches movement, moves enemies, and redraws.
- Lines 1500–2099: Movement subroutines for four diagonal directions (down-left, up-left, down-right, up-right), each separated by a dummy
RETURNat the base of the group. - Lines 2800–2980: Enemy AI for Sammy (the bouncer) and the Snake, using random direction selection each turn.
- Lines 3000–3040: Death sequence — plays sounds, animates, decrements lives.
- Lines 3500–3540: Egg collection — increments score, checks for level completion.
- Lines 4000–4070: Enemy-collision death sequence with explosion effect.
- Lines 5000–5240: Game over and high-score entry.
- Lines 9900–9970: Save/load block for the BASIC program and associated machine code.
Input Dispatching via Computed GOSUB
The key input mechanism is a notable idiom. Line 1075 reads the joystick state via STICK (1,1) and stores an ASCII key code into system variable address 23560:
POKE 23560, (113*(j=5) + 119*(j=9) + 97*(j=6) + 115*(j=10))
The values 113, 119, 97, 115 correspond to the ASCII codes for Q, W, A, S respectively. Line 1050 then dispatches to a movement subroutine with:
GO SUB PEEK 23560 * 5 + 1500
The mapping is:
| Key | ASCII | PEEK*5+1500 | Target line | Direction |
|---|---|---|---|---|
| Q | 113 | 2065 | 2065 | Up-left |
| W | 119 | 2095 | 2095 | Up-right |
| A | 97 | 1985 | 1985 | Down-left |
| S | 115 | 2075 | 2075 | Down-right |
| (none) | 0 | 1500 | 1500 | No-op RETURN |
Dummy RETURN statements are placed at lines 1984, 2064, 2074, and 2094 to catch any off-by-one addresses that might land between valid entry points. Line 1500 itself is the no-input no-op.
Machine Code Usage
Several calls to USR at high memory addresses appear throughout the program. These invoke pre-loaded machine code routines saved separately as a CODE block (see line 9910: SAVE "Q-BERT" CODE 58280,7225). The routines and their apparent purposes are:
| Address | Usage context | Apparent purpose |
|---|---|---|
| 65200 | After movement delta applied | Delay / animation frame |
| 65230 | Death/collision animation loop | Screen flash or shake effect |
| 65250 | Death animation loop | Further animation frame |
| 65275 | Death animation loop | Further animation frame |
| 65312 | Level init and intro | Board drawing / initialization |
| 65330 | On death/collision | Flash/explosion effect trigger |
The return value of each USR call is assigned to the dummy variable l (e.g., LET l=USR 65312) purely to satisfy BASIC syntax, as the results are not used.
Sprite Representation
All character sprites are two rows of two UDG characters each, stored in short strings. The variables a$ through i$ are initialized at lines 6800–6820 using UDG escape sequences (e.g., \a\b for the first row of the hero). The hero uses UDGs \a–\d for display and erase (blank-tile) versions, enemies use \e–\l and \n–\q, and a single-character UDG \m is used for Sammy’s erase frame. The program draws and erases sprites by printing with OVER 1, which XORs pixels — printing the same image twice restores the background.
Enemy AI
Both enemies (Sammy at bx,by and the Snake at sx,sy) use the same movement strategy each game tick in subroutine 2800–2980. A random direction is chosen via GO TO RND*4+2801 (or 2901), selecting one of four diagonal moves. Boundary and screen-content checks prevent movement off the board. The Snake has an additional behavior (line 2970): with a probability that increases with round number (.9-ro/200), it drops an “o” marker on a vacated cell, deducting 50 from the field counter fi — effectively placing obstacles for the player.
Collision Detection
Collision detection is performed entirely via SCREEN$ character reads rather than coordinate comparison. Movement routines check SCREEN$ (qy+1, qx)=" " to detect falling off the board (empty space below), and check adjacent cells for valid landing squares. Enemy-to-player collision is checked by coordinate equality (e.g., bx=qx AND by=qy) after each enemy move.
High-Score Table
The leaderboard uses DIM w$(12,24) — a 12-row by 24-character string array. Only 10 entries are displayed; the extra rows serve as overflow guards. Scores are left-padded to 6 digits with "000000"(TO 6-LEN STR$ sc)+STR$ sc for lexicographic comparison. The insertion sort at lines 5050–5070 walks down the table comparing string values to find the insertion point.
HUD and Lives Display
Lives remaining are displayed as pairs of UDG characters — "\a\b\a\b\a\b"(TO li*2) — which neatly scales the display to show exactly li sprite pairs by using a string slice. Score is right-justified by printing at column 7-LEN STR$ sc.
Level Progression
The field counter fi tracks egg collection progress. Collecting an egg (an “o” character detected by SCREEN$) adds 50 to both sc and fi. When fi reaches 1350 (27 eggs × 50 points), the level-complete sequence fires at line 3510, awarding a 500-point bonus and incrementing the round counter ro (capped at 120), which influences enemy speed and Snake drop probability.
Notable Anomalies
- Line 5010 uses variable
ain DATA statements (e.g.,a,0,a*2,7). This is legal in Spectrum BASIC —READevaluates expressions, soais read as the current numeric variable. The value ofais set to.135+q/500at line 5100 beforeRESTORE 5010is called, making the tune tempo dependent on leaderboard rank. - The
GO TO RND*4+2801idiom generates a non-integer target; BASIC truncates it, so valid targets are 2801–2804 and 2901–2904 — exactly the four direction sub-blocks. - Line 1037 uses
INPUT ""as a one-frame pause to allow the display to refresh before the game loop starts, rather than for actual user input.
Content
Source Code
999 GO TO 5800
1000 POKE 23658,0: LET ro=1: LET bon=0: LET sc=0: LET li=3
1010 CLS : LET l=USR 65312: LET fi=0: LET qx=16: LET qy=1: LET bx=4: LET by=19: LET sx=28: LET sy=19: LET qx1=qx: LET qy1=qy: LET bx1=bx: LET by1=by: LET sx1=sx: LET sy1=sy
1015 POKE 23560,0: PAPER 1: PRINT AT 1,7-LEN STR$ sc; INK 6;sc;AT 4,30-LEN STR$ ro; INK 6;ro;AT 1,32-li*2; INK 6;"\a\b\a\b\a\b"( TO li*2);AT 2,32-li*2; INK 6;"\c\d\c\d\c\d"( TO li*2): PAPER 0: IF bon THEN GO TO 1035
1020 RESTORE 1030: FOR q=1 TO 16: READ a: BEEP .095+li/100,a: NEXT q: LET bon=0
1030 DATA 20,16,12,10,20,16,12,10,20,16,19,23,19,16,12,10
1035 RANDOMIZE : PRINT OVER 1; INK 5;AT 18,4;e$;AT 19,4;f$; INK 6;AT 18,28;h$;AT 19,28;i$
1037 INPUT ""
1040 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$; INK 5;AT by-1,bx;e$;AT by,bx;f$; INK 6;AT sy-1,sx;h$;AT sy,sx;i$
1050 GO SUB PEEK 23560*5+1500: POKE 23560,0
1060 PRINT OVER 1;AT qy-1,qx;c$;AT qy,qx;d$
1070 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$; INK 5;AT by-1,bx;e$;AT by,bx;f$; INK 6;AT sy-1,sx;h$;AT sy,sx;i$
1075 LET j= STICK (1,1): POKE 23560,(113*(j=5)+119*(j=9)+97*(j=6)+115*(j=10))
1080 GO SUB 2800
1090 PRINT OVER 1;AT qy-1,qx;c$;AT qy,qx;d$
1100 GO TO 1040
1500 RETURN
1984 RETURN
1985 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$: LET qx=qx-2: LET qy=qy+3: LET l=USR 65200: IF SCREEN$ (qy+1,qx)=" " OR qy>=21 THEN GO TO 3000
1986 IF SCREEN$ (qy-3,qx+2)="" THEN PRINT OVER 1;AT qy-4,qx+2;a$;AT qy-3,qx+2;b$: GO TO 1986
1987 IF qy=by AND qx=bx OR qy=sy AND qx=sx THEN LET qx1=qx: LET qy1=qy: LET qx=qx+2: LET qy=qy-3: GO TO 4000
1988 LET qx1=qx: LET qy1=qy: IF SCREEN$ (qy,qx)="o" THEN GO SUB 3500
1989 RETURN
2064 RETURN
2065 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$: LET qx=qx-2: LET qy=qy-3: LET l=USR 65200: IF SCREEN$ (qy+1,qx)=" " OR qy<=0 THEN GO TO 3000
2066 IF SCREEN$ (qy+3,qx+2)="" THEN PRINT OVER 1;AT qy+2,qx+2;a$;AT qy+3,qx+2;b$: GO TO 2066
2067 IF qy=by AND qx=bx OR qy=sy AND qx=sx THEN LET qx1=qx: LET qy1=qy: LET qx=qx+2: LET qy=qy+3: GO TO 4000
2068 LET qx1=qx: LET qy1=qy: IF SCREEN$ (qy,qx)="o" THEN GO SUB 3500
2069 RETURN
2074 RETURN
2075 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$: LET qx=qx+2: LET qy=qy+3: LET l=USR 65200: IF SCREEN$ (qy+1,qx)=" " OR qy>=21 THEN GO TO 3000
2076 IF SCREEN$ (qy-3,qx-2)="" THEN PRINT OVER 1;AT qy-4,qx-2;a$;AT qy-3,qx-2;b$: GO TO 2076
2077 IF qy=by AND qx=bx OR qy=sy AND qx=sx THEN LET qx1=qx: LET qy1=qy: LET qx=qx-2: LET qy=qy-3: GO TO 4000
2078 LET qx1=qx: LET qy1=qy: IF SCREEN$ (qy,qx)="o" THEN GO SUB 3500
2079 RETURN
2094 RETURN
2095 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$: LET qx=qx+2: LET qy=qy-3: LET l=USR 65200: IF SCREEN$ (qy+1,qx)=" " OR qy<=0 THEN GO TO 3000
2096 IF SCREEN$ (qy+3,qx-2)="" THEN PRINT OVER 1;AT qy+2,qx-2;a$;AT qy+3,qx-2;b$: GO TO 2096
2097 IF qy=by AND qx=bx OR qy=sy AND qx=sx THEN LET qx1=qx: LET qy1=qy: LET qx=qx-2: LET qy=qy+3: GO TO 4000
2098 LET qx1=qx: LET qy1=qy: IF SCREEN$ (qy,qx)="o" THEN GO SUB 3500
2099 RETURN
2798 RETURN
2800 LET bx1=bx: LET by1=by: GO TO RND*4+2801
2801 LET bx=bx+2: LET by=by+3: GO TO 2810
2802 LET bx=bx+2: LET by=by-3: GO TO 2810
2803 LET bx=bx-2: LET by=by+3: GO TO 2810
2804 LET bx=bx-2: LET by=by-3
2810 IF by<0 OR by>21 THEN LET by=by1: LET bx=bx1: GO TO 2860
2820 IF SCREEN$ (by+1,bx)=" " THEN LET bx=bx1: LET by=by1: GO TO 2860
2830 IF bx=qx AND by=qy THEN PRINT OVER 1; INK 7;AT by1-1,bx1;e$;AT by1,bx1;f$; INK 5;AT by-1,bx;e$;AT by,bx;f$: GO TO 4000
2840 PRINT OVER 1; INK 7;AT by1-1,bx1;e$;AT by1,bx1;f$
2850 PRINT OVER 1; INK 5;AT by-1,bx;e$;AT by,bx;f$
2860 BEEP .01,-by: BEEP .01,-bx
2900 LET sx1=sx: LET sy1=sy: GO TO RND*4+2901
2901 LET sx=sx-2: LET sy=sy+3: GO TO 2910
2902 LET sx=sx+2: LET sy=sy-3: GO TO 2910
2903 LET sx=sx+2: LET sy=sy+3: GO TO 2910
2904 LET sx=sx-2: LET sy=sy-3
2910 IF sy<0 OR sy>21 THEN LET sy=sy1: LET sx=sx1: GO TO 2980
2920 IF SCREEN$ (sy+1,sx)=" " THEN LET sx=sx1: LET sy=sy1: GO TO 2980
2930 IF sx=qx AND sy=qy THEN PRINT OVER 1; INK 7;AT sy1-1,sx1;h$;AT sy1,sx1;i$; INK 6;AT sy-1,sx;h$;AT sy,sx;i$: GO TO 4000
2940 PRINT OVER 1; INK 7;AT sy1-1,sx1;h$;AT sy1,sx1;i$
2950 PRINT OVER 1; INK 6;AT sy-1,sx;h$;AT sy,sx;i$
2960 IF RND<.9-ro/200 THEN BEEP .01,-sy: BEEP .01,-sx: RETURN
2970 IF SCREEN$ (sy1,sx1)<>"o" AND sx1<>sx AND sy1<>sy AND sx1<>bx AND sy1<>by THEN PRINT AT sy1,sx1;"o": BEEP .01,0: BEEP .01,10: LET fi=fi-50
2980 BEEP .01,-sy: BEEP .01,-sx: RETURN
3000 LET l=USR 65330: BEEP .3,10: BEEP .3,0: BEEP .3,-10: FOR q=1 TO 8: LET l=USR 65230: NEXT q: FOR q=1 TO 5: LET l=USR 65250: LET l=USR 65275: NEXT q
3005 PRINT OVER 1; INK 6;AT 1,32-li*2;"\e\f\e\f\e\f"( TO li*2);AT 2,32-li*2;"\g\h\g\h\g\h"( TO li*2)
3010 PRINT OVER 1;AT qy1-1,qx1;a$;AT qy1,qx1;b$: IF SCREEN$ (qy1,qx1)="" THEN GO TO 3010
3020 FOR q=20 TO -20 STEP -1: BEEP .02,q: BEEP .01,q: NEXT q: FOR q=-20 TO 0: BEEP .01,q: NEXT q
3030 LET li=li-1: IF li<=0 THEN GO TO 5000
3040 LET bon=0: GO TO 1010
3500 LET sc=sc+50: BEEP .01,20: BEEP .01,10: PRINT AT qy,qx;" ": PRINT AT 1,7-LEN STR$ sc; PAPER 1; INK 6;sc: LET fi=fi+50: IF fi<1350 THEN RETURN
3510 PRINT OVER 1;AT qy1-1,qx1;a$;AT qy1,qx1;b$: IF SCREEN$ (qy1,qx1)="" THEN GO TO 3510
3515 PRINT OVER 1;AT qy-1,qx;a$;AT qy,qx;b$
3520 FOR q=1 TO 500 STEP 10: LET sc=sc+10: BEEP .01,0: PRINT AT 1,7-LEN STR$ sc; PAPER 1; INK 6;sc: NEXT q
3530 FOR q=-20 TO 50 STEP 5: BEEP .1,q: NEXT q
3540 LET bon=1: LET ro=ro+(1 AND ro<120): GO TO 1010
4000 LET l=USR 65330: BEEP .3,10: BEEP .3,0: BEEP .3,-10
4005 PRINT OVER 1; INK 6;AT 1,32-li*2;"\e\f\e\f\e\f"( TO li*2);AT 2,32-li*2;"\g\h\g\h\g\h"( TO li*2)
4010 PRINT OVER 1;AT qy1-1,qx1;a$;AT qy1,qx1;b$
4020 IF qx1<26 THEN PRINT AT qy1-1,qx1+2; FLASH 1; BRIGHT 1; PAPER RND*3; INK RND*4+4;"*%*''": GO TO 4040
4030 PRINT AT qy1-1,qx1+2; FLASH 1; BRIGHT 1; PAPER RND*3; INK RND*4+4;"*%";AT qy1,qx1+2;"!'"
4040 FOR q=1 TO 8: LET l=USR 65230: NEXT q: FOR q=1 TO 5: LET l=USR 65250: LET l=USR 65275: NEXT q
4050 FOR q=20 TO -20 STEP -1: BEEP .02,q: BEEP .01,q: NEXT q: FOR q=-20 TO 0: BEEP .01,q: NEXT q
4060 LET li=li-1: IF li<=0 THEN GO TO 5000
4070 LET bon=0: GO TO 1010
5000 LET x$="": PRINT AT 1,30; PAPER 1; INK 6;" ";AT 2,30;" "
5010 DATA a,0,a,2,a/2,3,a/2,2,a,0,a,0,a,2,a/2,3,a/2,2,a,0,a,3,a,5,a*2,7,a,3,a,5,a*2,7,.075,7,.025,8,a/2,7,a/2,5,a/2,3,a/2,2,a,0,.075,7,.025,8,a/2,7,a/2,5,a/2,3,a/2,2,a,0,a,0,a,-5,a*1.5,0,a,0,a,-5,a*1.5,0
5020 LET x$=x$+"000000"( TO 6-LEN STR$ sc)+STR$ sc: LET q=1
5050 IF q=11 OR sc=0 THEN GO TO 5240
5060 IF x$<w$(q, TO 6) THEN LET q=q+1: GO TO 5050
5070 FOR i=10 TO q STEP -1: LET w$(i+1)=w$(i): NEXT i
5100 LET w$(q)=x$+" ": LET a=.135+q/500: RESTORE 5010: FOR f=1 TO 36: READ b,c: BEEP b,c: NEXT f
5110 CLS : PRINT AT 7,1; INK 4;"Your score is one of the";AT 9,1;"highest today. Enter your name!";AT 14,4; INK 6-INT ((q-1)/2);x$; INK 7;" _________________"
5120 LET f=11
5130 LET q$=INKEY$: IF q$="" THEN GO TO 5130
5150 IF CODE q$=13 THEN GO TO 5240
5160 IF CODE q$=12 AND f>11 THEN LET w$(q,f-4)=" ": LET f=f-1: PRINT AT 14,f;" ";: IF INKEY$<>"" THEN GO TO 5220
5170 IF q$<" " OR q$>"z" THEN GO TO 5130
5180 PRINT AT 14,f;q$: LET w$(q,f-3)=q$: BEEP .01,20: LET f=f+1: IF f>=28 THEN LET f=11
5220 IF INKEY$<>"" THEN GO TO 5220
5230 GO TO 5130
5240 CLS : PRINT AT 11,9; FLASH 1; PAPER RND*3; INK RND*4+4;" GAME OVER ": BEEP 1,-25: BEEP 1,-30
5500 GO SUB 5780: PRINT AT 6,7; BRIGHT 1;" The persons: "
5510 PRINT AT 9,6;a$;AT 10,6;b$;"......the Hero"
5520 PRINT INK 5;AT 12,6;e$;AT 13,6;f$; INK 7;".........Sammy"
5530 PRINT INK 6;AT 15,6;h$;AT 16,6;i$; INK 7;".....the Snake"
5540 PAUSE 100: PRINT OVER 1;AT 9,6;c$;AT 10,6;d$;AT 12,6; INK 5;g$: GO SUB 5790: PAUSE 100
5545 IF INKEY$<>"" THEN GO TO 1000
5550 GO SUB 5780: PRINT BRIGHT 1;AT 7,7;" To move press: "
5560 PRINT AT 10,12;"Q W";AT 11,13;"\ /";AT 12,14;"x";AT 13,13;"/ \";AT 14,12;"A S"
5570 PRINT AT 17,6; BRIGHT 1;" Or use joysticks ": GO SUB 5790: PAUSE 200
5580 IF INKEY$<>"" THEN GO TO 1000
5620 GO SUB 5780: PRINT AT 7,7; BRIGHT 1;" Scoring: "
5630 PRINT AT 11,7; INVERSE 1;" 50"; INVERSE 0;" Points for each egg";AT 16,11;"for every new game";AT 14,7; INVERSE 1;"500"; INVERSE 0;" Points bonus": GO SUB 5790: PAUSE 200
5640 IF INKEY$<>"" THEN GO TO 1000
5660 CLS : PRINT TAB 3; BRIGHT 1; INK RND*4+4; PAPER RND*3;" The 10 leaders: ": PRINT
5680 FOR q=1 TO 10: PRINT 'TAB 3; INK 6-INT ((q-1)/2);w$(q, TO 6); INK 5;w$(q,7 TO ): NEXT q: GO SUB 5790: PAUSE 200
5720 IF INKEY$="" THEN GO TO 5500
5740 IF INKEY$<>"" THEN GO TO 1000
5750 GO TO 1000
5780 RANDOMIZE : CLS : PRINT AT 3,10; BRIGHT 1; INK RND*4+4; PAPER RND*3;" Q-BERT ": RETURN
5790 PRINT #1;" Press any key to start Q-BERT ": RETURN
5800 BORDER 0: PAPER 0: INK 7: CLS
5810 LET w$="Q-BERT"
5820 PRINT AT 3,8; FLASH 1; BRIGHT 1; PAPER RND*3; INK RND*4+4;" HOMECOMPUTER ";AT 5,10; PAPER RND*3; INK RND*4+4;" presents ";AT 18,3; INK 1; PAPER 6; FLASH 0;" adapted by Werner Kaelin "
5830 FOR q=1 TO 6: PRINT AT 8+q,11+q; FLASH 1; BRIGHT 1; INK RND*6+2;w$(q): NEXT q: POKE 23658,8
5840 GO SUB 5790: PAUSE 0
6710 LET l=USR 65312
6720 DATA 0,6,9,15,9,6,0
6730 FOR i=40 TO -20 STEP -3: RESTORE 6720: FOR q=1 TO 4: READ a: BEEP .04,a+i: NEXT q: NEXT i: BEEP 1,i+1: BEEP 1,i: BEEP 2,i+5
6750 DIM w$(12,24): FOR q=1 TO 12 STEP 3
6760 LET w$(q)="000000 Q-BERT___________"
6770 LET w$(q+1)="000000 SAMMY____________"
6780 LET w$(q+2)="000000 THE SNAKE________"
6790 NEXT q
6800 LET a$="\a\b": LET b$="\c\d": LET c$="\e\f": LET d$="\g\h"
6810 LET e$="\i\j": LET f$="\k\l": LET g$="\m"
6820 LET h$="\n\o": LET i$="\p\q"
6890 GO TO 5500
9000 STOP
9900 CLEAR : SAVE "Q-BERT" LINE 9940
9910 SAVE "Q-BERT"CODE 58280,7225
9930 STOP
9940 CLEAR 58200: LOAD "Q-BERT"CODE
9970 GO TO 5800
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

