CHR TEST is an educational program that demonstrates the CHR$ function in two modes: a quiz game and a character viewer. In the quiz mode, a random printable character is displayed and the player must type its ASCII code before a countdown timer expires, with a fuse-and-bomb graphic drawn using PLOT, DRAW, and CIRCLE. The character viewer mode (lines 270–400) iterates through characters 32–127, reading each character’s 8-byte font definition from the ROM character set via PEEK and displaying both the byte values and a graphical binary representation using UDG characters. The font base address is computed at runtime using PEEK 23606 and PEEK 23607 to locate the system variable CHARS, making the viewer portable across different memory configurations.
Program Structure
The program is organized into three distinct sections controlled by a menu at line 420:
- Menu (lines 420–460): Presents two options and routes to either the quiz or the character viewer.
- Quiz game (lines 20–260): Picks a random character, draws a bomb-and-fuse graphic, and accepts a numeric answer before the fuse burns down.
- Character viewer (lines 270–410): Iterates through all 96 printable characters, decoding and displaying their 8×8 font bitmaps.
Line 10 is RUN 420, jumping directly to the menu on startup. Line 470 saves the program with LINE 10 as the auto-run line.
Quiz Game Logic
A random character is chosen at line 80 using INT (RND*223)+33, giving characters in the range CHR$ 33–255 (excluding space). The player types digits which are accumulated in A$ and evaluated with VAL A$ into A (line 160). Entry is confirmed with ENTER (CHR$ 13, line 140), and non-digit keys are ignored (line 150).
The countdown mechanism uses variable T, initialized to 10. Each pass through the input loop calls the subroutine at line 190, which erases one segment of the fuse using DRAW OVER 1;-1,-1 and decrements T. When T reaches zero, NOT T becomes true (line 130) and the “explosion” sequence at line 220 fires, cycling PAPER and BORDER colors with BEEP sounds.
Bomb Graphic
The bomb is assembled from several primitives:
PRINT AT 15,5;"BOMB"— text labelCIRCLE 56,53,39— the bomb bodyPLOT 56,91:DRAW 0,5:DRAW T,T— the fuse, drawn diagonallyPLOT OVER 1;56+T,96+T— marks the tip of the fuse for erasing
The subroutine at line 190 uses DRAW OVER 1;-1,-1 to XOR-erase one pixel-step of the fuse each iteration, giving a visual countdown effect.
Character Viewer and Font Decoding
The viewer (lines 270–380) reads the ROM font data directly. Line 280 computes the base address of the character set:
LET c= VAL "PEEK 23606+256* PEEK 23607+256"
System variables at addresses 23606–23607 hold CHARS, which points 256 bytes before the font data. The +256 offset corrects for this, giving the address of CHR$ 0’s bitmap. For each character j (0–95, representing CHR$ 32–127), it reads 8 bytes with PEEK (c+p+j*8).
Each byte is then decomposed bit by bit in the inner loop (lines 330–360) using repeated halving: INT (byte/2) shifts right, and byte-2*x extracts the LSB. Each bit selects between two UDG characters (CHR$ 144 or CHR$ 143) to display a filled or empty pixel block.
UDG Definition
Lines 270 and 390–410 set up a UDG. Line 390 POKEs 23606–23607 to address 63744 (249×256), relocating the CHARS pointer, and the DATA at line 410 defines UDG “a” as a bordered rectangle pattern (255, 129, repeated middle rows, 255). However, the DATA line reads 255,129,a,a,a,a,a,255 — the middle values are the literal letter a, which BASIC will interpret as the variable a rather than a numeric constant. This is a latent bug: the filled rows of the UDG will depend on whatever value a holds at the time line 410’s DATA is read, rather than a fixed byte value like 129 or 255.
Key BASIC Idioms
VAL "PEEK 23606+256* PEEK 23607+256"— evaluates an expression stored as a string, here used to compute the font base address dynamically.PAUSE 10:LET I$= INKEY$— a short pause followed by INKEY$ creates a polled input loop with a built-in delay per iteration.IF NOT T THEN GO TO 220— uses the fact thatNOT 0is 1 (true) andNOT nfor nonzeronis 0 (false) to test for zero economically.PRINT AT 5,11;"\\{18}\\{1}WELL DONE!!\\{18}\\{0}"— embeds AT-style control codes (CHR$ 18 = OVER, CHR$ 1 = on, CHR$ 0 = off) directly in string literals using character code escapes.GO TO 420at line 260 targets the menu, routing to a non-sequential line rather than using a loop structure.
Notable Anomaly: Unreachable Code
Line 400 is STOP, placed between the DATA statement setup (line 390) and the DATA line (line 410). Lines 390–400 appear to be debugging or setup remnants; in normal execution the viewer is entered at line 270, which calls RESTORE 400 to point the DATA pointer at line 400 before reading — but line 400 is STOP, not a DATA statement. The actual DATA is at line 410. RESTORE 400 sets the DATA pointer to line 400; since that line is not DATA, the subsequent READ at line 270 will find the DATA on line 410 (the next DATA line). This works correctly in practice because RESTORE positions to the given line and the interpreter searches forward for DATA.
Source Code
10 RUN 420
20 REM CHR TEST
30 BORDER 6:PAPER 7:INK 2:CLS
40 PRINT AT 15,5;"BOMB"
50 CIRCLE 56,53,39
60 LET T=10
70 PLOT 56,91:DRAW 0,5:DRAW T,T:PLOT OVER 1;56+T,96+T
80 LET X= INT (RND*223)+33
90 PRINT AT 0,0;"What is the code for:"; CHR$ X
100 LET A$="":LET A=0
110 PAUSE 10:LET I$= INKEY$
120 GO SUB 190
130 IF NOT T THEN GO TO 220
140 IF I$= CHR$ 13 THEN GO TO 170
150 IF I$<"0" OR I$>"9" THEN GO TO 110
160 LET A$=A$+I$:LET A= VAL A$:PRINT AT 2,22;A$:GO TO 110
170 IF A=X THEN GO TO 210
180 PRINT AT 2,22; OVER 1;A$:GO TO 100
190 DRAW OVER 1;-1,-1
200 LET T=T-1:RETURN
210 PRINT AT 5,11;"\{18}\{1}WELL DONE!!\{18}\{0}":GO TO 230
220 FOR K=1 TO 2:FOR J=0 TO 7:PAPER J:BORDER J:CLS :BEEP .01,K*7-J:NEXT J:NEXT K:BORDER 6
230 PRINT "The code for "; CHR$ X;" is: ";X
240 PRINT AT 21,0;"Press ""R"" to Re-run":PAUSE 0
250 IF INKEY$="R" OR INKEY$="r" THEN RUN
260 GO TO 420
270 RESTORE 400:FOR j=0 TO 7:READ a:POKE USR "\a"+j,a:NEXT j
280 LET c= VAL "PEEK 23606+256* PEEK 23607+256"
290 FOR j=0 TO 95:CLS :PRINT AT 4,20;"CHR$ ";j+32;" "; CHR$ (j+32)
300 FOR p=0 TO 7
310 LET byte= PEEK (c+p+j*8)
320 PRINT AT p,15;byte
330 FOR l=1 TO 8
340 LET x= INT (byte/2):LET bit=byte-2*x:LET byte=x
350 PRINT AT p,9-l; CHR$ (144-bit)
360 NEXT l:NEXT p
370 IF j <>95 THEN PRINT AT 15,0;"Press any key for next character":PAUSE 0
380 NEXT j:STOP
390 POKE 23606,0:POKE 23607,249
400 STOP
410 DATA 255,129,a,a,a,a,a,255
420 CLS :PRINT '''' TAB 12;"CHR$ Demo"'''" \{18}\{1} 1 \{18}\{0} for test"''" \{18}\{1} 2 \{18}\{0} for examples"
430 INPUT "Select \{18}\{1}1\{18}\{0}, \{18}\{1}2\{18}\{0}, ";a
440 IF a<1 OR a>2 THEN GO TO 430
450 IF a=1 THEN RUN 20
460 RUN 270
470 SAVE "CHR TEST" LINE 10
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
