This program implements a full-featured text editor for the ZX81/TS1000, storing document text in a single string variable T$ and operating through a macro-command interpreter. Commands are entered as letter-prefixed macro strings where the letter selects a GOSUB target via a dispatch table array F(26) indexed by character code, allowing one-letter command codes (A=Append, C=Cursor, D=Delete, J=Justify, M=Memory, P=Print, R=Reset, S=Save, T=Test, X=Execute, Z=Stop) to route to the appropriate subroutine. The editor supports multi-page documents (640 characters per page), cursor movement with blinking via inverse-video toggling, word-wrap justification, and repeat-execution of macros via the X command. Text is stored and manipulated entirely as substring operations on T$, with memory usage reported by PEEKing the ZX81 system variables for RAMTOP and the display file pointer.
Program Analysis
Program Structure
The program is organized into an initialization block, a central macro-decode loop, and a suite of command subroutines. Initialization (lines 10–235) populates the dispatch table, then jumps to the title-screen/menu renderer at line 3670 before entering the macro interpreter at line 1000.
| Line range | Purpose |
|---|---|
| 10–150 | Build dispatch table F(26) |
| 200–235 | Initialize editor state variables, jump to splash screen |
| 1000–1230 | Macro decode loop |
| 2000–2140 | A – Append text |
| 2200–2390 | C – Cursor movement and display |
| 2500–2590 | D – Delete |
| 2800–2960 | J – Justify (word-wrap) |
| 3000–3025 | K – LIST (mapped but labelled K & L in menu) |
| 3100–3150 | M – Memory test |
| 3300–3390 | P – Print page |
| 3500–3520 | R – Reset (RUN) |
| 3600–3665 | S – Save to tape |
| 3670–3710 | Splash screen / menu, then enter decode loop |
| 3800–3820 | T – Test (load sample text) |
| 3900–3970 | X – Multiple/repeat execution |
| 4000–4040 | Z – Stop |
Dispatch Table via F(26)
Lines 10–150 initialize the array F(26) with line numbers corresponding to each command subroutine. The macro decoder (line 1200) calls GOSUB F(CODE K$-37), where K$ is the command letter. Since CODE "A" on the ZX81 is 38, subtracting 37 gives index 1 for A, 3 for C, 4 for D, and so on up to 26 for Z. Any letter without an entry in F has a default value of 0, causing a harmless GOSUB 0 to a non-existent line — a known ZX81 no-op technique.
Note that line 80 reads RAND F(12)=3000 rather than LET F(12)=3000. This is a typo or corruption; RANDOMIZE with a conditional expression will silently evaluate as a numeric statement and set the random seed, so F(12) (the L command) is never actually assigned 3000 and remains 0.
Macro Decode Loop
The loop at lines 1000–1230 accepts a line of input in M$, then scans for characters whose CODE falls in the range 38–63 (the letters A–Z on the ZX81 character set). The letter found becomes the command key K$; the digits before it become the numeric repeat count N; the characters between the letter and the next quote become the string argument W$. After the GOSUB dispatch, the parser resumes scanning for the next command in the same input string, allowing multiple commands per input line.
Text Storage and Editing
The entire document is held in the string T$. The current insert/cursor position is tracked by the integer CI. Pages are 640 characters wide (20 lines × 32 columns). Substring slicing with T$(x TO y) handles all insertions, deletions, and page extractions.
- Append (A): Inserts
Nrepetitions ofW$at positionCI, at the start, or at the end ofT$depending on whetherCIis 0, equal toLEN T$, or in between. - Cursor (C): Moves
CIbyNcharacters; special arguments"0"and"1"jump to page start or end of text. Cursor blinking at lines 2310–2340 loops 10 times printing the normal then inverse character. - Delete (D): Removes
Ncharacters from positionCIusing four conditional substring cases; then calls the cursor routine withN=-1to refresh display. - Justify (J): Scans 32-character blocks looking for a space near position 32 to use as a word-wrap break, padding into a fixed-length
DIM H$(32)to normalize the line.
Memory Reporting
The M command (lines 3100–3150) reports free space for text by reading ZX81 system variables directly: PEEK 16386 + 256*PEEK 16387 gives RAMTOP, and PEEK 16412 + 256*PEEK 16413 gives the current stack/string area pointer, with the difference being available bytes.
Notable Techniques
FAST/SLOWmode switching around INPUT and PRINT operations to balance display stability with execution speed.GOSUB F(CODE K$-37)— a compact computed GOSUB that replaces a long IF/THEN chain for 26 possible commands.- The splash screen at lines 3680–3685 uses ZX81 block-graphic escape sequences to draw a decorative banner.
- Line 55 places
F(9)=3670, pointing the I command (index 9: letter I = CODE 46, 46−37=9) back to the splash/menu screen, implementing a “show menu” function. - The X (multiple execution) command at lines 3900–3970 re-enters the decode loop at line 1040, bypassing the INPUT, to replay the current macro
Ntimes by resettingDandI. - Line 4030 contains
SAVE "1024"— the inverse-digit escapein the filename acts as an auto-run marker for the ZX81 tape loader.
Bugs and Anomalies
- Line 80:
RAND F(12)=3000should beLET F(12)=3000. The L command (index 12) is therefore never registered, so the K/L listing command is inaccessible via the dispatch table despite having its subroutine at line 3000. - The Justify routine (lines 2820–2960) references variable
V$as both a 32-character slice ofT$and later re-DIMsH$; however,V$(I2)at line 2860 treats the previously assigned stringV$as an array, which will work on a ZX81 only ifV$is exactly 32 characters — the logic is fragile for short documents. - The cursor blink loop (lines 2310–2340) uses
J=CODE T$(CI)+128to compute the inverse character, relying on the ZX81 inverse-video bit (bit 7). IfCIpoints beyond the end ofT$, this will cause an error.
Content
Source Code
10 DIM F(26)
20 LET F(1)=2000
30 LET F(3)=2200
40 LET F(4)=2500
50 LET F(6)=2600
55 LET F(9)=3670
60 LET F(10)=2800
70 LET F(11)=3000
80 RAND F(12)=3000
90 LET F(13)=3100
100 LET F(16)=3300
110 LET F(18)=3500
120 LET F(19)=3600
130 LET F(20)=3800
140 LET F(24)=3900
150 LET F(26)=4000
200 LET T$=""
210 LET CI=0
220 LET CPN=1
230 LET XC=0
235 GOTO 3670
1000 REM %M%A%C%R%O% %D%E%C%O%D%E
1010 LET D=0
1020 LET I=1
1025 FAST
1030 INPUT M$
1040 LET L=LEN M$
1050 IF M$(L)<>"""" THEN LET M$=M$+""""
1060 LET L=LEN M$
1070 LET A=CODE M$(I)
1080 IF A>=38 AND A<=63 THEN GOTO 1110
1090 LET I=I+1
1100 GOTO 1070
1110 LET I0=I
1120 LET K$=M$(I)
1130 IF I0=D+1 THEN LET N=0
1140 IF I0<>D+1 THEN LET N=VAL M$(D+1 TO I0-1)
1150 LET I=I+1
1160 IF M$(I)<>"""" THEN GOTO 1150
1180 LET D=I
1190 LET W$=M$(I0+1 TO D-1)
1200 GOSUB F(CODE K$-37)
1210 IF I=L THEN GOTO 1000
1220 LET I=I+1
1230 GOTO 1070
2000 REM %A%P%P%E%N%D
2010 IF N=0 THEN LET N=1
2020 LET U$=""
2030 FOR Q=1 TO N
2040 LET U$=U$+W$
2050 NEXT Q
2060 IF CI=0 THEN GOTO 2110
2070 IF CI=LEN T$ THEN GOTO 2130
2080 IF CI>0 AND CI<LEN T$ THEN LET T$=T$( TO CI)+U$+T$(CI+1 TO )
2090 LET CI=CI+LEN U$
2100 RETURN
2110 LET T$=U$+T$
2120 GOTO 2090
2130 LET T$=T$+U$
2140 GOTO 2090
2200 REM %C%U%R%S%O%R
2210 IF W$="0" THEN GOTO 2360
2215 IF W$="1" THEN GOTO 2380
2220 LET CI=CI+N
2230 IF CI>LEN T$ THEN LET CI=LEN T$
2240 IF CI<=0 THEN GOTO 2360
2250 LET PN=INT ((CI-1)/640)
2255 IF PN<>CPN-1 THEN RETURN
2260 LET CP=CI-640*PN
2270 LET LN=INT ((CP-1)/32)
2280 LET CN=CP-32*LN-1
2290 LET J=CODE T$(CI)+128
2300 IF J>=256 THEN LET J=J-256
2310 FOR Y=1 TO 10
2320 PRINT AT LN,CN;T$(CI)
2330 PRINT AT LN,CN;CHR$ J
2340 NEXT Y
2350 RETURN
2360 LET CI=640*(CPN-1)
2370 RETURN
2380 LET CI=LEN T$
2390 RETURN
2500 REM %D%E%L%E%T%E
2510 IF N=0 THEN LET N=1
2520 LET L0=LEN T$
2530 IF CI<=1 AND N>=L0 THEN LET T$=""
2540 IF CI<=1 AND N<L0 THEN LET T$=T$(N+1 TO )
2550 IF CI>1 AND CI+N-1>=L0 THEN LET T$=T$( TO CI-1)
2560 IF CI>1 AND CI+N-1<L0 THEN LET T$=T$( TO CI-1)+T$(CI+N TO )
2570 LET N=-1
2580 GOSUB 2200
2590 RETURN
2800 REM %J%U%S%T%I%F%Y
2810 LET CI=1
2820 IF CI+31>LEN T$ THEN RETURN
2830 LET V$=T$(CI+31)
2835 IF CI+32>LEN T$ THEN RETURN
2840 IF T$(CI+32)=" " THEN LET T$=T$( TO CI+31)+T$(CI+33 TO )
2850 LET I2=32
2860 IF V$(I2)=" " THEN GOTO 2920
2870 LET I2=I2-1
2880 IF I2=0 THEN GOTO 2900
2890 GOTO 2860
2900 LET CI=CI+32
2910 GOTO 2820
2920 DIM H$(32)
2930 LET H$=V$( TO I2)
2940 LET T$=T$( TO CI-1)+H$+T$(CI+I2 TO )
2950 LET CI=CI+32
2960 GOTO 2820
3000 REM %K% %A%N%D% %L
3005 CLS
3010 IF W$="" THEN LET N=1
3015 IF W$<>"" THEN LET N=VAL W$
3020 LIST N
3025 RETURN
3100 REM %M%E%M%O%R%Y
3110 CLS
3120 PRINT AT 5,0;"SPACE FOR TEXT IS ";
3130 PRINT PEEK 16386+256*PEEK 16387-PEEK 16412-256*PEEK 16413;
3140 PRINT " BYTES"
3150 RETURN
3300 REM %P%R%I%N%T
3310 CLS
3315 SLOW
3320 IF N=0 THEN LET N=CPN
3330 LET CPN=N
3340 LET H=640*N
3350 IF LEN T$<H THEN LET H=LEN T$
3360 PRINT T$(640*N-639 TO H)
3370 PRINT AT 21,0;"PAGE ";CPN
3375 LET N=0
3380 GOSUB 2200
3390 RETURN
3500 REM %R%E%S%E%T
3510 RUN
3520 RETURN
3600 REM %S%A%V%E
3610 CLS
3620 PRINT "%S%A%V%E"
3630 PRINT
3635 PRINT "INPUT PROGRAM NAME, DEFAULT NAME IS ""%T"""
3640 PRINT
3645 PRINT "START TAPE PLAYER"
3650 INPUT V$
3655 IF V$="" THEN LET V$="T"
3660 SAVE V$
3665 GOTO 10
3670 CLS
3680 PRINT "######## .''''. : : .''''. '. .' : "
3681 PRINT " ,,## :......: .''''.: '':''''' :......: '..' '':''''' "
3682 PRINT " ,,##~~ : . : : : . : . .''. : . "
3683 PRINT ",,##~~ '....' '....': '..' '....' .' '. '..'"
3684 PRINT "##~~"
3685 PRINT "################################################################"
3686 PRINT
3687 PRINT
3690 PRINT "A APPEND C CURSOR"
3691 PRINT
3692 PRINT "D DELETE I INFO (MENU)"
3693 PRINT
3694 PRINT "J JUSTIFY M MEMORY TEST"
3695 PRINT
3696 PRINT "P PRINT R RESET"
3697 PRINT
3698 PRINT "S SAVE T TEST"
3699 PRINT
3700 PRINT "X EXECUTE * N Z STOP"
3710 GOTO 1000
3800 REM %T%E%S%T
3810 LET T$="TO SEE A WORLD IN A GRAIN OF SAND AND A HEAVEN IN A WILD FLOWER, HOLD INFINITY IN THE PALM OF YOUR HAND AND ETERNITY IN AN HOUR."
3820 RETURN
3900 REM %M%U%L%T%I%P%L%E% %E%X%E%C%U%T%I%O%N% %X
3910 IF XC=N-1 OR N<=1 THEN GOTO 3960
3920 LET XC=XC+1
3930 LET D=0
3940 LET I=1
3950 GOTO 1040
3960 LET XC=0
3970 RETURN
4000 REM %S%T%O%P
4010 STOP
4020 CLEAR
4030 SAVE "1024%4"
4040 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
