--- title: "Textwriter 2000" id: 70851 type: "computer_media" slug: "textwriter-2000" url: "http://localhost/computer_media/textwriter-2000/" markdown_url: "http://localhost/computer_media/textwriter-2000.md" published_at: "2026-08-23T15:07:57+00:00" modified_at: "2026-08-23T23:49:33+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/textwriter2000-1.png" alt: "Textwriter 2000 screen" excerpt: "A surprisingly capable word processor with word wrap, block editing, tab stops, and both single- and double-spaced printer output packed into one BASIC listing." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "Tesseract Software Development" slug: "tesseract-software-development" taxonomy: "post_tag" url: "http://localhost/tag/tesseract-software-development/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Robert Fingerle" slug: "robert-fingerle" taxonomy: "indiv" url: "http://localhost/indiv/robert-fingerle/" genre: - name: "Word Processor" slug: "word-processor" taxonomy: "genre" url: "http://localhost/type/word-processor/" media_type: "Program" programmers: - name: "Robert Fingerle" slug: "robert-fingerle" taxonomy: "indiv" url: "http://localhost/indiv/robert-fingerle/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Textwriter%202000%20%281984%29%28Tesseract%20Software%20Development%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1984" producer_company: - id: 11236 title: "Tesseract Software Development" type: "company" url: "http://localhost/company/tesseract-software-development/" images: - url: "http://localhost/wp-content/uploads/2026/08/textwriter2000-1.png" alt: "Textwriter 2000 screen" - url: "http://localhost/wp-content/uploads/2026/08/textwriter2000.png" alt: "Textwriter 2000 screen" related_products: - id: 14271 title: "Textwriter 2000" type: "product" url: "http://localhost/product/textwriter-2000/" media_type_tags: "Word Processor" --- # Textwriter 2000 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 lines - `C` — Compact/reflow current line - `MS#` — Mark/store # lines into `m$` - `MI` — Insert stored block from `m$` - `MR` — Memory review (display `m$`) - `BM#` — Block move (cut # lines) - `BI` — Block insert (paste cut lines from `n$`) 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) and `POKE 23562,1` (likely adjusting a system flag). - `VAL "number"` is not present, but line 9062 uses `SAVE t$CODE r,m*32` where `r` is 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 8990` and `SAVE "TW2000.BA" LINE 8990`) that are never reached during normal execution — these are developer convenience lines for re-saving the program itself. ## 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 FREE THEN GO TO 1500 2300 LET cc=32*c: LET i=(m-9)*32 2320 IF i"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 it+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 i32-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 y6 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" ```