VU-CALC for Printer

Products: VU-CALC
Developer(s): Rudy A. Hilsmann
Date: 1988
Type: Program
Platform(s): TS 2068

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 ll is decoded from a two-character string l$ using CODE l$(1)-64, with an optional tens component from l$(2) if it is a printable character, giving a compact way to enter two-digit row numbers via a single INPUT into a DIM l$(2) string.

Key BASIC Idioms

  • VAL "number" is used pervasively in GO TO, GO SUB, POKE addresses, and literal constants — a standard memory optimization that avoids storing the numeric value in the tokenized line.
  • SGN PI evaluates to 1 and is used wherever the literal 1 is needed (e.g., BORDER color, array index).
  • NOT PI evaluates to 0 and appears in the error-handler row decode and in the menu range check (a> NOT PI means a>0).
  • LN PI evaluates to approximately 1.1447…, which truncates to 1 in integer contexts — used at line 3201 as xcol + LN PI to compute xrow = xcol + 1 = 32881 without 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).

Related Products

Spread-sheet approach to fiscal analysis, engineering calculations, statistical analysis and more. Formula approach locates and calculates complex tables of numbers...

Image Gallery

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.