This program is a full-screen text editor for the ZX81/TS1000, written entirely in BASIC and copyrighted N. Godwin 1982. It maintains a single string variable Z$ as the document buffer, with an integer cursor position A tracking the insertion point within that string. The editor supports cursor movement, character insertion and deletion, word-wrap printing with configurable left and right margins, and file load/save operations using PEEK and POKE to manipulate system variables directly. Key presses are read via a PAUSE/INKEY$ polling loop, and characters are identified by their CHR$ codes; control functions are dispatched through computed GOSUB targets of the form X*30+1000. The program also includes a 704-byte block copy routine that transfers text to and from a fixed RAM area at address 32000.
Program Analysis
Program Structure
The program is organised as a set of loosely coupled subroutines grouped by function, with a main edit loop beginning at line 130. The initial GOTO 130 at line 1 skips past short utility stubs at lines 2–7 that are called by number from elsewhere. The high-level flow is:
- Lines 100–169: Initialisation and main insert-mode loop.
Z$is the document buffer;Ais the cursor position (0 = before first character). - Lines 200–329: Secondary input loop used in certain modes; dispatches keyword/symbol lookup against a packed string at line 250.
- Lines 400–499: Display refresh — clears screen, prints prompt and buffer, then falls through to the keypress routine at 1000.
- Lines 500–609: Control-key dispatcher for non-printable key codes.
- Lines 1000–1099: Core keypress engine: highlights the character under the cursor by OR-ing 128 into its display code via POKE, then waits for a key with
PAUSE 40000/INKEY$. - Lines 1100–1519: Word-wrap printer with configurable left (
L) and right (R) margins. - Lines 1600–1889: File management (load/save with variable-name indirection) and block-selection/cut routines.
- Lines 4360–4630: Cursor-movement primitives (page up/down, character left/right, etc.), each terminated with
RETURN. - Lines 7540–7899: High-level menu actions: go to load (7540), clear and load 704-byte block from address 32000 (7570–7609), enter block-select mode (7660), go to save (7750), program-name input (7780), and a tape-based block load (7870).
- Line 9000: Copyright notice: N. Godwin 1982.
Document Buffer Management
The entire document is held in the single string Z$. The cursor position is the integer A, meaning the insertion point is between characters A and A+1. Three cases are handled explicitly at lines 450–470:
A=0: prepend —Z$=X$+Z$0 < A < LEN Z$: insert —Z$=Z$(1 TO A)+X$+Z$(A+1 TO )A=LEN Z$: append —Z$=Z$+X$
Deletion at line 4570–4589 mirrors this three-way split, removing the character at position A and decrementing A. Because BASIC string slicing allocates new strings on every operation, heavy editing will fragment the string area and may trigger garbage collection pauses.
Computed GOSUB Dispatch
Control characters are dispatched using the idiom GOSUB X*30+1000 (lines 230, 520, 772). The key code X is multiplied by 30 to produce a line-number target. The cursor-movement stubs at lines 4360, 4390, 4420, 4450, 4510, 4540, 4570, 4630 correspond to specific computed targets. For example:
| X (key code) | X*30+1000 | Routine |
|---|---|---|
| 112 | 4360 | Cursor page-up (A−32) |
| 113 | 4390 | Cursor page-down (A+32) |
| 114 | 4420 | Cursor left (A−1) |
| 115 | 4450 | Cursor right (A+1) |
| 117 | 4510 | Go to print/margin routine |
| 118 | 4540 | Set X=0 (newline/clear flag) |
| 119 | 4570 | Delete character |
| 121 | 4630 | Set X=12 (form-feed/CLS flag) |
Cursor Highlighting via PEEK/POKE
The character under the cursor is highlighted by directly poking the display file. Routine at lines 1000–1050 computes the display-file address of the cursor character using the system variable at 16396/16397 (D-FILE pointer), accounting for the newline byte at the end of each 32-character row with INT((A-1)/32). The character code is read with PEEK Q, converted via GOSUB 1080 (which maps code 192 to 11, i.e. treats the cursor-block graphic as a space), and if it is a printable ASCII character, OR-ed with 128 via POKE Q, X+128 to display it in inverse video as the cursor. This avoids any flicker from re-printing and works directly on the hardware display file.
System Variable Manipulation for File I/O
Rather than using a high-level LOAD/SAVE with a fixed name, the file routines at lines 1670–1682 patch system variables in RAM to redirect the tape filename at runtime. POKE 16559 sets the filename character, POKE 16562/16563 sets associated length or type bytes, allowing the editor to load or save named string variables (e.g. A$ through W$) by writing their letter code directly into the system area before falling through to line 4 (LET Z$="") or line 5 (RETURN).
Word-Wrap Printer
The print routine (lines 1220–1519) implements a simple word-wrap algorithm. Starting from position J in Z$, it attempts to advance 32−R characters (where R is the right margin). If that position is not a space, it walks backwards to find the nearest preceding space and breaks there. Left-margin padding is handled by subroutine 1370, which prints L spaces before each line. The routine checks PEEK 16442 (the display scroll counter) to detect a full screen, then pauses for a keypress (lines 1490–1519) before continuing or scrolling/clearing. A CHR$ 67 (the letter C) embedded in Z$ acts as a hard line-break sentinel (lines 1430, 7690).
Fixed RAM Buffer at Address 32000
Lines 7570–7609 implement a 704-byte (22 rows × 32 columns) snapshot of the display file, stored at the fixed address 32000. Loading reads the buffer back through PEEK and rebuilds Z$; saving copies the D-FILE contents (again compensating for end-of-line newline bytes with INT((J-1)/32)) into the block. This provides a crude fast-save to a fixed RAM location, presumably in a RAM pack, bypassing tape entirely.
Keyword Lookup Table
Line 250 defines a 43-character lookup string X$ containing block-graphic symbols and BASIC keywords. The loop at lines 260–280 searches this string for a character matching the current key code; if found, the corresponding single character at offset J-21 is used as the inserted text. This allows the editor to insert printable representations of ZX81 block graphics and keywords into the document buffer.
Notable Anomalies and Dead Code
- Lines 1045 and 1820 are
REM-commented-out conditionals that would have prevented re-highlighting a character already under the cursor (code 67). They appear to have been disabled during development. - Line 1231 is referenced by
GOTO 1231at line 1519 but does not exist as a distinct line; execution will fall to line 1235, which is the correct continuation — a deliberate jump-to-next-line technique. - Line 1246 is referenced at line 1449 but does not exist; execution continues at 1250. Similarly a known ZX81 BASIC technique.
- Lines 4520, 7689, and 7899 are bare
RETURNstatements that appear unreachable given the precedingGOTOs at lines 4510, 7660, and 7870 respectively. They may be defensive stubs. - The
SAVE X$at line 530 saves the filename string itself to tape when key code 226 is received andX$is non-empty — an unconventional use that saves the variable name as a tape file.
Content
Source Code
1 GOTO 130
2 IF C$="" THEN LET X=999
3 RETURN
4 LET Z$=""
5 RETURN
6 LET Z$=Z$+""
7 RETURN
100 LET Z$=""
120 LET A=0
130 GOSUB 400
140 IF X=227 THEN STOP
150 IF X>63 AND X<>192 THEN GOTO 500
160 LET X$=CHR$ X
165 GOSUB 450
169 GOTO 130
200 GOSUB 400
210 IF X=116 THEN GOTO 130
220 IF X=119 THEN GOTO 330
230 IF X=121 OR X=118 THEN GOSUB X*30+1000
235 GOSUB 1080
240 IF X>10 AND X<63 THEN GOTO 300
250 LET X$="' ' .. : ..'' :.::.:'':.''.##~~,,!!;;@@% "+CHR$ 117+" AND THEN TO "+CHR$ 114+CHR$ 113+CHR$ 112+CHR$ 115+""" OR STEP <=<>>= STOP LPRINT SLOW FAST LLIST **"+CHR$ 118
260 FOR J=22 TO 42
270 IF X=CODE X$(J) THEN GOTO 290
280 NEXT J
289 GOTO 200
290 LET X$=X$(J-21)
299 GOTO 320
310 LET X=X+128
320 GOSUB 450
329 GOTO 200
330 GOSUB 4570
339 GOTO 200
400 CLS
410 IF A=0 THEN PRINT ">";
420 PRINT Z$
449 GOTO 1000
450 IF A=0 THEN LET Z$=X$+Z$
460 IF A>0 AND A<LEN Z$ THEN LET Z$=Z$(1 TO A)+X$+Z$(A+1 TO )
470 IF A=LEN Z$ THEN LET Z$=Z$+X$
480 LET A=A+1
499 RETURN
500 IF X=116 THEN GOTO 200
505 IF X=227 THEN GOTO 140
510 IF X=228 THEN GOTO 100
520 GOSUB X*30+1000
530 IF X=226 AND X$>"" THEN SAVE X$
560 IF X=117 THEN GOTO 600
570 IF X=0 OR X=12 OR X=67 THEN GOTO 160
580 IF A>LEN Z$ THEN LET A=LEN Z$
590 IF A<1 THEN GOTO 120
599 GOTO 130
600 GOSUB 1060
608 IF X=118 OR X=121 THEN GOTO 130
609 GOTO 500
1000 GOSUB 1090
1010 LET Q=Q+A+INT ((A-1)/32)
1020 IF A=0 THEN LET Q=Q+2
1030 LET X=PEEK Q
1040 GOSUB 1080
1045 REM IF A>0 THEN IF CODE Z$(A)=67 THEN GOTO 1051
1050 IF X=0 OR X>10 AND X<64 THEN POKE Q,X+128
1060 PAUSE 40000
1061 POKE 16437,255
1062 LET X=CODE INKEY$
1069 RETURN
1080 IF X=192 THEN LET X=11
1089 RETURN
1090 LET Q=PEEK 16396+256*PEEK 16397
1099 RETURN
1100 PRINT AT 20,0;
1110 FOR J=28 TO 59
1120 PRINT CHR$ J;
1130 NEXT J
1140 PRINT "ENTER L.H. MARGIN"
1150 GOSUB 1060
1160 IF X<28 OR X>59 THEN GOTO 1180
1170 LET L=X-28
1180 PRINT AT 21,6;"R"
1190 GOSUB 1060
1200 IF X<28 OR X>59 THEN GOTO 1220
1210 LET R=60-X
1220 CLS
1230 FOR J=1 TO LEN Z$
1235 IF PEEK 16442<3 THEN GOTO 1490
1240 GOSUB 1370
1245 GOTO 1420
1250 LET K=J
1260 LET J=J+32-R
1270 IF J<=LEN Z$ THEN GOTO 1290
1280 PRINT Z$(K TO )
1289 GOTO 1360
1290 IF Z$(J)>" " THEN GOTO 1320
1300 PRINT Z$(K TO J-1)
1310 GOTO 1350
1320 LET J=J-1
1330 IF Z$(J)=" " THEN GOTO 1300
1340 GOTO 1320
1350 NEXT J
1365 LET X=117
1369 RETURN
1370 IF L=0 THEN RETURN
1380 FOR L=1 TO L
1390 PRINT " ";
1400 NEXT L
1410 LET L=L-1
1419 RETURN
1420 FOR K=J TO J+32-R
1425 IF K>LEN Z$ THEN GOTO 1449
1430 IF Z$(K)=CHR$ 67 THEN GOTO 1450
1440 NEXT K
1449 GOTO 1246
1450 IF K=J THEN GOTO 1480
1460 PRINT Z$(J TO K-1);
1470 LET J=K
1479 GOTO 1240
1480 PRINT
1489 GOTO 1350
1490 GOSUB 1060
1500 IF X<>121 AND X<>118 THEN RETURN
1510 IF X=118 THEN SCROLL
1511 IF X=121 THEN CLS
1519 GOTO 1231
1520 PRINT AT 21,0;"FILE REF (A TO W)"
1529 GOTO 1060
1600 GOSUB 1520
1610 IF X<38 OR X>59 THEN RETURN
1620 POKE 16529,X
1630 GOSUB 2
1640 IF X=999 THEN GOTO 1670
1650 PRINT AT 21,0;"WARNING - ";CHR$ X;"$ IN USE"
1660 GOSUB 1060
1669 IF X<>PEEK 16529 THEN GOTO 1610
1670 POKE 16559,PEEK 16529
1671 POKE 16562,63
1672 POKE 16563,13
1679 GOTO 4
1680 POKE 16559,61
1681 POKE 16562,X
1682 POKE 16563,13
1689 RETURN
1700 GOSUB 1520
1710 IF X<38 OR X>59 THEN RETURN
1720 GOSUB 1680
1750 GOSUB 4
1759 GOTO 450
1760 LET J=A
1770 GOSUB 1060
1772 IF X>111 AND X<116 THEN GOSUB X*30+1000
1773 IF A>LEN Z$ THEN LET A=LEN Z$
1774 IF A<J THEN LET A=J
1775 IF X=119 THEN GOTO 1840
1776 IF X<112 OR X>119 THEN GOTO 1880
1778 GOSUB 1090
1780 LET Q=Q+A+INT ((A-1)/32)
1790 IF A=0 THEN LET Q=Q+2
1800 LET X=PEEK Q
1810 GOSUB 1080
1820 REM IF A>0 THEN IF CODE Z$(A)=67 THEN GOTO 1770
1830 IF X=0 OR X>10 AND X<64 THEN POKE Q,X+128
1839 GOTO 1770
1840 IF J<2 THEN LET Z$=Z$(A+1 TO )
1850 IF J>1 AND A<LEN Z$ THEN LET Z$=Z$(1 TO J-1)+Z$(A+1 TO )
1860 IF J>1 AND A>=LEN Z$ THEN LET Z$=Z$(1 TO J-1)
1880 LET A=J
1889 RETURN
4360 LET A=A-32
4369 RETURN
4390 IF A=0 AND Z$>"" THEN LET A=1
4400 LET A=A+32
4409 RETURN
4420 LET A=A-1
4429 RETURN
4450 LET A=A+1
4459 RETURN
4510 GOTO 1100
4520 RETURN
4540 LET X=0
4549 RETURN
4570 IF A=LEN Z$ THEN GOTO 4580
4572 IF A<2 THEN LET Z$=Z$(2 TO )
4574 IF A>1 THEN LET Z$=Z$(1 TO A-1)+Z$(A+1 TO )
4579 GOTO 4581
4580 LET Z$=Z$(1 TO LEN Z$-1)
4582 LET A=A-1
4589 RETURN
4630 LET X=12
4639 RETURN
7540 GOTO 1700
7570 LET Z$=""
7571 FOR J=1 TO 704
7573 LET Z$=Z$+CHR$ PEEK (32000+J)
7574 NEXT J
7580 FOR J=704 TO 1 STEP -1
7582 IF Z$(J)>" " THEN RETURN
7584 LET Z$=Z$(1 TO J-1)
7586 NEXT J
7600 GOSUB 1090
7602 FOR J=1 TO 704
7604 POKE 32000+J,PEEK (Q+J+INT ((J-1)/32))
7606 NEXT J
7609 RETURN
7660 GOTO 1760
7689 RETURN
7690 LET X=67
7699 RETURN
7750 GOTO 1600
7780 PRINT AT 21,0;"ENTER PROGRAM NAME"
7790 INPUT X$
7799 RETURN
7870 GOSUB 1520
7872 POKE 16559,X
7873 POKE 16562,11
7874 POKE 16563,11
7879 GOTO 4
7899 RETURN
9000 REM COPYRIGHT N.GODWIN 1982
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.



