--- title: "Header5" id: 56046 type: "computer_media" slug: "header5" url: "http://localhost/computer_media/header5/" markdown_url: "http://localhost/computer_media/header5.md" published_at: "2024-07-14T20:40:00+00:00" modified_at: "2026-03-31T09:16:08+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-olti.png" excerpt: "A machine-code-assisted cassette utility reads tape file headers and reports their contents — file type, name, length, and auto-start line — to screen and printer." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Bill Ferrebee" slug: "bill-ferrebee" taxonomy: "indiv" url: "http://localhost/indiv/bill-ferrebee/" genre: - name: "Header" slug: "header" taxonomy: "genre" url: "http://localhost/type/header/" - name: "Tape" slug: "tape" taxonomy: "genre" url: "http://localhost/type/tape/" media_contents: - id: 56041 title: "CATUG Tape 0.0 0001" type: "computer_media" url: "http://localhost/computer_media/catug-tape-0-0-0001/" media_type: "Program" programmers: - name: "Bill Ferrebee" slug: "bill-ferrebee" taxonomy: "indiv" url: "http://localhost/indiv/bill-ferrebee/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Header5%20%28198x%29%28Ferrebee%2C%20Bill%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240714-olti.png" media_type_tags: "Header, Tape" --- 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: 1. **9969** — Set up error trap, display splash/credit screen, wait for keypress. 2. **9970** — Allocate memory, dimension the filename buffer `b$`, and DATA-load the machine code routine. 3. **9971** — DATA statement holding the 53-byte machine code payload. 4. **9975** — Prompt user to insert tape and press PLAY. 5. **9982** — Call machine code; parse and display the decoded header fields. 6. **9983** — Conditional branch: print array identifier for types 1 and 2. 7. **9988** — Print data length; for type 3 (BYTES), print start address. 8. **9991–9994** — For type 0 (BASIC), optionally print auto-start line and PROG/VARS length. 9. **9995** — Loop back to `9982`; the `REM` contains dead code for an interactive stop prompt. 10. **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 at `a0+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` — `RET` - `211,244` / `219,255` — `OUT (0F4h),A` / `IN A,(0FFh)` — direct I/O port access for the TS2068 cassette interface - `203,191` / `203,255` — `RES 7,A` / `SET 7,A` — bit manipulation of the port value - `251` — `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. ## 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 ```