--- title: "Wordfix" id: 56745 type: "computer_media" slug: "wordfix" url: "http://localhost/computer_media/wordfix/" markdown_url: "http://localhost/computer_media/wordfix.md" published_at: "2024-09-22T23:31:59+00:00" modified_at: "2026-04-03T07:58:53+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/WF.jpeg" excerpt: "A complete BASIC text editor from 1982 with cursor movement, word-wrap, margin settings, and file save/load — all crammed into a single string buffer." 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: "KOPAK Creations" slug: "kopak-creations-inc" taxonomy: "post_tag" url: "http://localhost/tag/kopak-creations-inc/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Word Processor" slug: "word-processor" taxonomy: "genre" url: "http://localhost/type/word-processor/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Wordfix%20%28198x%29%28KOPAK%29%28TS1000%29%28US%29%28Program%29.zip" mediadate: "198x" producer_company: - id: 11026 title: "KOPAK Creations, Inc." type: "company" url: "http://localhost/company/kopak-creations-inc/" images: - url: "http://localhost/wp-content/uploads/2024/09/WF.jpeg" - url: "http://localhost/wp-content/uploads/2024/09/INST1-6-scaled.jpg" - url: "http://localhost/wp-content/uploads/2024/09/INST2-6-scaled.jpg" - url: "http://localhost/wp-content/uploads/2024/09/Tape-19.jpg" related_products: - id: 14620 title: "Wordfix" type: "product" url: "http://localhost/product/wordfix/" media_type_tags: "Word Processor" --- 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: 1. **Lines 100–169:** Initialisation and main insert-mode loop. `Z$` is the document buffer; `A` is the cursor position (0 = before first character). 2. **Lines 200–329:** Secondary input loop used in certain modes; dispatches keyword/symbol lookup against a packed string at line 250. 3. **Lines 400–499:** Display refresh — clears screen, prints prompt and buffer, then falls through to the keypress routine at 1000. 4. **Lines 500–609:** Control-key dispatcher for non-printable key codes. 5. **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$`. 6. **Lines 1100–1519:** Word-wrap printer with configurable left (`L`) and right (`R`) margins. 7. **Lines 1600–1889:** File management (load/save with variable-name indirection) and block-selection/cut routines. 8. **Lines 4360–4630:** Cursor-movement primitives (page up/down, character left/right, etc.), each terminated with `RETURN`. 9. **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). 10. **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 1231` at 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 `RETURN` statements that appear unreachable given the preceding `GOTO`s 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 and `X$` is non-empty — an unconventional use that saves the variable name as a tape file. ## 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"" 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 A119 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 A1 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 ```