This program is a character set browser that displays each of the 96 printable ASCII characters (codes 32–127) along with a visual representation of the 8×8 pixel bitmap that defines each glyph. It reads the ROM character set address from system variables at addresses 23606–23607, then peeks the 8 bytes that define each character’s dot matrix. Each row byte is decomposed bit-by-bit using repeated integer division by 2, and each bit is rendered using a UDG (User Defined Graphic) character — either a filled or empty block depending on whether the bit is 1 or 0. A single UDG is defined at startup via the DATA statement at line 100, which encodes a fully bordered 8×8 square (255, 129, 129, 129, 129, 129, 129, 255). The program pauses between characters waiting for a keypress, then stops after displaying all 96 characters.
Program Analysis
Program Structure
The program is organized into three logical phases:
- Initialization (lines 5–10): Defines a single UDG and locates the ROM character set base address.
- Display loop (lines 15–65): Iterates over all 96 printable characters (ASCII 32–127), displaying each glyph’s bitmap row by row with a keypress pause between entries.
- Data (line 100): Provides the 8 bytes for the UDG used to render “off” pixels (a hollow bordered square).
Character Set Address Lookup
Line 10 reads the system variable pair at addresses 23606 and 23607, which together hold a pointer to the start of the character set data (normally pointing into ROM). The formula PEEK 23606 + 256 * PEEK 23607 + 256 computes the 16-bit address and adds 256. This offset of 256 is necessary because the system variable actually points 256 bytes before the character data for CHR$ 32 (space), so adding 256 aligns o to the actual start of the space character’s bitmap. Each subsequent character occupies 8 consecutive bytes, accessed as o + p + j*8 where j is the character index (0–95) and p is the row (0–7).
Bit Extraction Technique
Lines 35–50 implement a right-to-left bit unpacking loop without using bitwise operators. For each of the 8 bits in a byte, the code computes:
x = INT(byte / 2)— integer quotient (shifts right by one bit)bit = byte - 2*x— the least significant bit (0 or 1)byte = x— advance to next bit
Because bits are extracted LSB-first but printed left-to-right with a decrementing column index (AT p, 9-l), the display ends up correctly oriented — bit 7 (MSB) appears at the leftmost column.
UDG Usage
Only one UDG is defined (character 144, \a), using the 8 bytes 255,129,129,129,129,129,129,255 — an 8×8 square outline. This represents an “off” pixel. An “on” pixel is rendered as CHR$(144 - 1) = CHR$ 143, which is the built-in block graphic character \::=█ (a fully filled 8×8 block). The expression CHR$(144 - bit) thus elegantly selects between the filled block (when bit=1) and the UDG square outline (when bit=0).
Display Layout
For each character, the screen shows:
- The character code and the character itself printed at row 4, column 20 (
AT 4,20) - The decimal byte value for each of the 8 rows, printed at column 15
- The 8×1 pixel row rendered graphically starting at column 2 (columns 2–9), using
AT p, 9-l
Notable Idioms and Anomalies
The PAUSE 0 at line 60 waits indefinitely until any key is pressed — a standard keypress-wait idiom. The condition IF j<>95 skips the pause after the final character (index 95, CHR$ 127), so the program simply halts with STOP at line 65 after the last entry.
Line 9997 contains a redundant STOP that is unreachable under normal execution. Line 9998 is a SAVE statement that saves the program.
Variable Summary
| Variable | Purpose |
|---|---|
o | Base address of character bitmap data in ROM |
j | Character index (0–95, corresponding to CHR$ 32–127) |
p | Row index within the 8×8 character bitmap (0–7) |
byte | Current bitmap row byte, consumed during bit extraction |
l | Bit position counter within a row (1–8) |
x | Temporary integer quotient during bit extraction |
bit | Extracted bit value (0 or 1) |
a | Temporary variable for reading UDG DATA bytes |
Content
Source Code
5 RESTORE : FOR j=0 TO 7: READ a: POKE USR CHR$ 144+j,a: NEXT j
10 LET o=PEEK 23606+256*PEEK 23607+256
15 FOR j=0 TO 95: CLS : PRINT AT 4,20;"CHR$ ";j+32;" ";CHR$ (j+32)
20 FOR p=0 TO 7
25 LET byte=PEEK (o+p+j*8)
30 PRINT AT p,15;byte
35 FOR l=1 TO 8
40 LET x=INT (byte/2): LET bit=byte-2*x: LET byte=x
45 PRINT AT p,9-l;CHR$ (144-bit)
50 NEXT l
55 NEXT p
60 IF j<>95 THEN PRINT AT 15,0;"press any key for next character": PAUSE 0
65 NEXT j: STOP
100 DATA 255,129,129,129,129,129,129,255
9997 STOP
9998 SAVE "CHARACTER" LINE 0
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
