--- title: "QBERT" id: 71307 type: "computer_media" slug: "qbert-3" url: "http://localhost/computer_media/qbert-3/" markdown_url: "http://localhost/computer_media/qbert-3.md" published_at: "2026-09-02T07:06:31+00:00" modified_at: "2026-09-02T07:06:31+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/09/qbert.png" excerpt: "A BASIC Q*bert clone with joystick control, procedurally drawn isometric pyramid, UDG sprites, and ATTR-based collision detection across escalating difficulty sheets." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" basic: - name: "User Defined Graphics (UDG)" slug: "user-defined-graphics-udg" taxonomy: "basic" url: "http://localhost/basic/user-defined-graphics-udg/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/QBERT%20(198x)(-)(TS2068)(US)(Program).zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/09/qbert.png" media_type_tags: "Arcade" --- # QBERT QBERT is a BASIC implementation of the arcade game Q*bert, in which the player navigates a character across an isometric pyramid of blocks, changing each block’s color by landing on it while avoiding a bouncing ball and a roaming snake. The game uses 16 user-defined graphics (UDGs “a” through “p”) loaded from DATA statements at line 9900 onward, which are POKEd into UDG memory via a loop at line 9990. Movement is handled via STICK for joystick input, reading diagonal directions. The pyramid is drawn procedurally using nested print loops and DRAW commands, and ATTR is used extensively to detect block state and collision by reading screen attribute values. Later sheets require landing on each block twice to color it, and a disc mechanic sends the player back to the pyramid top; a bonus life is awarded every sixth sheet completed. *** ### Program Structure The program is organized into several functional regions: - **Lines 1–9:** Entry point redirects to line 9900 for UDG setup; line 2 is the real post-setup target. The title screen is displayed at line 4, and instructions prompt at line 5. - **Lines 10–61:** Game initialization and pyramid drawing. The pyramid is constructed iteratively using `GO SUB 40`, which prints a block tile at position (`x`, `y`) using UDGs. - **Lines 100–390:** Main game loop. Reads joystick, moves Q*bert, checks ATTR for block state and collisions, updates score, moves the ball and snake. - **Lines 400–440:** Death sequence, life decrement, game-over handling, and high-score update. - **Lines 1000–1010:** Sheet completion: increments sheet, awards bonus life every sixth sheet, adds 100 bonus points. - **Lines 2000–2030:** Falling-off-pyramid sequence with animated descent toward a disc, then respawn. - **Lines 3000–3060:** Instructions screen. - **Lines 9900–9999:** DATA blocks for all 16 UDGs and the loader loop that POKEs them into memory. ### UDG Loading Lines 9900–9912 and 9915–9916 contain the raw 8-byte UDG definitions for characters `"a"` through `"p"`. Line 9990 performs the load: `FOR f = USR "a" TO USR "p"+7`, reading each byte sequentially from `RESTORE 9900`. This efficiently covers all 16 UDGs (128 bytes) in a single loop. The UDGs represent the isometric block faces, Q*bert body parts, the ball, the snake, and disc sprites. ### Pyramid Construction The pyramid is built in lines 50–61. Starting with a row of 7 blocks (`T=7`), `GO SUB 40` is called for each block, printing block-face UDGs at the current (`x`, `y`) position. After each row, `T` is decremented and the starting offsets `l`, `m` shift to produce the isometric staggered layout. Line 61 draws the two side “discs” using `PLOT` and `DRAW OVER 0` lines to form a chevron shape on either side of the pyramid base. ### ATTR-Based Collision and State Detection The game makes heavy use of `ATTR` to read screen cell attributes for multiple purposes: - **Block color state:** An unvisited block has INK 6 (ATTR 7 = paper 0, ink 7 mapped differently — actually ATTR 71 = bright+ink 6+paper 0 for colored). `ATTR (a+2,b)=71` detects an already-colored block; `=68` detects a disc cell. - **Fall detection:**`ATTR (a+2,b)=7` at line 170 detects an empty cell (off the pyramid edge), triggering the fall sequence at line 2000. - **Enemy collision:** Line 340 checks `ATTR (A,B)=114 OR 115` to detect the snake occupying Q*bert’s cell. - **Snake pathfinding avoidance:** Lines 280–310 check ATTR before moving the snake to avoid landing on already-colored blocks (ATTR 71). ### Two-Color Sheet Mechanic Lines 173–174 implement the two-step coloring mechanic for sheets beyond the first. On sheet 2+, a block already colored INK 4 (yellow) must be re-landed to change it to INK 5 (cyan). `ATTR` value 70 (ink 6, paper 0, bright = ink 4 equivalent after color change) and 69 are tested to select the target ink `ik`. The completion counter `fi` increments only when `ik=4` (the final color is reached), so each block must be visited the correct number of times. ### Enemy Movement Two enemies exist: a bouncing ball (variables `b1`, `b2`) and a snake (`s1`, `s2`). - The **ball** (lines 220–240) moves down the pyramid one row each game tick, randomly going left or right. On sheet 2+, it has a 50% chance of jumping back up six rows. It resets to the top when it reaches row 19. - The **snake** (lines 260–330) picks a random direction (1–4) and moves diagonally, but only if the target cell’s ATTR is not 71 (avoiding already-colored blocks). If `sh<1`, the snake is disabled by resetting `s1=1`. ### Death and Respawn Collision with enemies (lines 191, 320, 340, 400) triggers the death sequence at line 400: Q*bert flashes an exclamation graphic with `FLASH 1`, plays a short tone sequence, then decrements lives. If lives remain, `CLS` and `GO TO 20` restart the level. The fall-off sequence (lines 2000–2030) animates Q*bert sliding diagonally downward off the pyramid edge, with `BEEP .01,a` producing a descending pitch, before respawning at position (1, 16). ### Score and Display Score `sc` is maintained as a plain integer and printed right-justified using `LEN STR$ sc` to compute the print column offset (line 30, 180). The same technique is used for the high score `hi`. The score increments by 1 per newly colored block and by 100 for completing a sheet. High score is updated at line 435 only when the game-over condition is reached. ### Notable Idioms and Techniques - `STICK (1,1)` reads the joystick for directional input; `STICK (2,1)` reads the fire button. Values 5, 6, 9, 10 correspond to the four diagonal directions Q*bert can move. - `POKE 23658,8` at line 5 enables caps lock for the `INPUT LINE a$` prompt, ensuring consistent input handling. - `OVER 1` is used when drawing Q*bert’s body shadow/mask elements (line 180) to XOR-blend with the block graphics beneath. - `PAPER 8` (transparent paper) is used throughout enemy and player sprite printing to avoid overwriting background block colors. - The bonus life condition `SH/6 = INT SH/6` at line 1000 is a modulo-6 check without using MOD. - `LET fi=fi+(1 AND ik=4)` at line 180 uses a Boolean expression as an integer increment — a compact BASIC idiom. ### Bugs and Anomalies - Line 2025 resets the score display to zeros before line 2026 reprints the actual values — this causes a brief flicker of zeroed display on respawn. - Line 321 checks `IF sh<1 THEN LET s1=1`, but `sh` starts at 1 and only increases, so this condition is never true. The snake is always active from the start. - The `STOP` at line 2050 is unreachable dead code. - Lines 4000–4020 (`SAVE`, `VERIFY`, `RUN`) are utility lines for saving the program and are not part of normal execution flow. - The instructions text at line 3035 misspells “DIAGIONAL” (should be “DIAGONAL”). ## Source Code ``` 1 GO TO 9900 2 CLS 4 PRINT AT 7,12; INK 6;"\{20}\{1}\{20}\{0}\{20}\{1}\ .\{20}\{0}\''\''\''\''\''\''\':"; AT 8,12;"\{20}\{1}\ :\{20}\{0}"; INK 2;"Q*BERT"; INK 6;"\ :"; AT 9,12;"\:.\..\..\..\..\..\..\.:" 5 POKE 23658,8:LET hi=0:PRINT AT 11,2;"Would you like instructions?":INPUT LINE a$:IF a$="Y" THEN GO SUB 3000 6 RANDOMIZE 10 OVER 1:BORDER 0:PAPER 0:INK 7:BRIGHT 1:CLS 15 LET sc=0:LET liv=3:LET sh=1 20 PRINT AT 0,0;"SC:000000"; AT 0,20;"HI:000000"; AT 1,0;"SHEET:1"; AT 1,20;"LIVES:3" 22 PRINT AT 3,0; INK 4;"\a\b"; AT 4,0;"\b\a" 23 IF SH>1 THEN PRINT AT 3,0; OVER 0; INK 5;"\a\b"; AT 4,0;"\b\a"; AT 5,0; INK 4;"\a\b"; AT 6,0;"\b\a" 30 OVER 1:PRINT AT 0,29- LEN STR$ hi; OVER 0;hi; AT 0,9; LEN STR$ sc; OVER 0;sc; AT 1,26;liv; AT 1,0;"SHEET:";sh 35 LET x=-2:LET y=15:LET c=1:LET l=-2:LET m=15:GO TO 50 40 INK 6:PRINT AT x+2,y+1;"\a\b"; AT x+3,y;"\a"; PAPER 6;" "; PAPER 0;"\b"; AT x+4,y;"\b"; PAPER 6;" "; PAPER 0;"\a"; AT x+4,y;"\c"; AT x+4,y+3;"\d"; AT x+5,y+1;"\b\a"; AT x+6,y+1;"\d":INK 7:RETURN 50 LET T=7:LET C=1 55 GO SUB 40:LET X=X+3:LET Y=Y-2:LET C=C+1:IF C1 THEN GO TO 55 61 INK 6:PLOT 40,32:FOR f=1 TO 6:DRAW OVER 0;16,-16:DRAW OVER 0;16,16:NEXT f:INK 7 70 PRINT AT 8,8; BRIGHT 0;"\i\j"; AT 9,8;"\k\l"; AT 8,24;"\i\j"; AT 9,24;"\k\l" 100 OVER 0:LET a=7:LET b=12:LET fi=0 101 LET b1=1:LET b2=16 102 LET s1=7:LET s2=20 120 PRINT AT a,b; PAPER 8; INK 8;" "; AT a+1,b;" " 130 LET q=STICK (1,1):IF q <>5 AND q <>6 AND q <>9 AND q <>10 THEN GO TO 191 140 IF q=10 THEN LET a=a+3:LET b=b+2 150 IF q=6 THEN LET a=a+3:LET b=b-2 160 IF q=5 THEN LET a=a-3:LET b=b-2 165 IF q=9 THEN LET a=a-3:LET b=b+2 170 IF ATTR (a+2,b)=7 THEN GO TO 2000 171 IF ATTR (a+2,b)=71 THEN OVER 1:FOR f=a TO 20 STEP 3:FOR g=1 TO 2:PRINT AT f,b; PAPER 8; INK 8;"\e\f"; AT f+1,b; PAPER 8; INK 8;"\g\h":NEXT g:BEEP .001,20-f:NEXT f:OVER 0:GO TO 410 172 IF ATTR (a+2,b)=68 THEN GO TO 181 173 LET ik=4:IF sh>1 AND ATTR (a+2,b)=70 THEN LET ik=5 174 IF sh>1 AND ATTR (a+2,b)=69 THEN LET ik=4 180 IF ATTR (a+2,b) <>71 THEN PRINT AT a-1,b; INK ik;"\a\b"; AT a,b-1;"\a"; PAPER ik;" "; PAPER 0; OVER 1;" "; OVER 0; AT a+1,b-1;"\b"; PAPER ik;" "; PAPER 0;"\a"; AT a+2,b;"\b\a":OVER 1:PRINT AT a+1,b-1; INK 8; PAPER 8;"\c \d":OVER 0:LET fi=fi+(1 AND ik=4):LET sc=sc+1:PRINT AT 0,9- LEN STR$ sc; OVER 0;sc 185 IF fi=21 THEN GO TO 1000 190 BEEP .01,-20 191 IF a=s1 AND b=s2 OR a=b1 AND b=b2 THEN GO TO 400 200 PRINT AT a,b; PAPER 8; INK 1;"\e\f"; AT a+1,b;"\g\h" 220 PRINT AT b1,b2; PAPER 8;" "; AT b1+1,b2;" ":LET b1=b1+3:LET d= INT (RND*2)+1:LET b2=b2+2*(d=1)-2*(d=2):IF sh>1 AND RND>.5 AND b1>4 AND ATTR (b1-6,b2) <>71 THEN LET b1=b1-6 231 IF b1=19 THEN LET c=0:LET b1=1:LET b2=16 240 PRINT AT b1,b2; INK 2; PAPER 8;"\i\j"; AT b1+1,b2;"\k\l" 260 LET d= INT (RND*4)+1 270 PRINT AT s1,s2; PAPER 8;" "; AT s1+1,s2;" " 280 IF d=1 AND ATTR (s1-3,s2-2) <>71 AND s1>3 THEN LET s1=s1-3:LET s2=s2-2 290 IF d=2 AND ATTR (s1-3,s2+2) <>71 AND s1>2 THEN LET s1=s1-3:LET s2=s2+2 300 IF d=3 AND ATTR (s1+3,s2+2) <>71 THEN LET s1=s1+3:LET s2=s2+2 310 IF d=4 AND ATTR (s1+3,s2-2) <>71 THEN LET s1=s1+3:LET s2=s2-2 320 IF s1=a AND s2=b THEN GO TO 400 321 IF sh<1 THEN LET s1=1 330 PRINT AT s1,s2; PAPER 8; INK 3;"\m\n"; AT s1+1,s2;"\o\p" 340 IF ATTR (A,B)=114 OR ATTR (A,B)=115 THEN GO TO 400 390 GO TO 120 400 PRINT AT a,b; INK 1; PAPER 8;"\e\f"; AT a+1,b;"\g\h"; AT a-1,b+1; FLASH 1; INK 6;"!#*@":FOR f=1 TO 15:BEEP .01,f:BEEP .01,7:BEEP .01,15-f:NEXT f 410 LET liv=liv-1:PRINT AT 1,20; OVER 0;"LIVES:";liv:FOR f=1 TO 150:NEXT f:IF liv>0 THEN CLS :GO TO 20 420 FOR f=1 TO 50:BEEP .01, RND*50-20:NEXT f:PRINT AT 21,0;"PRESS FIRE BUTTON TO PLAY AGAIN" 430 IF STICK (2,1) <>1 THEN GO TO 430 435 IF sc>hi THEN LET hi=sc 440 GO TO 6 1000 LET SH=SH+1:IF SH/6=(INT SH/6) THEN LET LIV=LIV+1 1010 LET SC=SC+100:FOR F=1 TO 40:BEEP .01,F:NEXT F:CLS :GO TO 20 2000 PRINT AT a,b;" "; AT a+1,b;" "; AT a+2,b;" ":LET b=b-4*(b<16)+4*(b>16) 2005 LET a=a-1:LET b=b+(b<16)-(b>16):BEEP .01,a:PRINT AT a,b-1; INK 1;" \e\f "; AT a+1,b-1;" \g\h "; AT a+2,b-1; INK 7;" \k\l "; AT a+3,b-1;" " 2010 IF a>0 THEN GO TO 2005 2015 PRINT AT a,b;" "; AT a+1,b;" "; AT a+2,b;" ":LET a=1:LET b=16 2025 PRINT AT 0,0;"SC:000000"; AT 0,20;"HI:000000"; AT 1,0;"SHEET:1"; AT 1,20;"LIVES:3" 2026 PRINT AT 0,29- LEN STR$ hi; OVER 0;hi; AT 0,9- LEN STR$ sc; OVER 0;sc; AT 1,28;liv; AT 1,0;"SHEET:";sh 2030 GO TO 200 2050 STOP 3000 CLS :PRINT AT 0,13;"Q*BERT"; AT 1,13; INK 3;"\''\''\''\''\''\''" 3030 PRINT ''" Jump on the blocks to change their color. Avoid the snake and bouncing ball. On later screens, jump on each block twice. Jumping on a disc will fly you to the top of the pyramid. On the third screen, the ball will bounce upwards." 3035 PRINT :PRINT " THIS VERSION USES THE LEFT-SIDE JOYSTICK, AND Q*BERT MOVES IN DIAGIONAL DIRECTIONS." 3040 PRINT INK 2; AT 19,3;"PRESS FIRE BUTTON TO START" 3050 IF STICK (2,1) <>1 THEN BEEP .01, RND*50-20:GO TO 3050 3060 CLS :RETURN 4000 SAVE "Q*BERT" LINE 0 4010 VERIFY "Q*BERT" 4020 RUN 8999 STOP 9900 DATA 1,3,7,15,31,63,127,255,128,192,224,240,248,252,254,255 9901 DATA 128,128,128,128,128,128,128,128 9902 DATA 1,1,1,1,1,1,1,1 9903 DATA 7,15,27,17,49,49,59,62,192,224,176,24,24,24,184,248,60,24,25,15,7,4,4,29,112,240,224,192,192,64,64,192 9907 DATA 0,7,31,63,63,127,127,127,0,224,248,252,252,254,254,254 9908 DATA 127,127,127,63,63,31,7,0,254,254,254,252,252,248,224,0 9909 DATA 0,0,60,78,126,60,12,6 9910 DATA 0,0,0,0,24,102,102,198 9911 DATA 6,6,1,0,0,0,0,0 9912 DATA 195,195,195,0,0,0,0,0 9915 DATA 1,3,7,15,15,7,3,1 9916 DATA 128,192,224,240,240,224,192,128 9990 PRINT AT 10,10; FLASH 1;"Please Wait..":RESTORE 9900:FOR f= USR "a" TO USR "p"+7:READ a:POKE f,a:NEXT f 9999 GO TO 2 ```