This program inverts the video attribute of every character token stored in the current BASIC program listing held in RAM. It walks memory from the start of the BASIC program area (address held in system variables at 16396–16397) to the end (16400–16401), toggling bit 7 of each byte to switch between normal and inverse video, while skipping newline tokens (value 118, which marks end-of-line). The main routine at lines 10–50 lists the program, prints it to the printer, stops, saves, and reruns — forming a self-modifying display loop. The technique exploits the ZX81/TS1000 convention that inverse video characters are simply normal character codes ORed with 128 (bit 7 set).
Program Analysis
Program Structure
The program is split into two functional blocks: a short main routine (lines 10–50) and a memory-walking subroutine (lines 8000–8080).
- Lines 10–50: Call the inversion subroutine,
LISTthe (now-modified) program to screen,LLISTit to printer,STOP,SAVEthe modified version, andRUNagain. - Lines 8000–8080: Traverse every byte of the BASIC program in RAM and toggle bit 7 on each non-newline byte.
Memory Map Usage
The subroutine uses four system variable addresses to locate the program boundaries:
| Address | System Variable | Role |
|---|---|---|
| 16396–16397 | PROG | Start address of BASIC program |
| 16400–16401 | VARS | Start of variables area (= end of program) |
The two-byte little-endian reads (PEEK addr + 256*PEEK (addr+1)) reconstruct the full 16-bit pointer, a standard ZX81/TS1000 idiom for reading system variable word values.
The Inversion Loop
Lines 8010–8080 implement a GOTO-based loop. INCA is set to 8020 at line 8010, so GOTO INCA at line 8080 always jumps back to the increment step at line 8020. This avoids a literal line-number constant in the loop tail, but since INCA never changes, it is effectively a fixed GOTO 8020.
The inversion logic at lines 8060–8070 is:
- If the byte value is less than 128 (bit 7 clear → normal character), add 128 to set bit 7, making it inverse.
- If the byte value is exactly 128, subtract 128, returning it to 0. This handles only the single case of inverse-space (code 128) → normal space (code 0).
- Bytes 129–255 (other inverse characters) are not handled by line 8070 and are left unchanged — a logic gap (see Bugs section).
Newline tokens (value 118, NEWLINE in ZX81 BASIC) are skipped by line 8050, preserving program line structure.
Self-Modifying Behaviour
Because the subroutine operates on the live program bytes in RAM — including its own lines — running it a second time (via RUN at line 50 after SAVE) would toggle all the bytes back, making the transformation fully reversible. The SAVE at line 40 captures the inverted state to tape.
Notable Techniques
- Using a variable (
INCA=8020) as aGOTOtarget is a form of computed jump, though here the value is constant. - The
PKAvariable cachesPEEK AMEMat line 8040, but line 8050 redundantly re-evaluatesPEEK AMEMrather than usingPKA— a minor inefficiency. - The main loop at lines 10–50 forms a tape-based iterative cycle: list → print → stop → save → run → repeat.
Bugs and Anomalies
- Incomplete inverse reversal: Line 8070 only un-inverts code 128 (inverse space). Any other already-inverse character (codes 129–255) passes through line 8060 untouched (condition
PKA<128is false) and also misses line 8070 (PKA=128is false), so it is left as-is. A correct toggle would needIF PKA>128 THEN POKE AMEM,(PKA-128). - Off-by-one start:
AMEMis initialised toPROG+1(incremented at line 8020 before the first byte is examined), so the very first byte of the program is never processed. - Line-number bytes also toggled: ZX81 BASIC line format stores a two-byte line number followed by a two-byte length before the tokens. These structural bytes are not distinguished from character tokens and will also have bit 7 flipped, potentially corrupting line numbers on a second pass.
Content
Source Code
10 LIST
20 GOSUB 8000
25 LLIST
30 STOP
40 SAVE "1013%9"
50 RUN
\n8000 LET AMEM=PEEK 16396+256*PEEK 16397
\n8010 LET INCA=8020
\n8020 LET AMEM=AMEM+1
\n8030 IF AMEM=PEEK 16400+256*PEEK 16401 THEN RETURN
\n8040 LET PKA=PEEK AMEM
\n8050 IF PEEK AMEM=118 THEN GOTO INCA
\n8060 IF PKA<128 THEN POKE AMEM,(PKA+128)
\n8070 IF PKA=128 THEN POKE AMEM,(PKA-128)
\n8080 GOTO INCA
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
