Header is a TS2068 utility that reads and displays the contents of cassette file-header blocks, reporting metadata such as file type, filename, data length, start address, and auto-start line number. It uses a 53-byte machine code routine POKEd into RAM at address 64000 that interfaces with the cassette hardware to read a 17-byte standard header block. Both screen and printer output are supported simultaneously via paired PRINT and LPRINT statements. The program decodes the Spectrum-compatible header format, recognizing all four block types: BASIC program (0), numeric array (1), character array (2), and bytes (3). Array variable names are recovered by masking the stored identifier byte with modulo-32 arithmetic and adding 96.
Program Analysis
Program Structure
The program is organized as a tight, single-pass loop across a handful of high-numbered lines (9969–9999), consistent with a utility designed to sit in upper memory without conflicting with user programs. The flow is:
- 9969 — Set up error trap, display splash/credit screen, wait for keypress.
- 9970 — Allocate memory, dimension the filename buffer
b$, and DATA-load the machine code routine. - 9971 — DATA statement holding the 53-byte machine code payload.
- 9975 — Prompt user to insert tape and press PLAY.
- 9982 — Call machine code; parse and display the decoded header fields.
- 9983 — Conditional branch: print array identifier for types 1 and 2.
- 9988 — Print data length; for type 3 (BYTES), print start address.
- 9991–9994 — For type 0 (BASIC), optionally print auto-start line and PROG/VARS length.
- 9995 — Loop back to
9982; theREMcontains dead code for an interactive stop prompt. - 9996 — Error handler:
ON ERR RESET : STOP— catches BREAK or tape errors and halts gracefully.
Machine Code Routine
The 53-byte routine is POKEd into address 64000 (FA00h) and invoked via RANDOMIZE USR a0. The DATA values decode to Z80 instructions that interface directly with the cassette hardware. Key observations from the byte sequence:
55—EX AF,AF'(preamble state save)62,0—LD A,0(select header block type)221,33,60,250—LD IX,FA3Ch(pointer to the 17-byte header buffer ata0+60)17,17,0—LD DE,17(header block length)205,14,250—CALL FA0Eh(self-referential call within the routine — actually calls the ROM tape-load routine via a trampoline, or is an internal subroutine)201—RET211,244/219,255—OUT (0F4h),A/IN A,(0FFh)— direct I/O port access for the TS2068 cassette interface203,191/203,255—RES 7,A/SET 7,A— bit manipulation of the port value251—EI(re-enable interrupts after cassette operation)243—DI(disable interrupts during sensitive I/O)241—POP AF/233—JP (HL)(indirect jump, likely into ROM tape routines)
After the call returns, the header data is read starting at a0+60 (FA3Ch): byte 0 is the file type s, bytes 1–10 are the 10-character filename stored into b$(), and subsequent bytes hold length, parameter 1, and parameter 2 words in little-endian format.
Header Block Decoding
The standard Spectrum-compatible cassette header is 17 bytes (plus flag and checksum bytes managed by the loader). The program maps the relevant fields as follows:
Offset from a0+60 | Field | BASIC variable |
|---|---|---|
| 0 | File type (0–3) | s |
| 1–10 | Filename (10 chars) | b$(1)…b$(10) |
| 11–12 | Data length (little-endian) | PEEK a + 256*PEEK(a+1) |
| 13–14 | Parameter 1 (start/auto-start) | b = PEEK(a+2)+256*PEEK(a+3) |
| 15–16 | Parameter 2 (PROG/VARS length) | PEEK(a+4)+256*PEEK(a+5) |
Key BASIC Idioms
Line 9982 uses the Boolean string multiplication idiom characteristic of Sinclair BASIC: ("BASIC PROGRAM: " AND (s=0)) evaluates to the string if the condition is true, or an empty string otherwise. The four such terms are concatenated with b$ to form the labelled filename line in a single expression, avoiding a multi-branch IF/THEN structure.
The ON ERR keyword (TS2068 extension, represented as { in source) is used at line 9969 to trap errors including BREAK, redirecting to line 9996 where RESET (another TS2068 extension) clears the error state before STOP.
Array Identifier Recovery (Line 9983)
For array types (s=1 numeric, s=2 character), the variable name byte stored in the header encodes the letter in bits 4–0. The expression b - 32*INT(b/32) + 96 computes (b MOD 32) + 96, mapping the stored value back to a lowercase ASCII letter. A "$" suffix is appended for character arrays using the Boolean string idiom.
Auto-Start Detection (Line 9993)
For BASIC files (s=0), parameter 1 holds the auto-start line number, or 32768 (8000h) if no auto-start is set. The program checks b <> 32768 before printing the auto-start line, correctly suppressing the field when absent.
Dead Code
Line 9995 contains a GO TO 9982 followed by a REM statement. The text inside the REM is the remnant of an earlier interactive prompt (asking whether to stop or read another header) that was replaced by the unconditional loop. The prompt logic — including a variable q$ and string comparisons for "STOP" — is entirely unreachable.
Memory Layout
CLEAR (a0-1) at line 9970 sets the RAMTOP to 63999, protecting the machine code area from 64000 upward against BASIC heap expansion. The machine code buffer starts at FA00h and the header data buffer is placed at FA3Ch (offset 60), neatly separated from the executable code.
Content
Source Code
9969 ON ERR GO TO 9996: CLS : PRINT "Header, a public-domain program published by T-S Horizons Dec. 1984 from T/S User Group in Nashville, TN via Bill Ferrebee."'''"The program reads and reports the contents of T/S 2068 cas- sette file-header blocks using screen and printer(if it's on.)": PRINT #0; FLASH 1;" Push any key to start ": PAUSE 0
9970 LET a0=64000: CLEAR (a0-1): DIM b$(10): LET a0=64000: RESTORE 9971: FOR a=a0 TO a0+52: READ b: POKE a,b: NEXT a
9971 DATA 55,62,0,221,33,60,250,17,17,0,205,14,250,201,33,252,0,205,34,250,58,33,250,211,244,219,255,203,191,211,255,251,201,0,243,245,219,255,203,255,211,255,219,244,50,33,250,62,1,211,244,241,233
9975 CLS : PRINT #0; FLASH 1;" Insert a tape and press PLAY Hit BREAK AT ANY TIME TO STOP "
9982 RANDOMIZE USR a0: CLS : LET a=a0+60: LET s=PEEK a: FOR c=1 TO 10: LET b$(c)=CHR$ PEEK (a+c): NEXT c: LET a$=("BASIC PROGRAM: " AND (s=0))+("NUMERIC ARRAY: " AND (s=1))+("CHARACTER ARRAY: " AND (s=2))+("BYTES: " AND (s=3))+b$: LPRINT 'a$: PRINT 'a$: LET a=a+c
9983 IF s=1 OR s=2 THEN LET b=PEEK (a+3): LET b=b-32*INT (b/32)+96: LET a$="Array i.d.: "+CHR$ b+("$" AND (s=2)): LPRINT a$: PRINT a$
9988 LET a$=" DATA LENGTH: "+STR$ (PEEK a+256*PEEK (a+1)): LPRINT a$: PRINT a$: LET b=PEEK (a+2)+256*PEEK (a+3): IF s=3 THEN LET a$="START ADDRESS: "+STR$ b: LPRINT a$: PRINT a$
9991 IF s THEN GO TO 9995
9993 IF b<>32768 THEN LET a$="AUTO START AT : "+STR$ b: LPRINT a$: PRINT a$
9994 LET a$="PROG/VARS LENGTH: "+STR$ (PEEK (a+4)+256*PEEK (a+5)): LPRINT a$: PRINT a$
9995 GO TO 9982: REM INPUT FLASH 1;"Enter STOP (key or word) to end OR push any other key to read another file header. ";q$: IF q$<>"STOP" AND q$<>"stop" AND q$<>" STOP " THEN GO TO 9975
9996 ON ERR RESET : STOP
9999 SAVE "Header5" LINE 9969
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
