Header Reader

Developer(s): Nazir A. Pashtoon
Date: 1984
Type: Program
Platform(s): TS 2068

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:

  1. 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.
  2. Header capture (lines 100–140): Prompts the user, calls the machine code via RANDOMIZE USR j, and reads back the type byte.
  3. BASIC program decoding (lines 150–340): Parses and prints filename, program length, variable area size, and autostart line for type-0 (BASIC) headers.
  4. 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,0LD A,0: loads accumulator with 0.
  • 55SCF: sets the carry flag, signaling to the ROM loader that this is a LOAD (not VERIFY) operation.
  • 221,33,42,128LD IX,#802A: points IX to address 32810 (the head buffer area).
  • 17,17,0LD DE,17: loads DE with 17 (the standard header block length).
  • 243DI: disables maskable interrupts.
  • 245PUSH 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.
  • 251EI: re-enables interrupts before returning.
  • 201RET: 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

AddressVariableContents
32765jMachine code entry point (45 bytes)
32810head (j+45)Header buffer: type byte + 10-char name + length/address fields
32810+11a+1116-bit block length (little-endian)
32810+13a+1316-bit load/autostart address
32810+15a+1516-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 0 followed by IF INKEY$="y" at line 1020 is the standard efficient keypress-wait pattern.
  • CLEAR 32764 at line 30 protects the machine code area from being overwritten by BASIC’s memory allocator by moving RAMTOP below it.
  • The variable a is set equal to head at line 205, then used in FN g(a+11) etc., allowing the function argument arithmetic to be slightly more readable than using head directly.

Header Type Dispatch

The standard Spectrum tape header type byte is decoded as follows:

Type byteMeaningHandling
0BASIC programFalls through to lines 300–340 after IF hdtype THEN GO SUB 500 is false
1Number arrayGO SUB 500; b$ set to “Number array”
2Character arrayGO SUB 500; b$ set to “Character array”
3Binary code blockGO SUB 500; prints start address
4Sentinel (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 N is used in the DATA POKE loop (line 80) while the loop control variable is declared as lowercase n at 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 empty b$ 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

Appears On

Related Products

Related Articles

Related 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.

Scroll to Top