VU-File Patch

This file is part of and Timex Sinclair Public Domain Library Tape 1005. Download the collection to get this file.
Developer(s): Tim Ward
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Database

This program is an extension module for the VU-FILE database system, designed to be merged into an existing VU-FILE program by deleting lines 52 and above and then merging. It provides a five-option menu covering record format setup, data entry, cassette save, printer format configuration, and data clearing. The printer formatting routines handle page headers, top/bottom margins, page numbering, and field layout across multiple print lines per record, with expanded (double-width via CHR$ 14/15 control codes) header line support. Several USR calls delegate core database logic to machine code routines at addresses such as 17986, 19157, 19154, and 10655, while PEEK of system variables at addresses like 16400/16401 is used to locate the start of the BASIC program area for direct memory patching of the DIM F$ array descriptor.


Program Analysis

Program Structure

The program is structured as a merge module for an existing VU-FILE database application. The instructions in lines 1000–1010 explicitly state that lines 52 and above of VU-FILE should be deleted before merging. The module then provides a complete replacement for the upper half of the combined program.

  1. Lines 1020–1260: Initialisation — dimensions the main data array F$, patches the array descriptor in memory, sets global variables, and seeds the first 20 characters of F$ with “VU-FILE+”.
  2. Lines 1270–1290: Calls machine code to initialise hardware, then jumps to the main menu.
  3. Lines 1300–1510: Main menu — presents five options and dispatches via a series of IF/GOTO chains.
  4. Lines 1520–1660: Option handlers for format setup (USR 17989), data entry loop (USR 19157/19160), and print-to-printer (USR 17983).
  5. Lines 1670–1800: Save routine — accepts a filename, sets tape player, calls USR 19154 to prepare, then uses SAVE.
  6. Lines 1810–2500: Printer format configuration — collects page dimensions, margins, header lines, page numbering preference, and per-field layout data into arrays H, H$, and E.
  7. Lines 2510–3040: Print subroutines — GOSUB 2510 is the main record-printing engine; subordinate routines handle header printing, page-break and margin logic, and individual line output via USR 10655.
  8. Lines 3050–3080: BL (blank) subroutine — clears three screen lines at rows 11–13 used for prompting.
  9. Lines 3090–3120: Tape save and LIST — saves the program under two names and lists it.

Memory Patching of the DIM Descriptor

Lines 1080–1160 perform direct surgery on the F$ array descriptor in memory. The program calculates the address of the array header using PEEK 16400 + 256 * PEEK 16401 (the ZX81 VARS system variable), then writes the correct byte-length fields into the descriptor at offsets L+1 through L+6. This is necessary because the DIM F$(9500,4) at line 1030 allocates 9500 rows of 4 characters each, but the intent is to use it as 9500 rows of 4 bytes — the patches rewrite the dimension descriptors to match the intended record layout expected by the machine code routines.

The arithmetic uses B=256 and the standard low-byte/high-byte decomposition pattern: A - B*INT(A/B) for the low byte and INT(A/B) for the high byte, avoiding the MOD operator (not available in ZX81 BASIC).

Machine Code Interface

All heavy lifting is delegated to machine code routines. The BASIC acts as a configuration and control shell.

AddressCalled fromPurpose (inferred)
179861270Hardware/display initialisation
179891520Set record format (Option 1)
191571540VU-FILE data entry; returns status in A
191601610Advance to next record in data entry loop
179831650Print/output records
191541770, 2470Prepare for tape save
191511790, 3100Post-save status check
179922260Data format setup
106552830, 2920, 3000Send line P$ to printer
106602700, 2740Send expanded header line to printer

The RAND USR idiom (rather than LET x = USR) is used when the return value is not needed, discarding the result cleanly without a variable assignment.

An unusual pattern appears at lines 2690, 2700, 2820, and 2830: RAND CODE "P" immediately before RAND USR. CODE "P" evaluates to 80 (ASCII for ‘P’), so RAND 80 seeds the random number generator as a side effect; the real purpose appears to be passing the ASCII value of “P” (the printer stream number or a parameter) to the machine code routine that follows — a creative way to communicate a constant to an MC routine without dedicated variable overhead.

Print Formatting Engine

The subroutine starting at line 2510 is the record printer. It is called once per field (driven by the MC data-entry loop at 1650) and accumulates field data from F$(21 TO ...) into a line buffer array R$(RPL, LL). When all NF fields have been processed (CF=NF), it outputs RPL lines to the printer and resets.

Page break detection at line 2540 checks whether the remaining lines on the page are fewer than RPL, triggering the bottom-margin and new-page routine at line 2860. Expanded (double-width) header lines are bracketed by CHR$ 14 and CHR$ 15 (the ZX81 expand/normal control codes) written into P$ before calling the printer MC routine.

Key BASIC Idioms

  • VAL "number" in GOTO/GOSUB: Used throughout (e.g. GOTO VAL "1540") as a memory optimisation; storing the target as a string literal saves bytes compared to a numeric constant in the tokenised line.
  • Named constants via variables: B=256, C=0, D=1 are set once and reused everywhere, saving tokenised bytes on repeated literals.
  • BL variable for subroutine address: LET BL=3050 at line 1240 stores the line number of the blank-screen subroutine; GOSUB BL then uses a variable, which is shorter than GOSUB VAL "3050" when called many times.
  • M variable for main menu return: LET M=1300 acts as a named entry point for returning to the menu, used with GOTO M.
  • Inverse video title: Line 1320 uses zmakebas %X escapes to display “PSION COMPUTERS” in inverse video, identifying the original software vendor.

Anomalies and Points of Interest

  • Line 2850 has condition IF PL-D <> FL-(MARGIN/2) THEN RETURN inside what is also the entry point of the page-break subroutine (line 2860). The GOSUB 2820/GOSUB 2830 path falls through to this check; the unconditional fall-through from line 2850 into 2860 when the condition is false means the page-break logic immediately executes — this appears intentional but makes the control flow non-obvious.
  • Line 2900 uses W$(D, LL/2 TO ) — an open-ended slice to the end of the string — for placing the page number; this is valid ZX81 BASIC slice syntax.
  • The FORMAT flag (lines 1220, 2250, 2480–2490) distinguishes first-time format entry from subsequent editing, routing back to the format change menu (1810) after data-format setup if a format already existed.
  • Lines 3090–3120 save the program under two filenames — "VU-FILE+" (with inverse + for auto-run) and "10220" (also with inverse final character) — before issuing LIST, suggesting a development/distribution workflow where the author distributed both a named and a line-numbered copy.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10211 – 10251.

Related Products

Timex repackage of VU-FILE.
High-speed storage and retrieval program. Access personal or business files almost instantly. Design or format files to suit specific needs...

Related Articles

Related Content

Image Gallery

VU-File Patch

Source Code

1000 REM DELETE LINES 52+ FROM VU-FILE AND MERGE THESE LINES OF BASIC IN WITH LINES 50 AND 51 OFVU-FILE.  CONTACT TIM WARD IF YOU HAVE ANY QUESTIONS ABOUT THIS PGM.
\n1010 REM 
\n1020 FAST 
\n1030 DIM F$(9500,4)
\n1040 LET A=VAL "9500"
\n1050 LET B=VAL "256"
\n1060 LET C=VAL "0"
\n1070 LET D=VAL "1"
\n1080 LET L=PEEK 16400+B*PEEK 16401
\n1090 LET A=A*4+5
\n1100 POKE L+D,A-B*INT (A/B)
\n1110 POKE L+2,INT (A/B)
\n1120 POKE L+3,D
\n1130 LET A=A-3
\n1140 POKE L+4,A-B*INT (A/B)
\n1150 POKE L+5,INT (A/B)
\n1160 POKE L+6,C
\n1170 LET Z$="                                "
\n1180 LET PL=C
\n1190 LET CF=C
\n1200 LET X$="N"
\n1210 LET HEAD=C
\n1220 LET FORMAT=C
\n1230 LET M=VAL "1300"
\n1240 LET BL=VAL "3050"
\n1250 LET F$( TO 20)="VU-FILE+"
\n1260 SLOW 
\n1270 RAND USR VAL "17986"
\n1280 CLS 
\n1290 GOTO VAL "1540"
\n1300 CLS 
\n1310 SLOW 
\n1320 PRINT AT 2,8;"%P%S%I%O%N% %C%O%M%P%U%T%E%R%S"
\n1330 PRINT "   MODIFICATIONS BY TIM WARD"
\n1340 PRINT AT 5,12;"VU-FILE+",,,,,
\n1350 PRINT " 1)...SET RECORD FORMAT",,,
\n1360 PRINT " 2)...ENTER VU-FILE",,,
\n1370 PRINT " 3)...SAVE VU-FILE AND/OR DATA",,,
\n1380 PRINT " 4)...SET PRINTER FORMATS",,,
\n1390 PRINT " 5)...CLEAR ALL DATA/FORMATS",,,,,
\n1400 PRINT "PRESS 1 TO 5 FOR DESIRED OPTION"
\n1410 LET I$=INKEY$
\n1420 IF I$<"1" OR I$>"5" THEN GOTO VAL "1410"
\n1430 LET PH=HEAD
\n1440 LET PAGE=C
\n1450 LET PL=C
\n1460 CLS 
\n1470 IF I$="1" THEN GOTO VAL "1520"
\n1480 IF I$="2" THEN GOTO VAL "1540"
\n1490 IF I$="3" THEN GOTO VAL "1670"
\n1500 IF I$="4" THEN GOTO VAL "1810"
\n1510 IF I$="5" THEN RUN 
\n1520 RAND USR VAL "17989"
\n1530 GOTO M
\n1540 LET A=USR VAL "19157"
\n1550 IF A=D THEN GOTO VAL "1580"
\n1560 IF A=VAL "2" THEN GOTO VAL "1630"
\n1570 GOTO M
\n1580 PRINT AT C,C;F$( TO 20);"   ";PEEK VAL "16603"
\n1590 PRINT PEEK VAL "16565"+B*PEEK VAL "16566";TAB VAL "17";INT (VAL "100"*(PEEK VAL "18585"+B*PEEK VAL "16586")/(PEEK VAL "16583"+B*PEEK VAL "16584"))
\n1600 PRINT TAB VAL "20";PEEK VAL "16567"
\n1610 LET A=USR VAL "19160"
\n1620 GOTO VAL "1550"
\n1630 GOSUB VAL "2510"
\n1640 SLOW 
\n1650 LET A=USR VAL "17983"
\n1660 GOTO VAL "1550"
\n1670 CLS 
\n1680 FAST 
\n1690 GOSUB BL
\n1700 PRINT "ENTER FILE NAME"
\n1710 INPUT N$
\n1720 GOSUB BL
\n1730 PRINT "SET PLAYER TO RECORD"
\n1740 PRINT "BEFORE PRESSING NEWLINE"
\n1750 LET F$( TO 20)=N$
\n1760 INPUT I$
\n1770 RAND USR VAL "19154"
\n1780 SAVE N$
\n1790 IF USR VAL "19151"<>C THEN STOP 
\n1800 GOTO M
\n1810 IF FORMAT=C THEN GOTO VAL "1880"
\n1820 GOSUB BL
\n1830 PRINT "CHANGE P)AGE FORMAT D)ATA FORMATOR R)ETURN TO MAIN MENU" 
\n1840 INPUT I$
\n1850 IF I$="P" THEN GOTO VAL "1880"
\n1860 IF I$="D" THEN GOTO VAL "2260"
\n1870 IF I$="R" THEN GOTO M
\n1880 GOSUB BL
\n1890 PRINT "ENTER LINE LENGTH (1-132)"
\n1900 INPUT LL
\n1910 GOSUB BL
\n1920 PRINT "ENTER PAGE LENGTH (1-112)"
\n1930 INPUT FL
\n1940 GOSUB BL
\n1950 PRINT "ENTER NBR OF LINES FOR TOP AND  BOTTOM MARGINS (0-111)"
\n1960 INPUT MARGIN
\n1970 LET LTP=FL-MARGIN
\n1980 GOSUB BL
\n1990 PRINT "HOW MANY HEADER LINES DO YOU    WANT PRINTED? (0-10)"
\n2000 INPUT HL
\n2010 IF HL<>C THEN LET HEAD=D
\n2020 IF HL=C THEN GOTO VAL "2280"
\n2030 DIM H(HL)
\n2040 DIM H$(HL,LL)
\n2050 FOR Z=D TO HL
\n2060 GOSUB BL
\n2070 PRINT "EXPAND HEADER LINE ";Z;" (Y-N)"
\n2080 INPUT I$
\n2090 LET H(Z)=C
\n2100 IF I$="Y" THEN LET H(Z)=D
\n2110 LET CM=LL
\n2120 IF I$="Y" THEN LET CM=LL/2
\n2130 GOSUB BL
\n2140 PRINT "CENTER HEADER LINE ";Z;" (Y-N)"
\n2150 INPUT J$
\n2160 GOSUB BL
\n2170 PRINT "ENTER HEADER LINE ";Z,"(";CM;" CHARS MAX)"
\n2180 INPUT I$
\n2190 IF J$="N" THEN LET H$(Z, TO CM)=I$
\n2200 IF J$="Y" THEN LET H$(Z,INT (CM-LEN I$)/2 TO CM)=I$
\n2210 NEXT Z
\n2220 GOSUB BL
\n2230 PRINT "PRINT PAGE NUMBERS (Y-N)"
\n2240 INPUT X$
\n2250 IF FORMAT=D THEN GOTO VAL "1810"
\n2255 CLS 
\n2260 RAND USR VAL "17992"
\n2270 CLS 
\n2280 GOSUB BL
\n2290 PRINT "ENTER NBR OF PRINT LINES PER    RECORD (1-10)"
\n2300 INPUT RPL
\n2310 GOSUB BL
\n2320 PRINT "ENTER NBR OF FIELDS IN RECORD   (1-19)"
\n2330 INPUT NF
\n2340 DIM E(NF,3)
\n2350 FOR Z=D TO NF
\n2360 GOSUB BL
\n2370 PRINT "ENTER LENGTH OF FIELD ";Z
\n2380 INPUT E(Z,D)
\n2390 GOSUB BL
\n2400 PRINT "ENTER LINE TO PRINT FIELD ";Z,"ON (0-"+STR$ RPL+")"
\n2410 INPUT E(Z,2)
\n2420 GOSUB BL
\n2430 PRINT "ENTER STARTING COLUMN FOR FIELD NBR ";Z;" (1-"+STR$ (LL-E(Z,D)+D)+")"
\n2440 INPUT E(Z,3)
\n2450 NEXT Z
\n2460 FAST 
\n2470 RAND USR VAL "19154"
\n2480 IF FORMAT=D THEN GOTO VAL "1810"
\n2490 LET FORMAT=D
\n2500 GOTO M
\n2510 FAST 
\n2520 IF PL=C THEN GOSUB VAL "2950"
\n2530 LET CF=CF+D
\n2540 IF CF=D AND (FL-(MARGIN/2))-PL<RPL THEN GOSUB VAL "2860"
\n2550 IF PH=D THEN GOSUB VAL "2660"
\n2560 IF CF=D THEN DIM R$(RPL,LL)
\n2570 IF E(CF,2)=C THEN GOTO VAL "2590"
\n2580 LET R$(E(CF,2),E(CF,3) TO E(CF,3)+E(CF,D)-D)=F$(21 TO (E(CF,D)+20))
\n2590 IF CF<NF THEN RETURN 
\n2600 LET CF=C
\n2610 FOR I=D TO RPL
\n2620 LET P$=R$(I)
\n2630 GOSUB VAL "2820"
\n2640 NEXT I
\n2650 RETURN 
\n2660 FOR X=D TO HL
\n2670 IF H(X)=C THEN GOTO VAL "2770"
\n2680 LET P$=CHR$ 14
\n2690 RAND CODE "P"
\n2700 RAND USR VAL "10660"
\n2710 LET P$=H$(X,D TO (LL/2))
\n2720 GOSUB VAL "2830"
\n2730 LET P$=CHR$ 15
\n2740 RAND USR VAL "10660"
\n2750 LET PL=PL+2
\n2760 GOTO VAL "2790"
\n2770 LET P$=H$(X)
\n2780 GOSUB VAL "2820"
\n2790 NEXT X
\n2800 LET PH=C
\n2810 RETURN 
\n2820 RAND CODE "P"
\n2830 RAND USR VAL "10655"
\n2840 LET PL=PL+D
\n2850 IF PL-D<>FL-(MARGIN/2) THEN RETURN 
\n2860 LET CTR=PL+D
\n2870 LET PAGE=PAGE+D
\n2880 FOR Z=CTR TO FL
\n2890 DIM W$(D,LL)
\n2900 IF Z=FL-D AND X$="Y" THEN LET W$(D,LL/2 TO )=STR$ PAGE
\n2910 LET P$=W$(D)
\n2920 RAND USR VAL "10655"
\n2930 LET PL=PL+D
\n2940 NEXT Z
\n2950 LET PL=C
\n2960 LET PH=HEAD
\n2970 LET P$=""
\n2980 FOR Z=D TO MARGIN/2
\n2990 RAND CODE "P"
\n3000 RAND USR VAL "10655"
\n3010 LET PL=PL+D
\n3020 NEXT Z
\n3030 LET PH=HEAD
\n3040 RETURN 
\n3050 PRINT AT 13,C;Z$
\n3060 PRINT AT 12,C;Z$
\n3070 PRINT AT 11,31;" "
\n3080 RETURN 
\n3090 SAVE "VU-FILE%+"
\n3100 IF USR 19151=0 THEN RUN 
\n3110 SAVE "1022%0"
\n3120 LIST 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top