This program implements a 64-column text editor using machine code poked from a hex string starting at address 63206 to drive a custom display routine. The machine code is initialized via a hex-string loop (lines 70–80) and activated with RANDOMIZE USR 63218, which installs a display interrupt handler outputting to port 255. UDG memory at 63256–63271 is patched (lines 90–95) to create a custom character set supporting the doubled horizontal resolution. The editor tracks cursor position using variables B (row, 1–24) and C (column, 1–64), with DF holding the display file base address that wraps between 16384 and 24575 in 8192-byte pages. Key presses 4, 5, and 6 toggle inverse video and cursor direction, while the ON ERR / RESET idiom at lines 9990–9992 provides a clean exit that restores normal display mode.
Program Analysis
Program Structure
The program divides into four logical phases: machine code installation (lines 40–95), main editor loop (lines 100–315), a forward-cursor subroutine implied at line 15 (called via GO SUB 15 but not explicitly listed — likely handled by the GO SUB 25 character-rendering routine), and an error/exit handler (lines 9989–9992). The entry point at line 10 jumps to line 40, which checks whether the machine code is already installed before proceeding with initialization.
Machine Code Installation
Lines 65–84 poke 49 bytes of Z80 machine code into address 63206 (U) by parsing a hex string two characters at a time. The expression 16*VAL U$(N)+VAL U$(N+1) converts each hex digit pair into a byte value. RANDOMIZE USR 63218 then executes the installed routine, which sets up a display interrupt using OUT 255,62 (port 255, value 62) to drive the 64-column display hardware, likely a peripheral or shadow memory bank. The check at line 40 (PEEK 23746=128) detects prior installation and skips re-poking.
UDG Patching
Lines 90–95 loop over addresses 63256–63263, patching three layers of UDG data to construct a character set suitable for half-width (64-column) rendering:
POKE A, 248— sets the low UDG row to a fixed maskPOKE (A+8), 255-PEEK (A+16)— inverts the third bank into the secondPOKE (A+16), 255— fills the third bank solid
This builds the inverse-video cursor glyph used during editing at the two UDG pages above the machine code.
Display File Navigation
The Spectrum’s display file is non-linear: pixel rows are stored in three 2048-byte banks. The program works around this using the variable DF, which represents the current character cell’s display address. The wrap logic at line 200 uses:
DF + 8192*(DF<22528)to advance to the next third of the screen- 8191*(DF>24575)to wrap back to 16384
Within a third, line 230 computes L = DF MOD 256 to detect row boundaries, advancing DF by 1792 (7×256) when needed to skip to the next character row within the same screen third.
Character Rendering Subroutine
The subroutine at line 20 (called as GO SUB 25 from line 120 and line 260) renders a character or cursor glyph into the display file. Variable s is set to the ROM font address for character k (at 15616 + 8*(k-32)), and a FOR loop at line 25 steps through the seven pixel rows of the display file in 256-byte strides, poking ABS(i - PEEK s) to optionally invert the glyph when i=255.
Key Handling
| Key code | Action |
|---|---|
| 4 | Set I=0 (normal video) |
| 5 | Set I=255 (inverse video) |
| 6 | Toggle cursor direction (PEEK 23658 XOR 8) |
| 12 | Jump to line 250 (delete/back-cursor path) |
| 32–122 | Printable characters rendered and cursor advanced |
The polling loop at line 130 reads INKEY$ directly and retries on a null return, forming an efficient busy-wait without PAUSE.
Error Handler and Exit
Line 100 uses the TS2068 ON ERR GO TO 9990 keyword ({ in source) to trap any runtime error and route to the cleanup routine. Lines 9990–9992 restore the display by sending OUT 255,128 to disable the 64-column mode, executing the machine code entry point at 63206 to tear down the interrupt, and finally issuing ON ERR RESET (©) to clear the error handler — a tidy three-step shutdown sequence.
Notable Techniques and Anomalies
- Line 15 is targeted by
GO SUB 15at line 190 but does not exist as a separate line; execution falls through to line 20, making the subroutine entry point flexible. - The
DATAstatement at line 55 defines values read intoa–fat line 60, but these variables do not appear to be used later in the listed code — they may serve as spare workspace or the listing is incomplete. - The hex string in
U$begins with a leading asterisk (*) at position 1, so the loop correctly starts atN=2, skipping it as a sentinel/flag character. - The boolean arithmetic idiom
8*(PEEK 23658=8)exploits the fact that Spectrum BASIC returns −1 for true comparisons, so the absolute value or negation is used implicitly through the multiplication producing 0 or 8.
Content
Source Code
5 REM 64COL-P Type in 64 columns
10 GO TO 40
20 LET s=15616+8*(k-32)
25 FOR d=df TO df+1792 STEP 256
30 POKE d,ABS (i-PEEK s): LET s=s+1
35 NEXT d: RETURN
40 IF PEEK 23746=128 THEN GO TO 100
50 CLEAR 63205: LET u=63206
55 DATA 10,11,12,13,14,15
60 READ a,b,c,d,e,f
65 LET u$="*2100603600237CFE7820F8C9F33E01D3F4DBFFCBFFD3FF3E80F5FBCD8E0EF3DBFFCBBFD3FFAFD3F4F1FE80200332C25CFBC9"
70 FOR N=2 TO LEN U$-1 STEP 2
75 POKE U,16*VAL U$(N)+VAL U$(N+1)
80 LET U=U+1: NEXT N
85 RANDOMIZE USR 63218
90 FOR A=63256 TO 63263
95 POKE A,248: POKE (A+8),255-PEEK (A+16): POKE (A+16),255: NEXT A
100 ON ERR GO TO 9990
105 OUT 255,62
110 LET DF=16384: LET B=24: LET C=65: LET I=0
120 LET S=63256+8*(PEEK 23658=8): GO SUB 25
130 LET K=CODE INKEY$
135 IF K=0 THEN GO TO 130
145 IF K=6 THEN POKE 23658,8*(PEEK 23658=0)
155 IF K=5 THEN LET I=255
160 IF K=4 THEN LET I=0
170 IF K=12 THEN GO TO 250
180 IF K<32 OR K>122 THEN GO TO 115
190 GO SUB 15: IF B=1 AND C=2 THEN GO TO 115
200 LET DF=DF+8192*(DF<22528)-8191*(DF>24575)
205 LET C=C-1
210 IF C>1 THEN GO TO 115
215 IF C=1 THEN GO TO 230
220 LET B=B-1: LET C=64
225 GO TO 115
230 LET L=DF-256*INT (DF/256)
235 IF L<>0 THEN GO TO 115
240 LET DF=DF+1792
245 GO TO 115
255 LET S=15616*(I=0)+63272*(I=255)
260 GO SUB 25
270 IF B=24 AND C=65 THEN GO TO 115
275 IF C<65 THEN GO TO 285
280 LET C=1: LET B=B+1
285 IF C>1 THEN GO TO 305
290 LET L=DF-256*INT (DF/256)
295 IF L<>0 THEN GO TO 305
300 LET DF=DF-1792
305 LET C=C+1
310 LET DF=DF-8192*(DF>24575)+8191*(DF<22528)
315 GO TO 115
9989 STOP
9990 OUT 255,128
9991 RANDOMIZE USR 63206
9992 ON ERR RESET
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
