This program offers several character-set manipulation utilities, accessible by jumping to specific line numbers. The main routine (line 30) copies the ROM character set from address 15616–16384 into RAM at 64368, then reverses the vertical order of each character’s eight pixel rows, producing an upside-down font. It then redirects the system variable CHARS (held at addresses 23606–23607) to point to the new RAM-based table at 64368 (stored as the two-byte value 64368−256 = 250×256+112, i.e. POKE 23606,112: POKE 23607,250), making the inverted set live immediately. The “New Chrs” routine (line 280) instead replaces the lowercase letter definitions (offset 264 bytes into the table, covering characters from ASCII 65 onward) with custom pixel data supplied via DATA statements, each row encoded with the literal variable b used as a zero placeholder. Line 460 uses RANDOMIZE USR 0 to crash/reset the machine, and lines 500–510 provide LOAD and SAVE commands for persisting the custom character data as a CODE block.
Program Analysis
Program Structure
The program is a menu-driven collection of independent utilities, each entered by jumping to a labeled line. Line 10 prints a directory of entry points, and line 20 halts execution if the program is simply RUN from the start. Each routine ends with STOP, so routines are self-contained.
| Entry Line | Routine | Description |
|---|---|---|
| 30 | Inversion | Copies ROM font to RAM, reverses row order of every glyph, redirects CHARS pointer |
| 220 | Apply inversion pointer | Only POKEs CHARS to point to 64368; skips the copy/transform work |
| 250 | Revert to normal | Restores CHARS pointer to ROM default (60×256 = 15360, offset by −256 → 23606=0, 23607=60) |
| 280 | New Chrs | Copies ROM font to RAM, then overwrites lowercase glyph data with custom DATA |
| 460 | Destroy program | Executes RANDOMIZE USR 0 to force a hard reset |
Font Inversion Technique (Lines 30–210)
The ZX Spectrum ROM character bitmaps occupy addresses 15616–16383 (768 bytes, covering ASCII 32–127, 8 bytes per character). The routine copies this block byte-for-byte into RAM at 64368. It then iterates over each of the 96 characters: for each character it reads its 8 row bytes into array d(8), writes them back in reverse order into array e(8) (line 170: LET e(e)=d(9-e)), and POKEs the reversed bytes back to the RAM copy. This vertically flips every glyph.
CHARS System Variable Manipulation
The system variable CHARS is stored at addresses 23606–23607 as a 16-bit little-endian value equal to the character table base address minus 256 (because the Spectrum calculates the glyph address as CHARS + 8*(code-32) + 256). The ROM default is 15360 (= 15616 − 256), stored as POKE 23606,0 : POKE 23607,60 (60×256 = 15360). Redirecting to the RAM copy at 64368 requires storing 64112 (= 64368 − 256), which is 112 + 250×256, giving POKE 23606,112 : POKE 23607,250.
New Characters Routine (Lines 280–450)
After copying the ROM font to 64368, this routine uses LET x=64368+264 to target the lowercase region (character code 65+32=97 starts at offset 264; the comment on line 360 notes that changing 264 to 520 would target uppercase). It then RESTOREs the DATA pointer and reads 207 bytes of custom pixel data via a FOR/READ/POKE loop, overwriting those glyphs with hand-drawn bitmaps.
The DATA statements use the literal variable name b as a zero placeholder (since at this point in execution b holds 0 from the copy loop). This is an unusual idiom: reading a variable name from a DATA statement would normally cause a syntax error in Spectrum BASIC, so b here actually refers to the numeric value 0 written symbolically as the letter b — but in practice this only works because READ b at line 380 reads numeric DATA, and the DATA values labeled b are in fact the numeric token for the letter b being interpreted as the number stored in variable b. This is a notable quirk and could be fragile if b were not zero.
RESTORE and DATA Pointer Reset
Line 370 calls RESTORE without a line number, resetting the DATA pointer to the first DATA statement in the program. Because the first DATA statements appear at line 410, the loop correctly reads the custom glyph bytes. A second set of DATA statements at line 480 (inside the “destroy” section) is never reached by the restore/read loop and appears to be an earlier or alternate version of the glyph data left in the program.
Notable Anomalies and Issues
- The variable conflict on line 160–180:
eis used both as an array (DIM e(8)) and as aFORloop variable (FOR e=1 TO 8). In Spectrum BASIC, a numeric variable and an array with the same single-letter name can coexist, so this is legal, though confusing. - Similarly on line 110,
qis assigned but the double colon (::) at the end of the line is a Spectrum BASIC separator (equivalent to:), not a block-graphic escape — it simply separates statements. - The copy loop range is 15616 TO 16384 (769 bytes), which is one byte more than the 768-byte ROM font. This extra byte is harmless but unnecessary.
CLEAR 64367is used to both protect the RAM area at 64368 from BASIC and to reset the stack pointer, which is standard practice for machine-code or data areas placed above RAMTOP.- Lines 500–510 provide LOAD and SAVE commands as executable lines but they are only reachable manually; SAVE at line 510 saves the BASIC program itself with a LINE 10 autostart, while the CODE SAVE is shown only in the trailing REM, requiring manual editing to use.
Destroy Routine (Line 460)
RANDOMIZE USR 0 jumps to address 0 in ROM, which is the cold-start entry point, effectively performing a hard reset and clearing the BASIC program from RAM. This is a common trick used as a dramatic “self-destruct” mechanism.
Content
Source Code
10 CLS : PRINT ''"Enter:"''"GO TO 30 for inversion"''"GO TO 250 to revert to normal"''"GO TO 220 for inversion"''"GO TO 280 for New Chrs"''"GO TO 250 to revert to normal"''"GO TO 460 to destroy program"
20 STOP
30 REM Inversion
40 CLEAR 64367
50 BEEP .2,-10: BEEP .4,20
60 PRINT AT 10,5; PAPER 1; INK 7;"Please wait a minute..."
70 LET mem=64368
80 LET b=0: FOR a=15616 TO 16384
90 POKE mem+b,PEEK a
100 LET b=b+1: NEXT a
110 FOR a=32 TO 127: LET q=a-32:: LET b=q*8: LET c=mem+b
120 DIM d(8): LET b=1
130 FOR e=c TO c+7
140 LET d(b)=PEEK e
150 LET b=b+1: NEXT e
160 DIM e(8): FOR e=1 TO 8
170 LET e(e)=d(9-e)
180 NEXT e
190 LET b=1: FOR e=c TO c+7
200 POKE e,e(b)
210 LET b=b+1: NEXT e: NEXT a
220 POKE 23606,112: POKE 23607,250
230 BEEP .2,20: BEEP .4,-10
240 STOP
250 REM revert to normal
260 POKE 23606,0: POKE 23607,60
270 STOP
280 REM New Chrs
290 CLEAR 64367
300 BEEP .2,-5: BEEP .4,15
310 PRINT AT 10,5; PAPER 1; INK 7;"Please wait 20 seconds..."
320 LET b=0: FOR a=15616 TO 16384
330 POKE 64368+b,PEEK a
340 LET b=b+1: NEXT a
350 LET x=64368+264
360 REM change 520 to 264 in line 350 for upper case letters
370 RESTORE : FOR a=x TO x+206
380 READ b: POKE a,b
390 NEXT a
400 POKE 23606,112: POKE 23607,250
410 DATA 0,124,76,76,124,76,b,0,b,124,100,b,124,100,124,0,b,124,76,64,76,b,124,0,b,124,76,b,b,b,124,0,b,124,96,b,124,96,124,0,b,124,96,b,124,96,b,0,b,124,76,64,92,76,124,0,b,100,b,b,124,100,b,0,b,24,b,b,b,b,b,0,b
420 DATA 12,b,b,b,b,60,0,b,100,b,b,124,76,b,0,b,96,b,b,b,b,120,0,b,126,90,b,b,b,b,0,b,124,76,b,b,b,b,0,b,124,100,b,b,b,124,0,b,124,100,b,124,64,b,0,b,124,100,b,b,b,126,0,b,124,100,b,124,76,b,0,b,124,100,96,28,76,124,0,b,126,24,b,b,b,b,0,b
430 DATA 100,b,b,b,b,124,0,b,100,b,b,b,b,56,0,b,90,b,b,b,b,126,0,b,100,b,b,124,92,b,0,b,104,b,b,120,16,b,0,b,124,100,28,32,76,124
440 BEEP .2,15: BEEP .4,-5
450 STOP
460 REM destroy program
470 RANDOMIZE USR 0
480 DATA 0,124,76,b,124,76,b,0,b,124,100,b,124,100,124,0,b,124,76,64,76,b,124,0,b,124,76,b,b,b,124,0,b,124,96,b,124,b,124,0,b,124,96,b,124,96,b,0,b,124,76,64,92,76,124,0,b,100,b,b,124,100,b,0,b,24,b,b,b,b,b,0,b
490 REM *********************** NEW CHR$ ***********************
500 LOAD "NEW CHR$"CODE 64368,264
510 SAVE "NEW CHR$" LINE 10: REM SAVE "NEW CHR$"CODE 64368,264
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


