Header Reader loads a 45-byte machine code routine into RAM starting at address 32765 and uses it to intercept and decode tape header blocks from the EAR port. The machine code temporarily disables interrupts, reads the header data byte by byte into memory, then returns control to BASIC for analysis. Once a header is captured, the program parses the 17-byte standard header structure: the type byte (0=BASIC, 1=number array, 2=character array, 3=code), a 10-character filename, block length, load address, and for BASIC programs, the autostart line number and variable area offset. The DEF FN g(x) function reads two consecutive PEEK values to reconstruct 16-bit little-endian integers for length and address fields. A type value of 4 is used as a sentinel to detect that no valid header was found, triggering a flashing error message.
Program Structure
The program is organized into four logical sections:
- Initialization (lines 30–90): Clears memory, sets up addresses, defines a helper function, and POKEs a 45-byte machine code routine into RAM at address 32765.
- Header capture (lines 100–140): Prompts the user, calls the machine code via
RANDOMIZE USR j, and reads back the type byte. - BASIC program decoding (lines 150–340): Parses and prints filename, program length, variable area size, and autostart line for type-0 (BASIC) headers.
- Array/code decoding and repeat loop (lines 500–1020): Subroutine at line 500 handles types 1–3; lines 1000–1020 handle errors and offer a retry.
Machine Code Routine
The 45 bytes POKEd starting at address j (32765) form a hand-assembled Z80 routine. Key observations from the DATA bytes:
62,0—LD A,0: loads accumulator with 0.55—SCF: sets the carry flag, signaling to the ROM loader that this is a LOAD (not VERIFY) operation.221,33,42,128—LD IX,#802A: points IX to address 32810 (theheadbuffer area).17,17,0—LD DE,17: loads DE with 17 (the standard header block length).243—DI: disables maskable interrupts.245—PUSH AF: preserves the carry/flag state.- EAR port reads (
219,255=IN A,(255)) and bit manipulation (203,255,211,255) handle raw tape signal sampling. 251—EI: re-enables interrupts before returning.201—RET: returns to BASIC.
The sentinel value 4 is pre-POKEd into head (address 32810) at line 110. If the machine code fails to overwrite it with a valid header type byte, the BASIC check at line 150 catches this condition and displays the error message.
Memory Map
| Address | Variable | Contents |
|---|---|---|
| 32765 | j | Machine code entry point (45 bytes) |
| 32810 | head (j+45) | Header buffer: type byte + 10-char name + length/address fields |
| 32810+11 | a+11 | 16-bit block length (little-endian) |
| 32810+13 | a+13 | 16-bit load/autostart address |
| 32810+15 | a+15 | 16-bit variable offset within BASIC file |
Key BASIC Idioms
DEF FN g(x)= PEEK x+256* PEEK (x+1)at line 50 is a standard idiom for reading a 16-bit little-endian word from two consecutive memory addresses.PAUSE 0followed byIF INKEY$="y"at line 1020 is the standard efficient keypress-wait pattern.CLEAR 32764at line 30 protects the machine code area from being overwritten by BASIC’s memory allocator by movingRAMTOPbelow it.- The variable
ais set equal toheadat line 205, then used inFN g(a+11)etc., allowing the function argument arithmetic to be slightly more readable than usingheaddirectly.
Header Type Dispatch
The standard Spectrum tape header type byte is decoded as follows:
| Type byte | Meaning | Handling |
|---|---|---|
| 0 | BASIC program | Falls through to lines 300–340 after IF hdtype THEN GO SUB 500 is false |
| 1 | Number array | GO SUB 500; b$ set to “Number array” |
| 2 | Character array | GO SUB 500; b$ set to “Character array” |
| 3 | Binary code block | GO SUB 500; prints start address |
| 4 | Sentinel (no header read) | Error message at line 1000 |
Notable Techniques and Anomalies
- The autostart detection at line 330 uses the heuristic
IF hdadr>10000. The actual ROM convention uses 32768+ (bit 15 set) to indicate no autostart, so values between 10001 and 32767 would be incorrectly reported as “no autostart.” A threshold of 32768 would be strictly correct. - The variable name
Nis used in the DATA POKE loop (line 80) while the loop control variable is declared as lowercasenat line 70. On the Spectrum, variable names in FOR loops are case-sensitive for display but the interpreter treats them as equivalent single-letter names, so this is harmless but visually inconsistent. - The subroutine at line 500 leaves
b$as an empty string for type 3 (binary code), so line 530 prints “Program is a ” with an emptyb$before line 540 handles the code-specific output separately — a minor cosmetic inconsistency. - The machine code uses the ROM’s tape loading conventions (carry flag set = LOAD, IX = destination, DE = byte count) while bypassing the full ROM loader, reading the EAR port more directly for header-only capture.
Content
Image Gallery
Source Code
10 REM <PROGRS. HEADER READER>
20 REM \* NAP AUG.,10,1984
30 CLEAR 32764
40 LET j=32765:LET head=j+45
50 DEF FN g(x)= PEEK x+256* PEEK (x+1)
60 RESTORE
70 FOR n=0 TO 44:READ code
80 POKE (j+N),code:NEXT n
90 DATA 62,0,55,221,33,42,128,17,17,0,243,245,219,255,203,255,211,255,219,244,50,41,128,62,1,211,244,241,205,252,0,58,41,128,211,244,219,255,203,191,211,255,251,201,0
100 CLS
110 POKE head,4
120 PRINT AT 18,8;"start the tape"
130 RANDOMIZE USR j
140 CLS :LET hdtype= PEEK head
150 IF hdtype=4 THEN GO TO 1000
160 LET h$=""
170 FOR n=1 TO 10
180 LET h$=h$+ CHR$ PEEK (head+n)
190 NEXT n
200 PRINT "Prog. Name > ";h$:PRINT :PRINT
205 LET a=head
210 LET hdlen= FN g(a+11):LET hdadr= FN g(a+13):LET hdvars= FN g(a+15)
220 LET vars=hdlen-hdvars
230 IF hdtype THEN GO SUB 500:GO TO 1010
300 PRINT "Program is in BASIC":PRINT
310 PRINT "Program area >";hdvars;" bytes":PRINT
320 PRINT "Variables area >";vars;" bytes":PRINT
330 IF hdadr>10000 THEN PRINT "There is no autostart !":GO TO 1010
340 PRINT "Line # for autostart >";hdadr:GO TO 1010
500 LET b$=""
510 IF hdtype=1 THEN LET b$="Number array"
520 IF hdtype=2 THEN LET b$="Character array"
530 PRINT "Program is a ";b$:PRINT
540 IF hdtype=3 THEN PRINT "Binary code ,start adr >";hdadr:PRINT
550 PRINT "Block length is >";hdlen;" bytes"
560 RETURN
1000 PRINT FLASH 1; AT 18,8;"This is not a tape header"
1010 PRINT FLASH 0; AT 19,1;"Are you game for another try ?(y/n)"
1020 PAUSE 0:IF INKEY$="y" THEN GO TO 100
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.