VU-CALC for Printer is a BASIC loader and extension program that adds printer output capability to the VU-CALC spreadsheet application. It loads two binary code files (“VCALC” and “VCPRINT”) and initializes a set of entry-point addresses stored in variables (e1, e2, e3) that serve as USR jump vectors into machine code routines. A 162-byte Z80 machine code routine, embedded as DATA at line 9500 and POKEd into addresses 62000–62161, handles the actual printer interface, including column and line positioning using byte-pair encoding across two memory addresses. The program supports two output paths: a direct COPY command for the 2040 printer, or a configurable column/row print region routed through the custom machine code.
Program Structure
The program is organized into functional blocks by line-number ranges:
- Lines 5–60: Bootstrap — clears memory, sets display attributes, loads the two CODE files, and jumps to line 3200.
- Lines 1000–1200: Utility subroutines for file-name input (
a$) and screen/paper setup. - Lines 2000–2070: Printer selection and output routing — either COPY (2040 printer) or the custom machine code path.
- Lines 3000–3300: Main menu (Exit / Clear worksheet / Return to VU-CALC).
- Lines 3200–3201: Variable initialization block — sets up entry-point addresses and system pointers, then jumps to
USR e1. - Lines 4000, 5000: File save and string-to-value conversion helper.
- Lines 9000–9200: Error handler — reports column and row of the error using PEEK of system addresses.
- Lines 9300–9400: Reinitializer and SAVEr — writes all three binary components back to tape.
- Lines 9500–9620: Machine code DATA and POKEing loop for the printer routine.
Machine Code Integration
The 162-byte machine code routine is stored as decimal DATA at lines 9500–9520 and loaded into addresses 62000–62161 by the loop at lines 9600–9620. Three entry points within the VU-CALC CODE file are stored as integer variables: e1 (29764), e2 (29767), and e3 (29770). All returns from BASIC subroutines re-enter the spreadsheet engine via GO TO USR e2 or GO TO USR e3, making BASIC act purely as a callback layer around the machine code core.
The printer routine at 62000 is invoked by RANDOMIZE USR VAL "62006", skipping the first six bytes which hold configuration data (column low/high, line, and width low/high bytes) rather than executable code.
Column and Line Encoding
Lines 2025–2035 implement a two-byte little-endian encoding of pixel column and line values:
cc(column) is multiplied by 7 (pixel width per character column), then split: high byte POKEd to 62005, low byte to 62004.- The remaining width (
350 - cc) is similarly split into 62000 and 62001. - The line value
llis decoded from a two-character stringl$usingCODE l$(1)-64, with an optional tens component froml$(2)if it is a printable character, giving a compact way to enter two-digit row numbers via a single INPUT into aDIM l$(2)string.
Key BASIC Idioms
VAL "number"is used pervasively inGO TO,GO SUB, POKE addresses, and literal constants — a standard memory optimization that avoids storing the numeric value in the tokenized line.SGN PIevaluates to 1 and is used wherever the literal1is needed (e.g., BORDER color, array index).NOT PIevaluates to 0 and appears in the error-handler row decode and in the menu range check (a> NOT PImeansa>0).LN PIevaluates to approximately 1.1447…, which truncates to 1 in integer contexts — used at line 3201 asxcol + LN PIto computexrow = xcol + 1 = 32881without a literal 1.GO TO VAL "3e3"is scientific notation inside VAL, evaluating to 3000 — a compact loop-back idiom.
Variable Namespace and Uppercase Anomaly
Lines 2030 and 2035 mix l$ (lowercase) and L$ (uppercase) when referencing the same string variable. In Sinclair BASIC, variable names are case-insensitive for string arrays, so CODE L$(VAL "2") correctly accesses the second element of the same DIM l$(2) array declared at line 2020. Similarly, line 9300 uses uppercase variable names (BFRE, DCONTX, etc.) that duplicate lowercase equivalents from line 3201; in BASIC these are distinct numeric variables, but line 9300 is a standalone reinitializer that sets its own copies before jumping to USR E3.
Save/Restore Architecture
Line 9400 saves all three components to tape in sequence: the BASIC program itself as VCALC with LINE 10 autostart, the printer machine code block (62000, 162 bytes) as VCPRINT, and the main spreadsheet CODE (29328, 5225 bytes) also as VCALC CODE. The boot sequence at lines 10–60 loads them back in the same order. Line 9998 contains a SAVE command (char code 12 via the \\{12} escape) targeting 9500, — a partial save line likely used during development.
Error Reporting
The error handler at lines 9000–9200 decodes a two-byte row address stored at xrow (32881) into a spreadsheet-style alphabetic row label. The high byte b provides the tens letter (added to 64, giving ‘A’=65 for row 1–26), and the low byte a provides the units letter (added to 65). When b = NOT PI (i.e., b=0), only the single-character suffix is displayed by slicing d$(2 TO 2).
Source Code
5 REM VU-CALC for printer R.A.Hilsmann SMUG Bytes 8/88
10 CLEAR VAL "29327":BORDER SGN PI:INK VAL "9":LOAD "VCALC"CODE
60 LOAD "VCPRINT"CODE :GO TO VAL "3200"
1000 GO SUB VAL "1100":LOAD a$ CODE :CLS :GO TO USR e2
1100 GO SUB VAL "1200":PRINT ''''"ENTER data file name":INPUT a$:RETURN
1200 PAPER VAL "7":BORDER SGN PI:CLS :RETURN
2000 INPUT "print to 2040 Printer <y/n> ";a$
2010 IF a$="n" OR a$="N" THEN GO TO VAL "2020"
2015 IF a$="y" OR a$="Y" THEN GO TO VAL "2060"
2017 GO TO VAL "2000"
2020 DIM l$(VAL "2"):POKE VAL "23658", VAL "8":INPUT "Printout to which column ";cc:INPUT "Printout to which line ";l$
2025 LET cc=cc* VAL "7":LET ch= INT (cc/ VAL "256"):POKE VAL "62005",ch:LET cl=cc-(VAL "256"*ch):POKE VAL "62004",cl
2030 LET ll= VAL "(CODE l$(1)-64)":IF CODE L$(VAL "2")> VAL "32" THEN LET LL= VAL "26+(CODE L$(2)-64)"
2035 POKE VAL "62003",ll:LET cl= VAL "350"-cc:LET ch= INT (cl/ VAL "256"):LET cl=cl-(VAL "256"*ch):POKE VAL "62000",cl:POKE VAL "62001",ch
2050 RANDOMIZE USR VAL "62006":GO TO VAL "2070"
2060 COPY
2070 GO TO USR E3
3000 GO SUB VAL "1200":PRINT AT VAL "9", VAL "2";"ENTER 1 : EXIT PROGRAM"'' TAB VAL "9";"2 :CLEAR WORKSHEET"'' TAB VAL "9";"3 :RETURN TO VU-CALC":INPUT "OPTION? ";a
3010 IF a> NOT PI AND a< VAL "4" THEN GO TO VAL "3000+a*100"
3020 GO TO VAL "3e3"
3100 GO SUB VAL "1200":STOP
3200 DIM b$(VAL "100"):DIM c$(VAL "20"):DIM l$(VAL "2"):GO SUB VAL "1200"
3201 LET e1= VAL "29764":LET e2= VAL "29767":LET e3= VAL "29770":LET zz= VAL "34553":LET bfre= VAL "34562":LET dcontx= VAL "34140":LET xcol= VAL "32880":LET xrow=xcol+ LN PI:GO TO USR e1
3300 GO TO USR e2
4000 GO SUB VAL "1100":SAVE a$ CODE zz, VAL "(PEEK bfre+256* PEEK (bfre+1)-zz)":CLS :GO TO USR e2
5000 LET c$= STR$ VAL b$:GO TO USR dcontx
9000 GO SUB VAL "1200":PRINT ''''"ERROR was at"''"COLUMN "; PEEK xcol+ SGN PI
9100 LET a= PEEK xrow:LET b= INT (a/ VAL "26"):LET a=a-b* VAL "26":LET d$= CHR$ (b+ VAL "64")+ CHR$ (a+ VAL "65"):IF b= NOT PI THEN LET d$=d$(VAL "2" TO VAL "2")
9200 PRINT "ROW ";d$:INPUT "Press ENTER TO CONTINUE ";x$:GO TO USR e2
9300 CLEAR VAL "29327":DIM B$(VAL "100"):DIM C$(VAL "20"):LET BFRE= VAL "34562":LET DCONTX= VAL "34140":LET XCOL= VAL "32880":LET XROW= VAL "32881":LET E1= VAL "29764":LET E2= VAL "29767":LET E3= VAL "29770":GO TO USR E3
9400 CLEAR :SAVE "VCALC" LINE 10:SAVE "VCPRINT"CODE 62000,162:SAVE "VCALC"CODE 29328,5225
9500 DATA 10,1,0,21,84,0,213,229,197,245,237,91,48,242,33,13,135,237,75,50,242,197,237,75,52,242,219,127,203,103,32,250,126,254,96,32,63,237,161,234,92,242,24,63,126,254,33,204,177,242,254,95,204,180,242
9510 DATA 254,64,204,183,242,254,35,204,186,242,254,34,204,189,242,254,58,204,192,242,254,39,204,195,242,254,36,204,198,242,254,59,204,201,242,254,38,204,204,242,254,63,204,207,242,211,127,237,161,234,74,242,62,13,211
9520 DATA 127,62,10,211,127,193,16,7,241,193,225,209,195,74,116,197,25,24,149,62,12,201,62,9,201,62,18,201,62,15,201,62,14,201,62,20,201,62,13,201,62,27,201,62,11,201,62,10,201,62,24,201
9600 RESTORE 9500
9610 FOR X=62000 TO 62161
9620 READ a:POKE x,a:NEXT x
9998 \{12}9500,
9999 REM **VU-CALC for full-size printer** 1.LOAD "VCALC"CODE 2.GO TO 9500 3.GO TO 9400
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
