Textwriter 2000 is a full-featured word processor, storing up to 840 lines of 32-character text in a large string array `b$` dimensioned at (841×32) bytes. It supports a typing mode with cursor movement, tab stops (up to five), word wrap, and a caps-lock toggle, alongside block editing commands including append, align, delete line, copy, move, and insert operations entered through an INPUT prompt. Files are saved and loaded as CODE blocks, with the start address located by a machine-code search routine at line 9300 that scans memory for a sentinel byte pattern; the actual RANDOMIZE USR 100 calls suggest a small resident machine-code routine is expected at address 100. Printing supports both single- and double-spaced output, with a double-width mode that interleaves two lines per LPRINT pass and handles hyphenation and period spacing adjustments.
Program Structure
The program is organized into several functional zones, with the main menu at lines 9005–9100, the text editor loop at lines 200–480, block editing commands at lines 1500–3550, printing routines at lines 5100–7030, and utility subroutines at lines 600–960 and 8000–9330. Execution begins at line 100, which jumps immediately to the initialization block at line 8990.
| Line Range | Function |
|---|---|
| 100 | Entry point — GO TO 8990 |
| 8990 | Initialization: POKEs, DIM b$, variable setup |
| 9005–9100 | Main menu and file operation dispatch |
| 200–480 | Core typing/editing loop |
| 600–960 | Display refresh and status bar subroutines |
| 1500–3550 | Block editing (AC/AL/DL/C/MS/MI/MR/BM/BI) |
| 4000–4090 | Tab stop setting and navigation |
| 5100–6110 | Printer output (single-width and double-width) |
| 7000–7030 | Line counter and page-feed subroutine |
| 8000–8015 | Word-wrap boundary finder |
| 9140–9155 | INK/PAPER/BORDER color setup |
| 9300–9330 | Machine-code address search routine |
Text Storage Model
All text is stored in a single large string b$, dimensioned at (z+1)*32 bytes where z=840, giving 841 lines of 32 characters each. Lines are accessed via the formula t=(w+x-1)*32, where w is the window base line and x is the screen row (1–21). Characters are addressed as b$(t+y) for column y (1–31, with column 32 reserved for control/flags). This layout treats the entire document as a flat byte array, which allows efficient block moves using Spectrum string slicing.
The variable m tracks the last used line number. On save, m is encoded into bytes 10 and 11 of b$ (b$(10)=low byte, b$(11)=high byte) and recovered on load at line 214 with LET m=CODE b$(11)*256+CODE b$(10).
Machine Code Usage
RANDOMIZE USR 100 appears at lines 214, 9062, 9992, and 9998. This calls a small machine-code routine expected to reside at address 100 in RAM. The program does not define this routine itself, implying it is loaded separately or pre-POKEd. Line 9998 performs LOAD "L.B1" after the USR call, suggesting “L.B1” may be such a support file. Line 214 also loads t$CODE 36585 before calling USR 100, indicating the machine code for the editor is stored as a CODE block at address 36585.
The routine at lines 9300–9330 is a BASIC-based address scanner that locates where b$‘s data begins in memory by searching for a sentinel byte sequence (PEEK r=32 followed by PEEK r=CODE b$(10)), then returns r-9 as the base address r for the CODE save at line 9062.
Editor Loop and Key Handling
The main editor loop uses PAUSE 0 followed by INKEY$ (line 235) as the standard keypress-wait idiom. Printable characters (space through “z”) are inserted directly. Control keys are dispatched by ASCII code:
- Code 8: cursor left (line 335, GO TO 275)
- Code 9: cursor right (line 330, GO TO 260)
- Code 10: cursor down (line 345, GO TO 295)
- Code 11: cursor up (line 340)
- Code 12: delete character (line 352)
- Code 13: carriage return / newline (line 350)
- Code 7: enter block edit mode (line 370, GO TO 1500)
- Code 195: set tab stop (line 365, GO TO 4000)
- Code 205: advance to next tab (line 355, GO TO 4050)
- Code 226: enter read/scroll mode (line 375, GO TO 870)
The caps-lock toggle is implemented using variable ct, which alternates between 2 and 0.5 via LET ct=1/ct (line 380). When ct=0.5, characters are shifted to uppercase by subtracting 32 from their code (lines 252, 403), with a guard against non-letter characters.
Word Wrap
Word wrap is triggered when the cursor reaches column 32 (line 235 checks y=32). The wrap routine (lines 400–480) scans backward from the current position to find the last space, then moves the word fragment to the next line. A check at line 468 handles the special case where the last character of the current line is a period, nudging the wrap point by one. The routine also reads ahead into z$ (a word accumulation buffer) during wrap for the mid-word case.
Block Editing Commands
Entering block edit mode (line 1500) prompts for a command string via INPUT LINE c$. Supported commands and their targets:
AC#— Append and center (add lines with centering)AL#— Add lines (insert blank lines)DL#— Delete linesC— Compact/reflow current lineMS#— Mark/store # lines intom$MI— Insert stored block fromm$MR— Memory review (displaym$)BM#— Block move (cut # lines)BI— Block insert (paste cut lines fromn$)
Line insertion and deletion (lines 2290–2476) use BASIC string slicing to shift 320-byte chunks (10 lines) at a time, stepping through the array in a loop. The subroutine at line 2250 calculates the iteration bound ib relative to m and the current position.
Printing
Single-space printing (lines 5520–5580) simply LPRINTs each 32-byte line. Double-width printing (lines 6000–6110) is more complex: it interleaves pairs of lines, computes a split point sp using the word-wrap boundary finder at line 8000, and constructs a combined 32-character line p$ that spans two document lines side by side. Special handling for hyphens (sp+2) and periods (sp-1) adjusts the split point. The page-feed subroutine at line 7000 increments lc by lf (1 or 2 for double spacing) and issues a form-feed when lc>=lp.
A byte value of 6 (CHR$ 6) stored in column 32 of a line acts as a tab/centering flag for double-width output, detected by CODE b$(i+32)=6 at lines 6023 and 6074, which routes those lines through a TAB 16 LPRINT for right-column-only output.
Tab Stops
Up to five tab stops are stored in array e(5). Setting a tab (line 4000) records the current column y into e(st) and increments st, with a guard at line 4010 that warns and refuses a sixth tab. Advancing to the next tab (lines 4050–4090) scans e(1) through e(6) (the sixth iteration falls through to y=1 as a wrap-around) and jumps to the first tab position greater than the current column.
Notable Techniques and Anomalies
- The initialization POKE at line 8990 includes
POKE 23658,0(disabling CAPS LOCK) andPOKE 23562,1(likely adjusting a system flag). VAL "number"is not present, but line 9062 usesSAVE t$CODE r,m*32whereris computed at runtime by the scanner subroutine — a clean approach to locating the dynamic string array in memory without hardcoded addresses.- Line 2075 checks whether the first eight bytes of the current line are all spaces to detect a blank/indented line and redirects to the delete logic at line 2454, effectively merging compact and delete behavior for empty lines.
- The read/scroll mode (lines 865–910) uses a separate PAUSE 0 / INKEY$ loop and only responds to cursor-up (code 11), cursor-down (code 10), and the “exit” key (code 226), making it a clean read-only navigation overlay.
- Line 9400 and 9992 contain SAVE statements (
SAVE "TW2000" LINE 8990andSAVE "TW2000.BA" LINE 8990) that are never reached during normal execution — these are developer convenience lines for re-saving the program itself.
Content
Source Code
100 GO TO 8990
210 IF ln=3 THEN GO TO 216
212 CLS
214 POKE 23728,100: RANDOMIZE USR 100: LOAD t$CODE 36585: LET m=CODE b$(11)*256+CODE b$(10): GO TO 9005
216 LET st=0: DIM e(5)
218 GO SUB 810: GO TO 225
220 GO SUB 820: IF m<1 THEN LET m=1
222 IF ct=.5 THEN PRINT FLASH 1;AT 0,21;"C"
225 LET t=(w+x-1)*32
230 PRINT INVERSE 1;AT x,y-1;b$(t+y)
232 IF y=26 THEN BEEP .01,30
235 PAUSE 0: LET a$=INKEY$: IF y=32 THEN GO TO 400
250 IF a$>"z" OR a$<" " THEN GO TO 320
252 IF ct=.5 THEN LET a$=CHR$ (CODE a$-32): IF a$<"!" THEN LET a$=CHR$ (CODE a$+32)
255 LET b$(t+y)=a$: PRINT AT x,y-1;b$(t+y)
260 LET y=y+1: IF y=32 THEN GO TO 950
270 GO TO 230
275 LET y=y-1: IF y<1 THEN LET x=x-1: LET y=31
280 IF x<1 AND w>1 THEN GO TO 1000
285 IF x<1 THEN LET x=1
290 GO TO 225
293 BEEP .01,0
295 LET x=x+1: IF w+x-1>z THEN LET x=x-1: GO TO 230
300 IF w+x-1>m THEN LET m=w+x-1
305 IF x>21 THEN GO TO 1050
310 GO TO 225
320 LET a=CODE a$
325 IF a>7 AND a<14 THEN PRINT AT x,y-1;b$(t+y)
330 IF a=9 THEN GO TO 260
335 IF a=8 THEN GO TO 275
340 IF a=11 THEN LET x=x-1: GO TO 280
345 IF a=10 THEN GO TO 295
350 IF a=13 THEN LET y=1: GO TO 293
352 IF a=12 THEN LET b$(t+y)=" ": PRINT AT x,y-1;" ": GO TO 275
355 IF a=205 THEN GO TO 4050
365 IF a=195 THEN BEEP .05,25: GO TO 4000
370 IF a=7 THEN GO TO 1500
375 IF a=226 THEN GO TO 870
378 IF a=0 THEN GO TO 230
380 LET ct=1/ct: IF ct=.5 THEN GO TO 222
385 IF ct=2 THEN PRINT AT 0,21;" ": GO TO 230
400 IF a$<=" " OR a$>"z" THEN GO TO 420
402 IF z$="" THEN BEEP .01,0
403 IF ct=.5 THEN LET a$=CHR$ (CODE a$-32): IF a$<"!" THEN LET a$=CHR$ (CODE a$+32)
405 LET z$=z$+a$
410 IF x<21 THEN PRINT AT x+1,0;z$
415 GO TO 235
420 IF z$="" THEN LET y=1: GO TO 293
425 LET d$=b$(t+1 TO t+31)
430 FOR y=y-1 TO 1 STEP -1
435 IF d$(y)=" " THEN GO TO 445
440 NEXT y
445 LET yy=LEN (d$(y+1 TO )+z$)+2
450 IF w+x=z THEN GO TO 220
455 LET b$(t+33 TO t+32+yy)=d$(y+1 TO )+z$
460 LET b$(t+y TO t+32)=""
465 LET y=yy: LET z$=""
468 IF b$(t+y+30)="." THEN LET y=y+1
470 IF x=21 THEN GO TO 1050
475 PRINT AT x,0;b$(t+1 TO t+64)
480 GO TO 293
600 INK ink: PAPER pap: BORDER bor: CLS
605 RETURN
610 LET o=21
620 IF w+o>z THEN CLS : LET o=z-w
630 PRINT AT 1,0;b$(w*32+1 TO (w+o)*32)
640 RETURN
700 IF w+22>z THEN GO TO 885
705 LET w=w+21: GO TO 870
730 LET w=w-21: IF w<1 THEN LET w=1
735 GO TO 870
810 GO SUB 600
820 GO SUB 610
830 GO SUB 960
840 PRINT INVERSE 1;AT 0,0;"TYPE MODE-LINE ";w
850 IF ll<>0 THEN PRINT AT 0,19;"m"
860 RETURN
865 GO SUB 600
870 GO SUB 610
875 GO SUB 960
880 PRINT INVERSE 1;AT 0,5;"READ MODE-LINE ";w
885 PAUSE 0
890 LET p$=INKEY$
895 IF CODE p$=11 THEN GO TO 730
900 IF CODE p$=10 THEN GO TO 700
905 IF CODE p$=226 THEN GO TO 9005
910 GO TO 220
950 IF a$=" " OR CODE a$=9 THEN LET y=1: GO TO 293
955 GO TO 235
960 PRINT AT 0,0;" "
965 RETURN
1000 LET w=w-10: IF w<1 THEN LET w=1: LET x=1: GO TO 220
1005 LET x=10
1008 GO SUB 610
1010 PRINT AT 0,15;" "; INVERSE 1;AT 0,15;w: BEEP .01,0
1015 IF o=z-w THEN GO SUB 840
1020 GO TO 225
1050 IF w>z-21 THEN GO TO 225
1055 LET w=w+10: LET x=12: GO TO 1008
1060 PRINT FLASH pr;AT 0,15;"EDIT"
1065 RETURN
1500 LET pr=0
1505 PRINT AT 0,0;"________________________________": GO SUB 1060
1510 INPUT INVERSE 1;"AC#/AL#/DL#/C/MS#/MI/MR/BM#/BI "; LINE c$
1515 LET pr=1: GO SUB 1060
1520 LET xx=x: LET d=w+x-1: LET q=y
1525 IF c$="" THEN GO TO 220
1530 IF c$="mi" THEN GO TO 2890
1533 IF c$="mr" THEN GO TO 5600
1535 IF c$="c" THEN GO TO 2000
1538 IF c$( TO 2)="bi" THEN GO TO 3000
1540 LET c=1: IF LEN c$>2 THEN LET c=VAL c$(3 TO )
1545 IF c$( TO 2)="al" THEN GO TO 2290
1550 IF c$(1)="a" THEN GO TO 2520
1555 IF c$(1)="d" THEN GO TO 2450
1560 GO TO 2800
2000 FOR l=y TO 31
2010 IF b$(t+l)<>" " THEN GO TO 2030
2020 NEXT l
2030 LET b$(t+y TO t+31)=b$(t+l TO t+31)
2040 FOR n=l-y TO 30
2050 IF b$(t+31-n)<>" " THEN GO TO 2070
2060 NEXT n
2070 IF b$(t+31-n)="-" THEN LET n=n+2
2072 IF b$(t+31-n)="." THEN LET n=n-1
2075 IF b$(t+1 TO t+8)=" " THEN LET c=1: LET y=q: GO TO 2454
2080 LET t=t+32: LET xx=xx+1
2090 IF b$(t+1)=" " OR t>(z-1)*32 THEN GO TO 2210
2100 FOR y=n TO 1 STEP -1
2110 IF b$(t+y)=" " THEN GO TO 2150
2120 NEXT y
2130 GO TO 2210
2150 IF n=31 THEN LET n=32
2160 LET b$(t+1-n TO t)=b$(t+1 TO t+y-1)
2165 IF xx<23 THEN PRINT AT xx-1,0;b$(t-31 TO t)
2170 LET l=y+1: IF b$(t-1)="." THEN LET l=l+1
2175 LET y=1: GO TO 2030
2210 LET y=q
2220 IF c$(1)="a" THEN GO TO 2580
2230 GO TO 220
2250 LET ib=i+320-INT ((m-t/32)/10)*320
2255 RETURN
2290 IF z-m<c THEN GO TO 1500
2295 IF 320+c*32> FREE THEN GO TO 1500
2300 LET cc=32*c: LET i=(m-9)*32
2320 IF i<t THEN LET ib=(m+1)*32: GO TO 2390
2330 GO SUB 2250
2340 FOR i=i TO ib STEP -320
2350 LET b$(i+1+cc TO i+320+cc)=b$(i+1 TO i+320)
2360 NEXT i
2390 LET b$(t+y+cc TO ib+cc)=b$(t+y TO ib)
2400 LET b$(t+y TO t+y-1+cc)=""
2410 IF c$="bi" OR c$="mi" THEN RETURN
2415 LET m=m+c
2420 IF c$(2)<>"l" THEN GO TO 2610
2430 GO TO 220
2450 IF y=1 THEN GO TO 2454
2451 LET b$(t+y TO t+32)=""
2453 GO TO 220
2454 IF d+c>m THEN LET c=m-d+1
2455 IF 320+c*32> FREE THEN GO TO 1500
2457 LET cc=c*32: LET i=(m-9)*32
2458 IF i<t THEN LET ib=(m+1)*32: GO TO 2462
2460 GO SUB 2250
2461 IF ib>t+cc THEN GO TO 2464
2462 LET ib=ib+320
2463 GO TO 2461
2464 LET b$(t+1 TO ib-cc)=b$(t+1+cc TO ib)
2466 IF i<t THEN GO TO 2474
2468 FOR j=ib TO i STEP 320
2470 LET b$(j+1-cc TO j+320-cc)=b$(j+1 TO j+320)
2472 NEXT j
2474 LET m=m-c
2476 LET b$((m+1)*32+1 TO (m+1+c)*32)=""
2480 IF c$(1)="a" THEN GO TO 2580
2485 GO TO 220
2520 LET p=c: LET b=0
2540 IF p>32-y THEN LET p=32-y
2550 FOR j=32-p TO 31
2560 IF b$(t+j)<>" " THEN LET t=t+32: LET c=1: LET y=1: GO TO 2300
2570 NEXT j
2580 LET t=(w+x-1)*32
2583 IF b>0 THEN LET b$(t+y+p TO t+32)=b$(t+y TO t+b-1): GO TO 2590
2585 LET b$(t+y+p TO t+32)=b$(t+y TO t+31-p)
2590 LET b$(t+y TO t+y+p-1)=""
2600 GO TO 220
2610 LET y=q: FOR b=j TO y STEP -1
2620 IF b$(t-32+b)=" " THEN GO TO 2640
2630 NEXT b
2640 LET b$(t+1 TO t+32)=b$(t-31+b TO t)
2650 PRINT AT xx,0;b$(t-31 TO t): LET xx=xx+1: LET l=y: GO TO 2040
2800 IF d+c-1>z THEN LET c=z+1-d
2810 IF c$(1)="b" THEN GO TO 3500
2820 IF c=0 THEN LET m$="": LET ll=0: GO TO 220
2830 LET m$=b$(t+1 TO t+32*c)
2840 LET ll=c
2850 GO SUB 3530: GO TO 220
2890 LET c=ll: IF t<=m*32 THEN GO SUB 2300
2900 LET b$(t+1 TO t+32*c)=m$
2910 LET m=m+c: GO TO 220
3000 IF n$="" THEN GO TO 1500
3010 LET c=lk: IF t<=m*32 THEN GO SUB 2300
3020 LET b$(t+1 TO t+32*c)=n$
3030 LET m=m+c: LET n$="": GO TO 220
3500 LET n$=b$(t+1 TO t+32*c)
3510 LET lk=c: GO SUB 3530
3520 LET c=lk: GO TO 2450
3530 IF x+c>22 THEN LET c=22-x
3540 PRINT INVERSE 1;AT x,0;b$(t+1 TO t+32*c): IF c$(1)="m" THEN PAUSE 50
3550 RETURN
4000 IF y=1 THEN GO TO 216
4005 LET st=st+1
4010 IF st>5 THEN PRINT INVERSE 1;AT 0,19;"5 TABS MAX": PAUSE 60: GO TO 220
4020 LET e(st)=y: GO TO 230
4050 IF e(1)<=1 THEN GO TO 230
4052 LET b$(t+32)=CHR$ 6
4055 PRINT AT x,y-1;b$(t+y)
4060 FOR j=1 TO 6
4070 IF j=6 THEN LET y=1: GO TO 293
4080 IF y<e(j) THEN LET y=e(j): GO TO 230
4090 NEXT j
5100 CLS : INPUT "Lines per page?";lp
5110 LET lf=1: IF ln=7 THEN LET lf=2
5120 LET lc=0
5130 INPUT "Double width?(y/n)";w$
5140 IF w$="y" THEN GO TO 6000
5520 FOR i=lb TO le
5530 LPRINT b$(i*32+1 TO i*32+32)
5540 GO SUB 7000
5570 NEXT i
5580 GO TO 9010
5600 CLS : PRINT AT 1,5;"___MEMORY PRINTOUT___"
5610 PRINT m$: PAUSE 0: GO TO 220
6000 FOR i=lb*32 TO le*32 STEP 64
6020 LET p$=b$(i+1 TO i+32)
6023 IF CODE p$(32)=6 THEN LPRINT TAB 16;p$( TO 16): GO SUB 7000: LET i=i-32: NEXT i: GO TO 6065
6025 IF b$(i+33)=" " THEN GO TO 6050
6030 GO SUB 8000
6035 LET sp=31-y
6038 IF p$(y)="-" THEN LET sp=sp+2
6039 IF p$(y)="." THEN LET sp=sp-1
6040 IF sp<1 OR y=21 THEN GO TO 6050
6045 LET p$(33-sp TO 32)=b$(i+33 TO i+32+sp)
6050 GO SUB 6900
6055 IF b$(i+33)=" " OR y=21 THEN LET i=i-32
6060 NEXT i
6065 LET lc=0: LPRINT ''"----"''
6070 FOR i=lb*32 TO le*32 STEP 32
6074 IF CODE b$(i+32)=6 THEN LPRINT b$(i+17 TO i+31): GO SUB 7000: NEXT i: GO TO 9010
6076 GO SUB 8000
6078 IF y=21 THEN LPRINT : GO SUB 7000: NEXT i: GO TO 9010
6080 LET sp=31-y
6082 IF b$(i+y)="-" THEN LET sp=sp+2
6084 IF b$(i+y)="." THEN LET sp=sp-1
6085 LET i=i+32
6090 LET p$=b$(i+sp+1 TO i+31)
6092 IF p$( TO 4)=" " THEN LPRINT
6095 IF sp=-1 THEN LET p$(1)=" "
6100 GO SUB 6900
6105 IF CODE b$(i+96)<>6 AND b$(i+65)=" " AND i<=le*32 THEN LPRINT : GO SUB 7000: LET i=i+32: GO TO 6105
6110 NEXT i: GO TO 9010
6900 LPRINT p$
7000 IF ln=7 THEN LPRINT
7010 LET lc=lc+lf
7020 IF lc>=lp THEN LPRINT ''': LET lc=0
7030 RETURN
8000 FOR y=31 TO 22 STEP -1
8005 IF b$(i+y)<>" " THEN RETURN
8010 NEXT y
8015 RETURN
8990 POKE 23658,0: LET z=840: DIM b$((z+1)*32): LET m=1: LET ink=0: LET pap=7: LET bor=7: LET m$="": LET n$="": LET t$="": LET ll=0: LET z$="": POKE 23562,1
9005 LET ct=2: LET x=1: LET y=1: LET w=1
9010 INK ink: PAPER pap: BORDER bor: CLS
9015 PRINT INVERSE 1;AT 3,9;"TEXTWRITER 2000"''': PLOT 0,141: DRAW 255,0
9020 PRINT "TEXT FILE OPERATIONS"''" 1-Initialize colors"'" 2-Load a file"'" 3-Start a new file"'" 4-Save a file"'" 5-Display file:"''
9025 PRINT "PRINTING"''" 6-Single space"'" 7-Double space": PLOT 0,5: DRAW 255,0
9030 PRINT AT 12,17;t$
9035 BEEP .05,25
9040 PAUSE 0
9045 LET ln=VAL INKEY$: CLS
9048 IF ln>4 THEN GO TO 9065
9050 IF ln=2 OR ln=3 THEN INPUT "Enter file name (9 chr max.) "; LINE t$: GO TO 200
9060 IF ln=1 THEN GO TO 9140
9061 IF ln=0 THEN GO TO 9998
9062 LET m1=m/256: LET b$(11)=CHR$ INT m1: LET b$(10)=CHR$ ((m1-INT m1)*256): GO SUB 9300: RANDOMIZE USR 100: SAVE t$CODE r,m*32
9063 INPUT "Verify? (y/n)";n$: IF n$="n" THEN GO TO 9005
9064 VERIFY t$CODE r,m*32: GO TO 9005
9065 PRINT AT 20,0;"Enter start line"'"Last text on ";m
9070 INPUT lb
9080 LET w=lb
9085 IF ln=5 THEN CLS : LET st=0: DIM e(5): GO TO 865
9090 PRINT AT 20,6;"final": INPUT le
9100 GO TO 5100
9140 PRINT AT 21,0;"Enter value for";
9145 INVERSE 1: PRINT " INK ": INPUT ink
9150 PRINT AT 21,16;"PAPER ": INPUT pap
9155 PRINT AT 21,16;"BORDER": INPUT bor: INVERSE 0: CLS : GO TO 9010
9300 LET fl=0: FOR r=36560 TO 38000
9310 IF PEEK r=32 THEN LET fl=1
9320 IF PEEK r=CODE b$(10) AND fl=1 THEN LET r=r-9: RETURN
9330 NEXT r
9400 SAVE "TW2000" LINE 8990
9992 CLEAR : RANDOMIZE USR 100: SAVE "TW2000.BA" LINE 8990
9998 RANDOMIZE USR 100: LOAD "L.B1"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

