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 1autostart.
Machine Code Usage
Two separate machine code blocks are installed by BASIC:
- 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
C1–C6, with a busy-wait loop and strobe generation. - Tape header reader (lines 3995–3997): 45 bytes POKEd at
loadat=61952. The routine usesIN A,(255)(EAR port) and writes results into a 17-byte buffer atwh=61997(loadat+45). BASIC POKEs a flag value of 4 intowhbefore each call, and reads it back after: ifwt>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:
| wt | Line | Type | Format |
|---|---|---|---|
| 0 | 4001 | PROG | name + PROG, vars length, autostart info |
| 1 | 4002 | Numeric array | name + letter + () + length |
| 2 | 4003 | String array | name + letter + $() + length |
| 3 | 4004 | CODE | name + 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+wtdispatches 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 TOloop padsw$to 3 characters with leading spaces before embedding it in the filename atz$(1,12 TO 14). - Printer detection at line 4005:
LET w=PEEK 26704=PEEK 26694stores the boolean result of a comparison directly into variablew— used to check whether the T-S 2040 printer ROM vector is in effect. - Self-saving with LINE autostart (line 9998):
SAVE "TapeLister" LINE 1saves the BASIC program so it auto-runs from line 1 on load. The precedingDIM 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 C1–C6 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 4006indirectly 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, andC5as symbolic names embedded in the DATA list. At runtime, BASIC reads these as the current numeric values of variablesC3,C1, andC5, so the machine code is patched with the user-configured port addresses — a neat self-modifying code pattern mediated through BASIC DATA.
Content
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.
