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

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.
 1010 REM 
 1020 FAST 
 1030 DIM F$(9500,4)
 1040 LET A=VAL "9500"
 1050 LET B=VAL "256"
 1060 LET C=VAL "0"
 1070 LET D=VAL "1"
 1080 LET L=PEEK 16400+B*PEEK 16401
 1090 LET A=A*4+5
 1100 POKE L+D,A-B*INT (A/B)
 1110 POKE L+2,INT (A/B)
 1120 POKE L+3,D
 1130 LET A=A-3
 1140 POKE L+4,A-B*INT (A/B)
 1150 POKE L+5,INT (A/B)
 1160 POKE L+6,C
 1170 LET Z$="                                "
 1180 LET PL=C
 1190 LET CF=C
 1200 LET X$="N"
 1210 LET HEAD=C
 1220 LET FORMAT=C
 1230 LET M=VAL "1300"
 1240 LET BL=VAL "3050"
 1250 LET F$( TO 20)="VU-FILE+"
 1260 SLOW 
 1270 RAND USR VAL "17986"
 1280 CLS 
 1290 GOTO VAL "1540"
 1300 CLS 
 1310 SLOW 
 1320 PRINT AT 2,8;"%P%S%I%O%N% %C%O%M%P%U%T%E%R%S"
 1330 PRINT "   MODIFICATIONS BY TIM WARD"
 1340 PRINT AT 5,12;"VU-FILE+",,,,,
 1350 PRINT " 1)...SET RECORD FORMAT",,,
 1360 PRINT " 2)...ENTER VU-FILE",,,
 1370 PRINT " 3)...SAVE VU-FILE AND/OR DATA",,,
 1380 PRINT " 4)...SET PRINTER FORMATS",,,
 1390 PRINT " 5)...CLEAR ALL DATA/FORMATS",,,,,
 1400 PRINT "PRESS 1 TO 5 FOR DESIRED OPTION"
 1410 LET I$=INKEY$
 1420 IF I$<"1" OR I$>"5" THEN GOTO VAL "1410"
 1430 LET PH=HEAD
 1440 LET PAGE=C
 1450 LET PL=C
 1460 CLS 
 1470 IF I$="1" THEN GOTO VAL "1520"
 1480 IF I$="2" THEN GOTO VAL "1540"
 1490 IF I$="3" THEN GOTO VAL "1670"
 1500 IF I$="4" THEN GOTO VAL "1810"
 1510 IF I$="5" THEN RUN 
 1520 RAND USR VAL "17989"
 1530 GOTO M
 1540 LET A=USR VAL "19157"
 1550 IF A=D THEN GOTO VAL "1580"
 1560 IF A=VAL "2" THEN GOTO VAL "1630"
 1570 GOTO M
 1580 PRINT AT C,C;F$( TO 20);"   ";PEEK VAL "16603"
 1590 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"))
 1600 PRINT TAB VAL "20";PEEK VAL "16567"
 1610 LET A=USR VAL "19160"
 1620 GOTO VAL "1550"
 1630 GOSUB VAL "2510"
 1640 SLOW 
 1650 LET A=USR VAL "17983"
 1660 GOTO VAL "1550"
 1670 CLS 
 1680 FAST 
 1690 GOSUB BL
 1700 PRINT "ENTER FILE NAME"
 1710 INPUT N$
 1720 GOSUB BL
 1730 PRINT "SET PLAYER TO RECORD"
 1740 PRINT "BEFORE PRESSING NEWLINE"
 1750 LET F$( TO 20)=N$
 1760 INPUT I$
 1770 RAND USR VAL "19154"
 1780 SAVE N$
 1790 IF USR VAL "19151"<>C THEN STOP 
 1800 GOTO M
 1810 IF FORMAT=C THEN GOTO VAL "1880"
 1820 GOSUB BL
 1830 PRINT "CHANGE P)AGE FORMAT D)ATA FORMATOR R)ETURN TO MAIN MENU" 
 1840 INPUT I$
 1850 IF I$="P" THEN GOTO VAL "1880"
 1860 IF I$="D" THEN GOTO VAL "2260"
 1870 IF I$="R" THEN GOTO M
 1880 GOSUB BL
 1890 PRINT "ENTER LINE LENGTH (1-132)"
 1900 INPUT LL
 1910 GOSUB BL
 1920 PRINT "ENTER PAGE LENGTH (1-112)"
 1930 INPUT FL
 1940 GOSUB BL
 1950 PRINT "ENTER NBR OF LINES FOR TOP AND  BOTTOM MARGINS (0-111)"
 1960 INPUT MARGIN
 1970 LET LTP=FL-MARGIN
 1980 GOSUB BL
 1990 PRINT "HOW MANY HEADER LINES DO YOU    WANT PRINTED? (0-10)"
 2000 INPUT HL
 2010 IF HL<>C THEN LET HEAD=D
 2020 IF HL=C THEN GOTO VAL "2280"
 2030 DIM H(HL)
 2040 DIM H$(HL,LL)
 2050 FOR Z=D TO HL
 2060 GOSUB BL
 2070 PRINT "EXPAND HEADER LINE ";Z;" (Y-N)"
 2080 INPUT I$
 2090 LET H(Z)=C
 2100 IF I$="Y" THEN LET H(Z)=D
 2110 LET CM=LL
 2120 IF I$="Y" THEN LET CM=LL/2
 2130 GOSUB BL
 2140 PRINT "CENTER HEADER LINE ";Z;" (Y-N)"
 2150 INPUT J$
 2160 GOSUB BL
 2170 PRINT "ENTER HEADER LINE ";Z,"(";CM;" CHARS MAX)"
 2180 INPUT I$
 2190 IF J$="N" THEN LET H$(Z, TO CM)=I$
 2200 IF J$="Y" THEN LET H$(Z,INT (CM-LEN I$)/2 TO CM)=I$
 2210 NEXT Z
 2220 GOSUB BL
 2230 PRINT "PRINT PAGE NUMBERS (Y-N)"
 2240 INPUT X$
 2250 IF FORMAT=D THEN GOTO VAL "1810"
 2255 CLS 
 2260 RAND USR VAL "17992"
 2270 CLS 
 2280 GOSUB BL
 2290 PRINT "ENTER NBR OF PRINT LINES PER    RECORD (1-10)"
 2300 INPUT RPL
 2310 GOSUB BL
 2320 PRINT "ENTER NBR OF FIELDS IN RECORD   (1-19)"
 2330 INPUT NF
 2340 DIM E(NF,3)
 2350 FOR Z=D TO NF
 2360 GOSUB BL
 2370 PRINT "ENTER LENGTH OF FIELD ";Z
 2380 INPUT E(Z,D)
 2390 GOSUB BL
 2400 PRINT "ENTER LINE TO PRINT FIELD ";Z,"ON (0-"+STR$ RPL+")"
 2410 INPUT E(Z,2)
 2420 GOSUB BL
 2430 PRINT "ENTER STARTING COLUMN FOR FIELD NBR ";Z;" (1-"+STR$ (LL-E(Z,D)+D)+")"
 2440 INPUT E(Z,3)
 2450 NEXT Z
 2460 FAST 
 2470 RAND USR VAL "19154"
 2480 IF FORMAT=D THEN GOTO VAL "1810"
 2490 LET FORMAT=D
 2500 GOTO M
 2510 FAST 
 2520 IF PL=C THEN GOSUB VAL "2950"
 2530 LET CF=CF+D
 2540 IF CF=D AND (FL-(MARGIN/2))-PL<RPL THEN GOSUB VAL "2860"
 2550 IF PH=D THEN GOSUB VAL "2660"
 2560 IF CF=D THEN DIM R$(RPL,LL)
 2570 IF E(CF,2)=C THEN GOTO VAL "2590"
 2580 LET R$(E(CF,2),E(CF,3) TO E(CF,3)+E(CF,D)-D)=F$(21 TO (E(CF,D)+20))
 2590 IF CF<NF THEN RETURN 
 2600 LET CF=C
 2610 FOR I=D TO RPL
 2620 LET P$=R$(I)
 2630 GOSUB VAL "2820"
 2640 NEXT I
 2650 RETURN 
 2660 FOR X=D TO HL
 2670 IF H(X)=C THEN GOTO VAL "2770"
 2680 LET P$=CHR$ 14
 2690 RAND CODE "P"
 2700 RAND USR VAL "10660"
 2710 LET P$=H$(X,D TO (LL/2))
 2720 GOSUB VAL "2830"
 2730 LET P$=CHR$ 15
 2740 RAND USR VAL "10660"
 2750 LET PL=PL+2
 2760 GOTO VAL "2790"
 2770 LET P$=H$(X)
 2780 GOSUB VAL "2820"
 2790 NEXT X
 2800 LET PH=C
 2810 RETURN 
 2820 RAND CODE "P"
 2830 RAND USR VAL "10655"
 2840 LET PL=PL+D
 2850 IF PL-D<>FL-(MARGIN/2) THEN RETURN 
 2860 LET CTR=PL+D
 2870 LET PAGE=PAGE+D
 2880 FOR Z=CTR TO FL
 2890 DIM W$(D,LL)
 2900 IF Z=FL-D AND X$="Y" THEN LET W$(D,LL/2 TO )=STR$ PAGE
 2910 LET P$=W$(D)
 2920 RAND USR VAL "10655"
 2930 LET PL=PL+D
 2940 NEXT Z
 2950 LET PL=C
 2960 LET PH=HEAD
 2970 LET P$=""
 2980 FOR Z=D TO MARGIN/2
 2990 RAND CODE "P"
 3000 RAND USR VAL "10655"
 3010 LET PL=PL+D
 3020 NEXT Z
 3030 LET PH=HEAD
 3040 RETURN 
 3050 PRINT AT 13,C;Z$
 3060 PRINT AT 12,C;Z$
 3070 PRINT AT 11,31;" "
 3080 RETURN 
 3090 SAVE "VU-FILE%+"
 3100 IF USR 19151=0 THEN RUN 
 3110 SAVE "1022%0"
 3120 LIST 

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

Scroll to Top