ZPRINT-80 Printer Support v1.3

Products: ZPRINT-80
Date: 1985
Type: Program
Platform(s): TS 2068

This program configures “Zprint80,” a machine code Centronics printer driver for the TS2068, developed by Zebra Systems, Inc. in 1985. It allows the user to select from two memory-resident versions of the driver (base address 27000 or 64000), choose from seven supported dot-matrix printers (Gemini, Memotech, Panasonic, Epson, Spirit-80, Prowriter), and select from four printer interfaces (Aerco, Tasman, A&J). The configuration process patches specific bytes within the loaded machine code block at address 64000 — including linefeed mode, output width, interface jump vectors, and printer control (Pcontrl) blocks — before saving the customized code back to tape. A custom character set is loaded from tape at address 51200, and a user-defined function using PEEK 23635/23636 computes the start address of the BASIC program area to invoke machine code routines via RANDOMIZE USR.


Program Analysis

Program Structure

The program is organized into clearly delineated subroutines separated by REM comments. Execution begins at line 100 after an auto-run, proceeding through an introductory splash screen, tape load of the target machine code, and a series of interactive configuration menus. The main flow is linear with subroutine calls for display management, configuration patching, and tape save/verify.

Line RangePurpose
1–2Embedded machine code (REM line), DEF FN for program area address
10–90Display utilities: header bars, screen clear subroutine (line 60)
100–210Startup, character load, version selection, Zprnt code load
220–390Printer and interface selection menus
410–555Linefeed query, patch application, tape save, verify, completion
800–836Unused interface POKE subroutines (Aerco, Tasman, A&J)
900–940Page-flip / “press any key” subroutine
1000–1200Address declarations and version-dependent jump vector values
1300–1360DATA blocks: Pcontrl main and GRAF512 blocks per printer model
1500–1650Configuration mod application (calls 1000, 2000, 2200)
2000–2070Load main Pcontrl block from DATA into memory
2200–2250Load GRAF512 block from DATA into memory
2400–2440Save-error handler with retry
2600–2670Copyright splash screen with machine code calls
9900–9920Developer save routine (program + char set to tape)

Machine Code Integration

Line 1 is a REM statement containing an embedded machine code routine. The function FN a() defined at line 2 computes the address of this code as PEEK 23635 + 256*PEEK 23636 + 5, which reads the PROG system variable (the start of the BASIC program area) and adds 5 bytes to skip past the line header into the body of the REM. The code is invoked repeatedly via RANDOMIZE USR FN a() at lines 26, 45, 2630, and 2644, with different parameters pre-loaded into system variables 23728, 23729, and 23723 before each call. This parameterized calling convention allows the single embedded routine to perform multiple display functions (likely text rendering using the custom character set loaded at 51200).

The Zprnt80 machine code module itself is always loaded to address 64000 regardless of which version is selected; only during the final SAVE step are the correct base-address bytes POKEd at offsets 64169–64170 to relocate the saved image to 27000 or 64000 as appropriate.

Configuration Patching via POKE

After loading the Zprnt code, the subroutine at line 1500 patches specific bytes at fixed offsets within the 64000-based image:

  • LNFDaddr (64647): linefeed character (10 or 0)
  • WDTHaddr (64012): output column width (1–80)
  • jmplo / jmphi (65128–65129): low/high bytes of the interface dispatch jump vector
  • port (65136): port selection byte (set to 251 for Tasman-c variant)

Jump vector values differ between the lo (27000-base) and hi (64000-base) versions and are set by the declaration block at lines 1060–1086, conditioned on v$.

Pcontrl DATA Block Loading

Lines 1300–1360 define printer control block DATA using a simple sentinel protocol: value 254 signals the end of one sub-record (advance to the next 8-byte slot within the Mreset region), and 255 terminates the entire block. The main block loader at lines 2010–2070 iterates m from Mreset (64599) in steps of 8, POKEing bytes until sentinel 255 is reached. The GRAF512 block loader at lines 2210–2250 is simpler, writing sequentially from Mreset+32 until 255. Different DATA lines are selected via RESTORE before calling these subroutines, enabling per-printer customization without duplicating the loader logic.

Key BASIC Idioms and Techniques

  • The display clear subroutine at line 60 uses DIM a$(544+32) to create a string of spaces, then prints it starting at row 4, efficiently blanking the lower portion of the screen.
  • ON ERR GO TO 2400 at line 501 traps tape verify failures, with ON ERR RESET used to clear the handler after success or within the error handler itself.
  • Input validation at lines 260 and 335 uses CODE k$ comparisons against ASCII values rather than string comparisons, which is typical for tight key-range checks.
  • Line 435 converts lowercase to uppercase via CHR$ (CODE k$-32), providing case-insensitive Y/N input.
  • The variable name If (line 340) shadows the BASIC keyword IF; this is legal in Spectrum BASIC since variable names can begin with keyword letters as long as they are not standalone keywords — here IF as an identifier stores the interface number.

Unused Subroutines

The subroutines at lines 800–836 (Aerco, Tasman, A&J POKE routines) are defined but never called from the main flow; the equivalent logic is inlined directly in the configuration mod routine at lines 1530–1560. Line 820 contains GO TO 810 which falls into the Tasman block — this appears to be a vestige of an earlier structure where separate entry points were used.

Developer Save Routine

Lines 9900–9920 form a developer-only section that saves the BASIC program itself with LINE 100 auto-run and separately saves the custom character data from address 51200 (2000 bytes). These lines are not reachable from the normal program flow and serve purely as the authoring/mastering tool.

Anomalies and Notes

  • Lines 190 and 200 both load the selected Zprnt version to address 64000 regardless of choice — the distinction between lo and hi versions is deferred to the SAVE step (lines 480–490), where different relocation bytes are POKEd before saving.
  • Line 50 contains a bare RETURN that is unreachable in normal flow, sitting between the flashing-prompt logic and the GO SUB 60 subroutine. It appears to be a leftover fragment.
  • The PAUSE 4e4 at line 49 (approximately 444 seconds at 50 Hz) is effectively a very long wait rather than a true indefinite pause, functioning as a timeout before resetting the flash flag.

Content

Appears On

Related Products

Print grey-scale reproduction of color screen to an 80-column printer. Includes high and low memory TS 2068 and Spectrum versions....

Related Articles

Related Content

Image Gallery

Source Code

    1 REM !VAL \ FREE !CODE \ RETURN ( RETURN ( THEN \'. THEN AND *U\>"PEEK #( IF RESTORE !LEN \N!@ RESTORE !VAL \ FREE NEW o>& THEN % THEN gSTR$ RESTORE !VAL \ FREE LLIST RETURN 8 RETURN "TAB \ LLIST RESTORE FREE = OR RETURN \a8p\aSTR$ X COPY OR  \ao> THEN % THEN gSTR$ RESTORE !ATTR \ FREE LLIST RETURN (j CAT OR RESTORE FREE W!ATTR \ FREE RETURN z(u*TAB \r#s+ OR STR$ "TAB \ LLIST #STR$ OR RESTORE x RETURN  !!CODE \ THEN F PI GO SUB B"TAB \*TAB \ LPRINT PI GO SUB B"TAB \ LLIST STR$ !CODE \ THEN F*TAB \(PI GO SUB BA\ePI GO SUB B\ RETURN  #"TAB \ LLIST #>"PEEK >= DATA \:'! THEN ? THEN THEN - THEN ? THEN THEN ,T]!ATTR \ FREE RETURN ( FLASH OR RESTORE FREE VERIFY *TAB \r#s+"TAB \ LLIST #STR$ *TAB \ RETURN PI GO SUB B"TAB \ LLIST #>"PEEK >=\b LLIST #>"PEEK >=\bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    2 DEF FN a()=PEEK 23635+256*PEEK 23636+5
   10 BORDER 6: PAPER 6: CLS 
   12 LET x=4
   18 PRINT AT 0,0; PAPER x;"                                "
   20 PRINT AT 1,0; PAPER x;"                                "
   22 PRINT AT 2,0; PAPER x;"                                "
   23 PRINT AT 3,0; PAPER x;"                                "
   24 POKE 23728,1: POKE 23729,1: POKE 23723,2
   26 RANDOMIZE USR FN a()
   28 REM "Zprint80 Customization Program"
   30 PLOT 0,175: DRAW 255,0
   40 PLOT 0,144: DRAW 255,0
   42 LET f=1: GO SUB 60
   44 POKE 23728,11: POKE 23729,3: POKE 23723,3
   45 RANDOMIZE USR FN a()
   46 REM "STOP THE TAPE"
   47 FOR n=1 TO 6: BEEP .1,-30: PAUSE 5: NEXT n
   48 PRINT AT 14,0; FLASH f;"     Press ENTER to proceed"
   49 PAUSE 4e4: LET f=0
   50 RETURN 
   60 DIM a$(544+32)
   70 PRINT AT 4,0; FLASH f;a$
   80 PRINT AT 5,0;
   90 RETURN 
  100 REM  main start     
  101 GO SUB 2600: INK 2: LOAD "chars"CODE 51200: POKE 23607,199: INK 0
  110 GO SUB 10
  115 IF INKEY$>"" THEN GO TO 115
  120 GO SUB 60
  130 PRINT "    The purpose of this program is to allow you to configure    the 'Zprnt80' machine code to   work with your particular       printer and printer interface,  and set LPRINT/LLIST output     width appropriately for your    desired application."
  140 PRINT ,,"    'Zprnt80' comes in two      versions; one resides in low    memory at address 27000, the    other in high memory at address 64000.  Both versions are 1300  bytes long."
  150 GO SUB 900
  160 PRINT ,,,,,,"  So which version do you want  to LOAD from Tape and configure?",,,,,,"    1 = ""Zprnt-lo"" (base=27000)",,,"    2 = ""Zprnt-hi"" (base=64000)"
  170 PRINT AT 21,0; INVERSE 1;"       press no# to select      "
  180 LET k$=INKEY$: IF k$="" THEN GO TO 180
  181 IF k$<>"1" AND k$<>"2" THEN GO TO 180
  182 GO SUB 60
  184 PRINT AT 10,0;"     Start the tape so your","    selected 'Zprnt' machine","      code module can load"
  190 IF k$="1" THEN LOAD "Zprnt-lo"CODE 64000
  200 IF k$="2" THEN LOAD "Zprnt-hi"CODE 64000
  210 LET v$=k$
  220 GO SUB 60
  230 PRINT AT 7,0;"   Which printer do you use?"
  240 PRINT ,,,,"       1 = Gemini 10X","       2 = Gemini SG10","       3 = Memotech","       4 = Panasonic","       5 = Epson RX/FX","       6 = Spirit-80","       7 = Prowriter 8510"
  250 PRINT AT 21,0; INVERSE 1;"       press no# to select      "
  260 LET k$=INKEY$: IF CODE k$<49 OR CODE k$>55 THEN GO TO 260
  270 LET prntr=CODE k$-48
  300 GO SUB 60
  305 IF INKEY$>"" THEN GO TO 305
  310 PRINT AT 8,0;"     Which printer interface","           do you use?"
  315 PRINT AT 12,0;"          1 = Aerco","          2 = Tasman-b","          3 = Tasman-c","          4 = A & J"
  320 PRINT AT 21,0; INVERSE 1;"       press no# to select      "
  330 LET k$=INKEY$: IF k$="" THEN GO TO 330
  335 IF k$<>"1" AND k$<>"2" AND k$<>"3" AND k$<>"4" THEN GO TO 330
  340 LET If=CODE k$-48
  350 GO SUB 60
  360 PRINT AT 11,0;" What printing width would you"," like for LPRINT/LLIST output?"
  370 INPUT "Width?";k
  380 LET wdth=k
  390 IF wdth<1 OR wdth>80 THEN GO TO 370
  410 GO SUB 60
  420 PRINT AT 6,0;"   Many printers come from the  factory setup to provide an     automatic linefeed after each   carriage return.  Usually you   can enable/disable this auto    linefeed feature via a DIP      switch.  If auto-linefeed is    enabled on your printer you     will not want the 'Zprnt'       software to provide a linefeed  code."
  430 INPUT "Should 'Zprint80' provide a linefeed?(y or n)";k$
  435 IF CODE k$>97 THEN LET k$=CHR$ (CODE k$-32)
  440 IF k$<>"Y" AND k$<>"N" THEN GO TO 430
  450 IF k$="Y" THEN LET lnfd=10
  452 IF k$="N" THEN LET lnfd=0
  455 GO SUB 60
  456 PRINT AT 21,0; FLASH 1;"Please Wait, making mods to code"
  457 GO SUB 1500
  460 GO SUB 60
  470 PRINT AT 8,0;"   You have finished the        configuration process, so put a blank tape in your recorder,    start recording and press any   key to SAVE your custom version of the 'Zprnt' code."
  480 IF v$="1" THEN POKE 64169,134: POKE 64170,105: SAVE "zprnt-lo"CODE 64000,1300
  490 IF v$="2" THEN POKE 64169,14: POKE 64170,250: SAVE "zprnt-hi"CODE 64000,1300
  495 GO SUB 60
  500 PRINT AT 21,0; FLASH 1;"Rewind Tape and Play for VERIFY "
  501 ON ERR GO TO 2400
  502 PRINT AT 5,0;: INK 6: VERIFY ""CODE : INK 0
  503 ON ERR RESET 
  510 GO SUB 60
  520 PRINT AT 7,0;"  The Tape save was good.  You  now have a custom configured    version of the 'Zprnt' code on  Tape.  In the future use the    following command to LOAD it ..."
  530 IF v$="1" THEN PRINT ,,,," < LOAD ""zprnt-lo"" CODE 27000 >"  
  540 IF v$="2" THEN PRINT ,,,," < LOAD ""zprnt-hi"" CODE 64000 >"  
  550 PRINT AT 21,0; INVERSE 1;" Customization is now Complete  "
  555 GO TO 555
  799 STOP 
  800 REM  Aerco  
  802 POKE jmplo,aercolo
  804 POKE jmphi,aercohi
  806 RETURN 
  810 REM  Tasman 
  812 POKE jmplo,taslo
  813 POKE jmphi,tashi
  814 IF k$="3" THEN POKE port,251
  817 RETURN 
  820 GO TO 810
  830 REM  A & J  
  832 POKE jmplo,ajlo
  834 POKE jmphi,ajhi
  836 RETURN 
  900 REM  page flip   
  910 PRINT AT 21,0; INVERSE 1;"    press any key to continue   "
  920 IF INKEY$="" THEN GO TO 920
  930 GO SUB 60
  940 RETURN 
 1000 REM  Declarations  
 1005 LET Mreset=64599
 1010 LET lNFDaddr=64647
 1020 LET WDTHaddr=64012
 1030 LET jmplo=65128
 1040 LET jmphi=65129
 1050 LET port=65136
 1060 IF v$="1" THEN LET taslo=226
 1061 IF v$="1" THEN LET tashi=109
 1062 IF v$="2" THEN LET taslo=106
 1063 IF v$="2" THEN LET tashi=254
 1070 IF v$="1" THEN LET aercolo=2
 1071 IF v$="1" THEN LET aercohi=110
 1072 IF v$="2" THEN LET aercolo=138
 1073 IF v$="2" THEN LET aercohi=254
 1080 IF v$="1" THEN LET ajlo=20
 1082 IF v$="1" THEN LET ajhi=110
 1084 IF v$="2" THEN LET ajlo=156
 1086 IF v$="2" THEN LET ajhi=254
 1200 RETURN 
 1300 REM  Pcontrl Blocks  
 1310 DATA 2,27,64,254,4,27,76,192,3,254,3,27,65,6,254,3,27,65,8,255: REM common Main block
 1320 DATA 11,27,68,21,0,9,0,0,27,76,0,2,255: REM GRAF512 for Mem/Pan/Spir80
 1330 DATA 11,27,77,21,0,0,0,0,27,76,0,2,255: REM GRAF512 for Gem 10X/SG10
 1340 DATA 11,27,108,21,0,0,0,0,27,76,0,2,255: REM GRAF512 for Epson RX/FX
 1350 DATA 13,27,40,48,50,49,46,9,27,83,48,53,49,50,255: REM GRAF512 for Prowr 8510
 1360 DATA 2,27,62,254,6,27,83,48,57,54,48,254,4,27,84,49,50,254,4,27,84,49,54,255: REM Prowr Main block
 1500 REM  Cofiguration Mods  
 1502 GO SUB 1000
 1510 POKE LNFDaddr,lnfd
 1520 POKE WDTHaddr,wdth
 1530 IF IF=1 THEN POKE jmplo,aercolo: POKE jmphi,aercohi
 1540 IF IF=2 OR IF=3 THEN POKE jmplo,taslo: POKE jmphi,tashi
 1550 IF IF=3 THEN POKE port,251
 1560 IF IF=4 THEN POKE jmplo,ajlo: POKE jmphi,ajhi
 1570 IF prntr<=6 THEN RESTORE 1310
 1580 IF prntr=7 THEN RESTORE 1360
 1590 GO SUB 2000
 1600 IF prntr=3 OR prntr=4 OR prntr=6 THEN RESTORE 1320
 1610 IF prntr=1 OR prntr=2 THEN RESTORE 1330
 1620 IF prntr=7 THEN RESTORE 1350
 1630 IF prntr=5 THEN RESTORE 1340
 1640 GO SUB 2200
 1650 RETURN 
 2000 REM  Load Main Pcntrl Blck 
 2010 FOR m=Mreset TO Mreset+31 STEP 8
 2020 LET Daddr=m
 2030 READ n: IF n=254 THEN GO TO 2060
 2040 IF n=255 THEN RETURN 
 2050 POKE Daddr,n: LET Daddr=Daddr+1: GO TO 2030
 2060 NEXT m
 2070 RETURN 
 2200 REM  Load GRAF512 block 
 2210 LET m=Mreset+32
 2220 READ n
 2230 IF n=255 THEN RETURN 
 2240 POKE m,n: LET m=m+1
 2250 GO TO 2220
 2400 REM  Save Error  
 2410 ON ERR RESET 
 2420 INK 0: GO SUB 60
 2430 PRINT AT 10,0;"          SAVE was Bad!",,,"    Press ENTER to try again"
 2440 PAUSE 4e4: GO TO 460
 2600 REM  Copyrigt message 
 2610 BORDER 2: PAPER 2: INK 7: CLS 
 2620 POKE 23728,3: POKE 23729,2: POKE 23723,3
 2630 RANDOMIZE USR FN a()
 2640 REM "** Zprint80 **"
 2641 PLOT 10,125: DRAW 235,0: PLOT 10,162: DRAW 235,0
 2642 POKE 23728,8: POKE 23729,0: POKE 23723,2
 2644 RANDOMIZE USR FN a()
 2646 REM "   Centronics Printer Support"
 2650 PRINT AT 13,8;"Copyright \* 1985"
 2660 PRINT ,,"       ZEBRA SYSTEMS, INC."
 2670 RETURN 
 9900 REM  Save to Tape  
 9910 SAVE "customize" LINE 100
 9920 SAVE "chars"CODE 51200,2000

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

People

No people associated with this content.

Scroll to Top