--- title: "CHR TEST" id: 71459 type: "computer_media" slug: "chr-test" url: "http://localhost/computer_media/chr-test/" markdown_url: "http://localhost/computer_media/chr-test.md" published_at: "2026-09-09T09:04:56+00:00" modified_at: "2026-09-09T09:04:57+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/09/chr-test.png" excerpt: "A two-mode CHR$ explorer that quizzes you on ASCII codes against a ticking bomb timer, then lets you inspect every printable character's raw font bytes." 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: "User Defined Graphics" slug: "user-defined-graphics" taxonomy: "genre" url: "http://localhost/type/user-defined-graphics/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/CHR%20TEST%20(198x)(-)(TS2068)(US)(Program).zip" tsrun_member: "CHR TEST (198x)(-)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/09/chr-test.png" media_type_tags: "User Defined Graphics" --- # CHR TEST 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 label - `CIRCLE 56,53,39` — the bomb body - `PLOT 56,91:DRAW 0,5:DRAW T,T` — the fuse, drawn diagonally - `PLOT 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 that `NOT 0` is 1 (true) and `NOT n` for nonzero `n` is 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 420` at 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 ```