Tape Lister – Centronics

Date: 198x
Type: Program
Platform(s): TS 2068

This program reads and catalogs tape header information from cassette tapes, storing up to 128 entries in a string array and displaying each header as it is read. A 45-byte machine code routine is POKEd into RAM at address 61952 and called via RANDOMIZE USR to interface directly with the tape hardware; a working address at offset 45 (wh=61997) serves as a shared communication buffer between the Z80 routine and BASIC. The program decodes all four standard file types — PROG (with autostart LINE detection), numeric arrays, string arrays, and CODE blocks — formatting each entry with name, type, length, and address information. Printer support covers both the TS 2040 and any Centronics-compatible printer via a Z80 PIO chip configured with OUT commands and a secondary machine code block at 61594. The collected headers can be saved back to tape as a named DATA array, with the entry count embedded in the filename.


Program Analysis

Program Structure

The program is divided into several functional regions with non-contiguous line numbering reflecting distinct phases:

  • Lines 1–3, 200: Startup banner, description display, and initial STOP prompting the user to stop the tape recorder.
  • Lines 399–422: Memory reservation (CLEAR 59903), printer setup subroutine supporting both T-S 2040 and Centronics/PIO configurations.
  • Lines 450–452: Graphics copy/print helper using LPRINT escape sequences and a USR call (likely for a screen dump).
  • Lines 3995–4010: Main tape reading loop — machine code installation, array initialization, and the header-decode/display/log cycle.
  • Lines 4500–4600: STOP and bulk display of all collected entries.
  • Lines 9990–9999: Save section — pads the entry count into the filename, saves the array, and re-saves the BASIC program itself with LINE 1 autostart.

Machine Code Usage

Two separate machine code blocks are installed by BASIC:

  1. Centronics PIO printer driver (lines 417–418): 26 bytes POKEd into address 61594–61619. The DATA includes Z80 opcodes for IN/OUT operations using the configurable port addresses C1C6, with a busy-wait loop and strobe generation.
  2. Tape header reader (lines 3995–3997): 45 bytes POKEd at loadat=61952. The routine uses IN A,(255) (EAR port) and writes results into a 17-byte buffer at wh=61997 (loadat+45). BASIC POKEs a flag value of 4 into wh before each call, and reads it back after: if wt>3, no valid header was found and the loop retries after a pause.

The tape routine at 61952 communicates via a shared memory area beginning at wh. The layout mirrors the standard tape header structure: type byte at offset 0, filename at offsets 1–10, length at offsets 11–12, parameter 1 at offsets 13–14, parameter 2 at offsets 15–16.

Header Decoding

After a successful read, line 4000 assembles the 10-character filename from wh+1 to wh+10, then dispatches on the type byte wt via a computed GO TO 4001+wt:

wtLineTypeFormat
04001PROGname + PROG, vars length, autostart info
14002Numeric arrayname + letter + () + length
24003String arrayname + letter + $() + length
34004CODEname + CODE, length, load address

For PROG files, autostart detection compares PEEK (wh+14) to 128: a value of 128 means no autostart LINE, otherwise the LINE number is reconstructed from wa. The result is written into columns 36 onward of the 64-character array row using a string slice assignment with a boolean AND idiom.

Key BASIC Idioms

  • Boolean string selection: ("Autostart LINE "+STR$ wa) AND (PEEK (wh+14)<>128) evaluates to the string if the condition is true, or an empty string if false. This is a standard Sinclair-dialect trick exploiting the fact that AND with a zero numeric operand yields 0 (empty string).
  • Computed GO TO: GO TO 4001+wt dispatches to one of four handler lines based on the file type — a clean jump table using arithmetic on line numbers.
  • Left-padded string counter (lines 9990–9991): A GO TO loop pads w$ to 3 characters with leading spaces before embedding it in the filename at z$(1,12 TO 14).
  • Printer detection at line 4005: LET w=PEEK 26704=PEEK 26694 stores the boolean result of a comparison directly into variable w — used to check whether the T-S 2040 printer ROM vector is in effect.
  • Self-saving with LINE autostart (line 9998): SAVE "TapeLister" LINE 1 saves the BASIC program so it auto-runs from line 1 on load. The preceding DIM z$(1,1) minimizes the variable area included in the save.

Printer Configuration

The printer setup subroutine (lines 410–422) supports two printer types. For the T-S 2040, line 414 copies two bytes from the ROM’s default LPRINT vector addresses (26693–26694) into the system’s current vector pointers (26703–26704). For Centronics printers via a Z80 PIO, lines 418–422 install the machine code driver and initialize the PIO channels with OUT instructions using configurable port addresses, then redirect the LPRINT vector to the installed routine at address 240*256+154 = 61594.

The display at line 415 shows current port values and invites the user to modify variables C1C6 and re-run line 415 before continuing — an interactive configuration loop gated by STOP at line 416.

Array Storage

DIM z$(129,64) allocates a 129-row by 64-column string array. Row 1 is used as a header record ("TapeHdrs /128"), with the entry count eventually written into columns 12–14. Rows 2–129 hold individual tape header descriptions. The counter ww starts at 2 and increments with each successfully decoded header.

The final save at line 9995 uses SAVE z$(1, TO 10) DATA z$(), which takes the first 10 characters of row 1 as the filename — matching the "TapeHdrs " prefix — and saves the entire array as a DATA file.

Notable Anomalies

  • Line 4010 references GO TO 4006 indirectly via a forward branch from line 3999 (IF wt>3 THEN GO TO 4006), but line 4006 does not exist in the listing. This is a well-known technique: on this platform, a GO TO targeting a non-existent line falls through to the next available line, which would be 4010 — the retry/pause loop. This is intentional.
  • Lines 450–452 reference a variable LDat (for a screen dump USR call) that is never assigned in the visible listing, suggesting these lines are only reachable from a separate part of the program not shown, or require manual setup.
  • The DATA at line 417 contains the literal token identifiers C3, C1, and C5 as symbolic names embedded in the DATA list. At runtime, BASIC reads these as the current numeric values of variables C3, C1, and C5, so the machine code is patched with the user-configured port addresses — a neat self-modifying code pattern mediated through BASIC DATA.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Tape Lister – Centronics

Source Code

    1 CLS : PRINT FLASH 1;AT 3,8;" STOP RECORDER "
    2 PRINT ''"  This program accesses tape     header information.  It creates a file of up to 128 entries as  well as displaying the results."''"  It is configured for printing  to any CENTRONICS printer."
    3 PAUSE 600
  200 STOP 
  399 CLEAR 59903
  410 REM  Printer Setup 
  411 LET C1=130: LET C2=131: LET C3=128: LET C4=129: LET C5=16: LET C6=64
  412 CLS : PRINT ,,"  Please select PRINTER."''"   1  T-S 2040"''"   2  CENTRONICS w/PIO, IM1"
  413 INPUT "Which? ";Printer: IF Printer=2 THEN GO TO 415
  414 POKE 26703,PEEK 26693: POKE 26704,PEEK 26694: RETURN 
  415 PRINT AT 3,1;" PRINTER ADDRESSES "''"  Printer: c1","Command: c2"'"   Status: c3","Command: c4"'" BUSY bit: c5","STB Bit: c6"''"  PRESENT VALUES "''"  c1=";c1,"c2=";c2'"  c3=";c3,"c4=";c4'"  c5=";c5,"c6=";c6
  416 PRINT '"  Input new values as required  and GO TO 415, else CONTINUE.": STOP 
  417 RESTORE 417: DATA 8,219,C3,230,C5,32,12,8,211,C1,175,243,211,C3,47,211,C3,251,201,205,9,32,56,-23,207,12
  418 FOR N=61594 TO 61619: READ Byte: POKE N,Byte: NEXT N
  420 REM  init PIO 
  421 OUT C2,255: OUT C2,0: OUT C2,7: OUT C4,255: OUT C4,255-C6: OUT C4,7: OUT C3,64
  422 POKE 26703,154: POKE 26704,240: GO TO 999
  450 LPRINT ("\l/" AND Zoom);"\lG";STR$ Margin;";32;22;";STR$ Aspect;"\lZ";: RANDOMIZE USR LDat: RETURN : REM  COPY 
  451 LET Zoom=0: LET Margin=40: LET aspect=3: GO TO 450
  452 LET Zoom=1: LET Margin=40: LET aspect=2: GO TO 450
 3995 DATA 62,0,55,221,33,45,242,17,17,0,243,245,219,255,203,255,211,255,219,244,50,44,242,62,1,211,244,241,205,252,0,58,44,242,211,244,219,255,203,191,211,255,251,201,0
 3996 LET loadat=61952: LET wh=loadat+45: REM wh=61997
 3997 RESTORE 3995: FOR n=loadat TO loadat+44: READ wc: POKE n,wc: NEXT n: DIM z$(129,64): LET z$(1)="TapeHdrs      /128": LET ww=2: LET w$="": CLS : PRINT AT 10,6;" START RECORDER ": PRINT AT 0,0;
 3998 POKE wh,4: RANDOMIZE USR loadat: LET wt=PEEK wh: IF wt>3 THEN GO TO 4010
 3999 LET wl=PEEK (wh+11)+256*PEEK (wh+12): LET wc=PEEK (wh+14): LET wa=PEEK (wh+13)+256*wc: LET wv=PEEK (wh+15)+256*PEEK (wh+16): LET wt=PEEK wh: IF wt>3 THEN GO TO 4006
 4000 LET w$="": FOR n=wh+1 TO wh+10: LET w$=w$+CHR$ PEEK n: NEXT n: GO TO 4001+wt
 4001 LET Z$(WW)=w$+" PROG "+STR$ wv+", VARS "+STR$ (wl-wv): LET z$(ww,36 TO )=(("Autostart LINE "+STR$ wa) AND (PEEK (wh+14)<>128))+("No Autostart" AND (PEEK (wh+14)=128)): GO TO 4005
 4002 LET Z$(WW)=w$+" "+CHR$ (wc-32)+"(), "+STR$ wl: GO TO 4005
 4003 LET Z$(WW)=w$+" "+CHR$ (wc-96)+"$(), "+STR$ wl: GO TO 4005
 4004 LET Z$(WW)=w$+" CODE "+STR$ wl+" from "+STR$ wa
 4005 PRINT AT 0,0;z$(ww): LPRINT z$(ww);CHR$ 10;: LET ww=ww+1: LET w=PEEK 26704=PEEK 26694
 4010 PAUSE 300: GO TO 3998
 4500 STOP 
 4600 FOR n=1 TO ww: PRINT z$(n): NEXT n: STOP 
 9990 LET w$=STR$ (ww-1)
 9991 IF LEN w$<3 THEN LET w$=" "+w$: GO TO 9991
 9994 LET z$(1,12 TO 14)=w$
 9995 SAVE z$(1, TO 10) DATA z$()
 9996 STOP 
 9998 DIM z$(1,1): SAVE "TapeLister" LINE 1
 9999 GO TO 9998
    1 CLS : PRINT FLASH 1;AT 3,8;" STOP RECORDER "
    2 PRINT ''"  This program accesses tape     header information.  It creates a file of up to 128 entries as  well as displaying the results."''"  It is configured for printing  to any CENTRONICS printer."
    3 PAUSE 600
  200 STOP 
  399 CLEAR 59903
  410 REM  Printer Setup 
  411 LET C1=130: LET C2=131: LET C3=128: LET C4=129: LET C5=16: LET C6=64
  412 CLS : PRINT ,,"  Please select PRINTER."''"   1  T-S 2040"''"   2  CENTRONICS w/PIO, IM1"
  413 INPUT "Which? ";Printer: IF Printer=2 THEN GO TO 415
  414 POKE 26703,PEEK 26693: POKE 26704,PEEK 26694: RETURN 
  415 PRINT AT 3,1;" PRINTER ADDRESSES "''"  Printer: c1","Command: c2"'"   Status: c3","Command: c4"'" BUSY bit: c5","STB Bit: c6"''"  PRESENT VALUES "''"  c1=";c1,"c2=";c2'"  c3=";c3,"c4=";c4'"  c5=";c5,"c6=";c6
  416 PRINT '"  Input new values as required  and GO TO 415, else CONTINUE.": STOP 
  417 RESTORE 417: DATA 8,219,C3,230,C5,32,12,8,211,C1,175,243,211,C3,47,211,C3,251,201,205,9,32,56,-23,207,12
  418 FOR N=61594 TO 61619: READ Byte: POKE N,Byte: NEXT N
  420 REM  init PIO 
  421 OUT C2,255: OUT C2,0: OUT C2,7: OUT C4,255: OUT C4,255-C6: OUT C4,7: OUT C3,64
  422 POKE 26703,154: POKE 26704,240: GO TO 999
  450 LPRINT ("\l/" AND Zoom);"\lG";STR$ Margin;";32;22;";STR$ Aspect;"\lZ";: RANDOMIZE USR LDat: RETURN : REM  COPY 
  451 LET Zoom=0: LET Margin=40: LET aspect=3: GO TO 450
  452 LET Zoom=1: LET Margin=40: LET aspect=2: GO TO 450
 3995 DATA 62,0,55,221,33,45,242,17,17,0,243,245,219,255,203,255,211,255,219,244,50,44,242,62,1,211,244,241,205,252,0,58,44,242,211,244,219,255,203,191,211,255,251,201,0
 3996 LET loadat=61952: LET wh=loadat+45: REM wh=61997
 3997 RESTORE 3995: FOR n=loadat TO loadat+44: READ wc: POKE n,wc: NEXT n: DIM z$(129,64): LET z$(1)="TapeHdrs      /128": LET ww=2: LET w$="": CLS : PRINT AT 10,6;" START RECORDER ": PRINT AT 0,0;
 3998 POKE wh,4: RANDOMIZE USR loadat: LET wt=PEEK wh: IF wt>3 THEN GO TO 4010
 3999 LET wl=PEEK (wh+11)+256*PEEK (wh+12): LET wc=PEEK (wh+14): LET wa=PEEK (wh+13)+256*wc: LET wv=PEEK (wh+15)+256*PEEK (wh+16): LET wt=PEEK wh: IF wt>3 THEN GO TO 4006
 4000 LET w$="": FOR n=wh+1 TO wh+10: LET w$=w$+CHR$ PEEK n: NEXT n: GO TO 4001+wt
 4001 LET Z$(WW)=w$+" PROG "+STR$ wv+", VARS "+STR$ (wl-wv): LET z$(ww,36 TO )=(("Autostart LINE "+STR$ wa) AND (PEEK (wh+14)<>128))+("No Autostart" AND (PEEK (wh+14)=128)): GO TO 4005
 4002 LET Z$(WW)=w$+" "+CHR$ (wc-32)+"(), "+STR$ wl: GO TO 4005
 4003 LET Z$(WW)=w$+" "+CHR$ (wc-96)+"$(), "+STR$ wl: GO TO 4005
 4004 LET Z$(WW)=w$+" CODE "+STR$ wl+" from "+STR$ wa
 4005 PRINT AT 0,0;z$(ww): LPRINT z$(ww);CHR$ 10;: LET ww=ww+1: LET w=PEEK 26704=PEEK 26694
 4010 PAUSE 300: GO TO 3998
 4500 STOP 
 4600 FOR n=1 TO ww: PRINT z$(n): NEXT n: STOP 
 9990 LET w$=STR$ (ww-1)
 9991 IF LEN w$<3 THEN LET w$=" "+w$: GO TO 9991
 9994 LET z$(1,12 TO 14)=w$
 9995 SAVE z$(1, TO 10) DATA z$()
 9996 STOP 
 9998 DIM z$(1,1): SAVE "TapeLister" LINE 1
 9999 GO TO 9998

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

People

No people associated with this content.

Scroll to Top