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.
- 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 ofF$with “VU-FILE+”. - Lines 1270–1290: Calls machine code to initialise hardware, then jumps to the main menu.
- Lines 1300–1510: Main menu — presents five options and dispatches via a series of
IF/GOTOchains. - Lines 1520–1660: Option handlers for format setup (USR 17989), data entry loop (USR 19157/19160), and print-to-printer (USR 17983).
- Lines 1670–1800: Save routine — accepts a filename, sets tape player, calls USR 19154 to prepare, then uses
SAVE. - Lines 1810–2500: Printer format configuration — collects page dimensions, margins, header lines, page numbering preference, and per-field layout data into arrays
H,H$, andE. - Lines 2510–3040: Print subroutines —
GOSUB 2510is the main record-printing engine; subordinate routines handle header printing, page-break and margin logic, and individual line output via USR 10655. - Lines 3050–3080:
BL(blank) subroutine — clears three screen lines at rows 11–13 used for prompting. - 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.
| Address | Called from | Purpose (inferred) |
|---|---|---|
17986 | 1270 | Hardware/display initialisation |
17989 | 1520 | Set record format (Option 1) |
19157 | 1540 | VU-FILE data entry; returns status in A |
19160 | 1610 | Advance to next record in data entry loop |
17983 | 1650 | Print/output records |
19154 | 1770, 2470 | Prepare for tape save |
19151 | 1790, 3100 | Post-save status check |
17992 | 2260 | Data format setup |
10655 | 2830, 2920, 3000 | Send line P$ to printer |
10660 | 2700, 2740 | Send 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"inGOTO/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=1are set once and reused everywhere, saving tokenised bytes on repeated literals. BLvariable for subroutine address:LET BL=3050at line 1240 stores the line number of the blank-screen subroutine;GOSUB BLthen uses a variable, which is shorter thanGOSUB VAL "3050"when called many times.Mvariable for main menu return:LET M=1300acts as a named entry point for returning to the menu, used withGOTO M.- Inverse video title: Line 1320 uses zmakebas
%Xescapes 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 RETURNinside what is also the entry point of the page-break subroutine (line 2860). TheGOSUB 2820/GOSUB 2830path 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
FORMATflag (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 issuingLIST, suggesting a development/distribution workflow where the author distributed both a named and a line-numbered copy.
Content
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.
