--- title: "VU-Write" id: 55609 type: "computer_media" slug: "vu-write" url: "http://localhost/computer_media/vu-write/" markdown_url: "http://localhost/computer_media/vu-write.md" published_at: "2024-06-30T13:42:38+00:00" modified_at: "2026-04-03T07:59:06+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/SCR-20240630-irwk.png" excerpt: "A full-featured document processor with line insert, delete, block-move, tab stops, title fields, and printer output — all packed into a compact BASIC program." 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: "SyncMaster" slug: "syncmaster" taxonomy: "post_tag" url: "http://localhost/tag/syncmaster/" - 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: "Cassette" download_url: "https://archive.org/download/timex-sinclair-software-archive/VU-Write%20%281983%29%28SyncMaster%29%28TS1000%29%28US%29%28Cassette%29.zip" mediadate: "1983" producer_company: - id: 11369 title: "SyncMaster" type: "company" url: "http://localhost/company/syncmaster/" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240630-irwk.png" related_products: - id: 14549 title: "Vu-Write" type: "product" url: "http://localhost/product/vu-write/" media_type_tags: "Word Processor" --- # VU-Write VU-Write is a document processor that allows users to create, edit, and print text documents such as letters. It supports configurable record lengths (up to 30 characters per line), two user-definable tab stops, a document title field, and a saved-program name field. The text editor provides insert, delete, and block-move operations on lines stored in a two-dimensional string array `D$(L1,L2)`, where `L1` (maximum line count) is calculated as `INT(11144/L2)` to fit within available memory. Output is sent to a printer via `LPRINT`, with lines beginning with “/” treated as blank lines and text right-padded to a fixed column offset of 30 minus the record length. The program uses `RAND USR` calls at lines 670 and 1010 to invoke machine code routines embedded in the REM statement at line 1. *** ## Program Analysis ### Program Structure VU-Write is organized into clearly labeled subroutines and sections, each introduced by a REM comment. The main modules are: - **Lines 100–390:** Display subroutine — renders document lines on screen scrolling upward from the current line. - **Lines 500–895:** Text editor — handles line entry, move operations, and navigation. - **Lines 900–990:** Title editor — sets the document title stored in `T$`. - **Lines 1000–1090:** Main menu — presents four top-level options (File, Write, LPrint, Save). - **Lines 1100–1179:** File setup — configures record length, allocates `D$()` array. - **Lines 1180–1199:** Initialization subroutine — sets constants and dimensions ancillary arrays. - **Lines 1200–1295:** Tab/Title/Text sub-menu — secondary menu branching to tabs, title, or text editor. - **Lines 1300–1395:** LPrint — sends the document to the printer. - **Lines 1400–1440:** Save — saves the program (including data) to tape. - **Lines 1450–1490:** Init — cold-start entry point, calls the init subroutine then falls through to Save. ### Machine Code Usage Line 1 contains a REM statement that encodes machine code routines directly in the program listing. Two entry points are called explicitly: - `RAND USR VAL "16514"` (line 670) — called after a line is confirmed non-empty before advancing the line pointer, likely performing a screen or memory operation. - `RAND USR VAL "16530"` (line 1010) — called at the start of the main menu, likely initializing the display or clearing the screen in a specific way. Using `VAL "number"` as the argument to `USR` is consistent with the program-wide idiom of passing numeric literals through `VAL` to save memory by avoiding storing floating-point number representations inline. ### Key BASIC Idioms The program makes extensive use of several space- and speed-saving idioms: - **`VAL "number"` in `GO TO`/`GO SUB`:** Almost every branch target is written as `GOTO VAL "NNN"` rather than a bare numeric literal, saving five bytes per number by storing the line number as a string rather than a five-byte floating-point value. - **Named constants `A` and `B`:**`A` is set to `0` and `B` to `1` in the init subroutine (lines 1180–1181). These are used throughout as zero and one, again avoiding the five-byte floating-point overhead for frequently used small integers. - **`C` as a menu return address:**`C` is set to `1000` (line 1182), and `GOTO C` is used as a universal “return to main menu” throughout every module. - **`P` as a long pause value:**`P` is set to `40000` (line 1183) and used in `PAUSE P` followed by `INKEY$` polling — the standard efficient keypress-wait idiom. - **`S$` as a blank/padding string:**`S$` is dimensioned to 32 characters (line 1190) and used as a blank-line sentinel and screen-clearing padding throughout. ### Document Storage and Memory Calculation After the user specifies a record length `Q` (1–30 characters), the program calculates the maximum number of lines as `INT(11144/L2)` (line 1174), where 11144 is an empirically chosen constant representing available string space. The two-dimensional array `D$(L1,L2)` is then dimensioned at line 1176. The length of the largest line number in decimal is stored in `L` via `LEN STR$ L1` (line 1175) and used for column alignment in the editor display. ### Text Editor Logic The editor at lines 500–895 operates around a current-line pointer `L3` and a mode flag `F`: | F value | Mode | | --- | --- | | 0 | Normal navigation/entry | | 1 | Display refresh only | | 2 | Move — source line selected, awaiting destination | | 3 | Move — performing the block copy | Entering “M” as the first character of a line number (line 620) sets move mode. The block-move routine (lines 800–895) copies a user-specified quantity of lines from source `L4` to destination `L3`, adjusting iteration direction (`Z1` is ±1) to avoid overwriting source data when the destination overlaps the source. Lines beginning with “/” are treated as paragraph separators: the LPrint module (line 1364) outputs a blank line for them rather than their content, and they are skipped in the display module (line 200) when checking for blank lines. ### Display Subroutine The display subroutine (lines 100–390) renders lines bottom-up on the screen. Starting from line `L3` and screen row `Z2=16`, it walks backward through `D$()`, printing each non-blank line. When `L2<29` (short records), it also prints the line number at column 0 for reference. Any screen rows not filled by document content are cleared by printing `S$` (the blank string) at those rows in the cleanup loop at lines 300–320. ### Printer Output The LPrint routine (lines 1300–1395) first prints a header consisting of the save name `X$`, a separator, and the title `T$`. Document lines are printed with `TAB VAL "30"-L2`, centering or indenting the text based on the chosen record length. Empty lines (`D$(Z)=S$(TO L2)`) are silently skipped (line 1362), while lines starting with “/” produce a blank printed line (line 1380). ### Save Mechanism The Save option (lines 1400–1440) uses `SAVE X$` to save the entire program — including the populated `D$()` array — back to tape. The save name `X$` can be updated at save time, defaulting to the previously stored name. This means a saved file reloads as a complete self-contained document. ### Anomalies and Notes - Line 1130 checks `INKEY$<>CHR$ VAL "118"`; character 118 is the ENTER key token on this system — effectively waiting for ENTER to proceed past the caution screen. - Line 1262 is referenced in the tab-entry input validation (line 1272 `GOTO VAL "1262"`) but the displayed listing shows the loop body starting at 1265; line 1262 does not appear explicitly, meaning the `GOTO` re-enters the `FOR` loop mid-body, re-prompting for the same tab without re-printing the label — a deliberate tight retry loop. - The initialization cold-start path (lines 1450–1490) calls the init subroutine, sets `L1` and `L2` to zero (skipping any caution prompt at line 1110), and falls directly into the Save option, prompting the user for a program name before any file is set up — likely intentional to establish the tape name early. ## Source Code ``` 1 REM E£RND7 FAST )5 ;SGN ' ? ' GOSUB %KTAN 5~~ 67RND6~~RNDTAN 10 REM DOCUMENT PROCESSOR 11 REM VU-WRITE / 2C.01 / 3069 15 REM PROPRIETARY TO: 16 REM MANAGEMENT SERVICES CO. 100 REM ** DISPLAY ** 150 LET Z1=L3 160 LET Z2=VAL "16" 170 LET Z3=A 180 IF L2L3 THEN GOTO 250 210 IF L2<29 THEN PRINT AT Z2,0;Z1;" "; 220 PRINT AT Z2,Z3;D$(Z1); 230 LET Z2=Z2-1 250 IF Z2<0 OR F=1 THEN RETURN 260 LET Z1=Z1-1 290 IF Z1 THEN GOTO 200 300 FOR Z=Z2 TO 0 STEP -1 310 PRINT AT Z,0;S$; 320 NEXT Z 390 RETURN 500 REM ** TEXT ** 550 CLS 560 IF NOT L3 THEN GOTO VAL "600" 570 LET F=A 575 GOSUB VAL "100" 600 PRINT AT VAL "18",A;R$;"--LINE";S$( TO VAL "28");"ENTER LINE NO.: (1<=";L1;",M,E)"," ";R$( TO L);S$( TO VAL "31"-L) 605 LET F=A 610 INPUT Q$ 615 IF Q$="" THEN GOTO VAL "650" 620 IF Q$(B)<>"M" THEN GOTO VAL "625" 621 LET F=VAL "2" 622 LET Q$=Q$(VAL "2" TO ) 625 IF NOT LEN Q$ OR LEN Q$>L THEN GOTO VAL "610" 626 IF Q$(B)="E" THEN GOTO VAL "1220" 630 LET Q=A 632 FOR Z=B TO LEN Q$ 634 IF Q$(Z)<"0" OR Q$(Z)>"9" THEN LET Q=B 636 NEXT Z 638 IF NOT Q THEN IF VAL Q$>=B AND VAL Q$<=L1 THEN GOTO VAL "640" 639 GOTO VAL "610" 640 LET L3=VAL Q$ 645 GOTO VAL "680" 650 IF L3=L1 THEN GOTO VAL "610" 655 IF NOT F THEN LET F=B 660 IF L3 AND FS$( TO L2) THEN GOTO VAL "670" 665 GOTO VAL "675" 670 RAND USR VAL "16514" 675 LET L3=L3+B 680 IF F=VAL "3" THEN GOTO VAL "800" 685 GOSUB VAL "100" 690 PRINT AT VAL "19",VAL "5";L3 692 IF FL4 THEN LET Z2=L1-L3+B 830 PRINT L3,TAB VAL "6";"QTY TO MOVE: (0<=";Z2;")";S$;AT VAL "21",A;R$( TO L) 840 INPUT Q 842 IF QZ2 THEN GOTO VAL "840" 844 IF NOT Q THEN GOTO VAL "600" 850 LET Q=Q-B 860 IF L3<=L4 THEN GOTO VAL "870" 862 LET L3=L3+Q 864 LET L4=L4+Q 866 LET Q=-Q 868 LET Z1=-Z1 870 FOR Z=A TO Q STEP Z1 875 LET D$(L3+Z)=D$(L4+Z) 880 NEXT Z 890 IF L3<=L4 THEN LET L3=L3+Q 895 GOTO VAL "780" 900 REM ** TITLE ** 910 CLS 920 PRINT "OPTION: TITLE";AT VAL "11",A;"TITLE: ";T$;AT VAL "21",B;R$( TO 24) 930 INPUT Q$ 940 IF Q$="" THEN GOTO VAL "1220" 950 LET T$=Q$ 990 GOTO VAL "1220" 1000 REM ** MENU ** 1010 RAND USR VAL "16530" 1015 FAST 1020 CLS 1025 PRINT TAB VAL "11";"""VU-WRITE""",TAB VAL "7";"DOCUMENT PROCESSOR",TAB B;R$,,TAB VAL "11";"1. FILE",,TAB VAL "11";"2. WRITE",,TAB VAL "11";"3. LPRINT ",,TAB VAL "11";"4. SAVE ",,,,TAB VAL "6";"ENTER OPTION: (1<=4)",,TAB B;R$,TAB VAL "10";"** STATUS **",,," SAVED: ";X$," TITLE: ";T$;" LINES: ";L1,"LENGTH: ";L2," TAB 1: ";T(B)," TAB 2: ";T(B+B) 1030 PAUSE P 1050 LET Q$=INKEY$ 1060 IF Q$<"1" OR Q$>"4" THEN GOTO VAL "1030" 1080 CLS 1090 GOTO VAL Q$*VAL "100"+C 1100 REM ** FILE ** 1110 IF NOT L1 THEN GOTO VAL "1140" 1115 PRINT "OPTION: SET FILE";AT VAL "8",VAL "6";"%C%A%U%T%I%O%N: THIS OPTION",,TAB VAL "3";"WILL CLEAR AND REDEFINE THE",,TAB VAL "5";"TEXT FILE. PRESS %E%N%T%E%R",,TAB VAL "10";" TO CONTINUE." 1120 PAUSE VAL "60" 1130 IF INKEY$<>CHR$ VAL "118" THEN GOTO C 1140 CLS 1145 PRINT "OPTION: SET FILE";AT VAL "11",VAL "11";"(1<=30)";AT VAL "10",VAL "4";"ENTER RECORD LENGTH: "; 1150 INPUT Q 1152 IF QVAL "30" THEN GOTO VAL "1150" 1154 PRINT Q 1156 POKE VAL "16507",Q 1160 PRINT AT VAL "21",VAL "7";"CORRECT? (%END-Y-N)" 1162 PAUSE P 1164 IF INKEY$="Y" THEN GOTO VAL "1170" 1166 IF INKEY$="N" THEN GOTO VAL "1140" 1168 GOTO C 1170 CLEAR 1171 GOSUB VAL "1180" 1172 LET L3=A 1173 LET L2=PEEK VAL "16507" 1174 LET L1=INT (VAL "11144"/L2) 1175 LET L=LEN STR$ L1 1176 DIM D$(L1,L2) 1179 GOTO C 1180 LET A=VAL "0" 1181 LET B=VAL "1" 1182 LET C=VAL "1000" 1183 LET P=VAL "40000" 1184 LET R$="------------------------------" 1190 DIM S$(VAL "32") 1191 DIM T(VAL "2") 1192 DIM T$(VAL "24") 1193 DIM X$(VAL "8") 1199 RETURN 1200 REM ** TAB ** 1210 IF NOT L1 THEN GOTO C 1220 CLS 1225 PRINT TAB VAL "11";"""VU-WRITE""",TAB VAL "7";"DOCUMENT PROCESSOR",TAB B;R$,,TAB VAL "12";"1. TABS",,TAB VAL "12";"2. TITLE",,TAB VAL "12";"3. TEXT",,,,TAB VAL "6";"ENTER OPTION: (1<=3)",,TAB B;R$ 1230 PAUSE P 1240 IF INKEY$="3" THEN GOTO VAL "500" 1242 IF INKEY$="2" THEN GOTO VAL "900" 1244 IF INKEY$<>"1" THEN GOTO C 1250 CLS 1255 PRINT "OPTION: SET TABS";AT VAL "10",VAL "2";"ENTER TAB POSITIONS (0<=";L2;")",,TAB VAL "5";"TAB 1:"," TAB 2:" 1260 FOR Z=B TO B+B 1265 PRINT AT VAL "12",Z*VAL "12";"%<" 1270 INPUT T(Z) 1272 IF T(Z)L2 THEN GOTO VAL "1262" 1274 PRINT AT VAL "12",Z*VAL "12";T(Z) 1278 NEXT Z 1280 PRINT AT VAL "21",VAL "9";"CORRECT? (Y-N)" 1285 PAUSE P 1290 IF INKEY$<>"N" THEN GOTO VAL "1220" 1295 GOTO VAL "1250" 1300 REM ** LPRINT ** 1310 IF NOT L1 THEN GOTO C 1320 PRINT "OPTION: LPRINT ";AT VAL "11",VAL "7";"OK TO PRINT? (N-Y)" 1330 PAUSE P 1340 IF INKEY$<>"Y" THEN GOTO C 1350 LPRINT X$,S$,T$,,,,, 1360 FOR Z=B TO L1 1362 IF D$(Z)=S$( TO L2) THEN GOTO VAL "1390" 1364 IF D$(Z,B)="/" THEN GOTO VAL "1380" 1370 LPRINT TAB VAL "30"-L2;D$(Z) 1375 GOTO VAL "1390" 1380 LPRINT 1390 NEXT Z 1395 GOTO C 1400 REM ** SAVE ** 1410 PRINT "OPTION: SAVE ";AT VAL "6",B;"ENTER PGM NAME: (";X$;",END)" 1412 INPUT Q$ 1414 IF Q$="END" THEN GOTO C 1416 IF Q$<>"" THEN LET X$=Q$ 1420 PRINT AT VAL "6",A;S$,TAB VAL "7";"PGM NAME: ";X$,,,,TAB VAL "5";"PRESS %E%N%T%E%R WHEN READY",,TAB VAL "8";" OR %END TO ABORT." 1422 PAUSE P 1424 IF INKEY$<>CHR$ VAL "118" THEN GOTO C 1430 SAVE X$ 1440 GOTO C 1450 REM ** INIT ** 1460 GOSUB VAL "1180" 1470 LET L1=A 1480 LET L2=A 1490 GOTO VAL "1400" ```