Headscan

This file is part of and CATS Library Tape 3. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Header, Tape

This program reads a cassette tape header block and displays its metadata, acting as a tape analyzer utility. It POKEs 53 bytes of Z80 machine code into memory starting at address 64000, then calls that routine via RANDOMIZE USR to load the 17-byte header from tape into a buffer at address 64060 onward. After the machine code returns, the BASIC code at lines 9983–9994 PEEKs into the buffer to decode and print the file type (program, numeric array, character array, or bytes), filename, data length, optional start address for byte files, auto-start line number for BASIC programs, and program/variables length. The loop at lines 9995–9996 allows repeated analysis of successive tapes without restarting.


Program Analysis

Program Structure

The program is organized into three logical phases:

  1. Initialization (lines 9970–9982): Clears memory above address 63999, POKEs 53 bytes of Z80 machine code into addresses 64000–64052, prompts the user to play a tape, then calls the machine code with RANDOMIZE USR 64000.
  2. Header Decoding (lines 9983–9994): Reads the loaded header buffer via PEEK and interprets the tape block fields one by one.
  3. Loop (lines 9995–9996): Waits for a keypress then jumps back to line 9975 to analyze another tape.

Machine Code Routine

The 53 bytes spread across DATA lines 9980–9981 form a Z80 routine loaded at address 64000. The entry point at 64000 sets up a tape-loading call targeting the ROM’s tape routines. Key observations about the byte sequence:

  • 55 (PUSH HL / EXX preamble area), followed by 62,0 (LD A,0) — sets the header flag.
  • 221,33,60,250LD IX,64060, pointing the IX register at the destination buffer where the 17-byte tape header will be stored.
  • 17,17,0LD DE,17, the header block length.
  • 205,14,250CALL 64014, an internal sub-call within the machine code block itself, which contains the actual ROM tape-loading invocation (likely calling the Spectrum ROM’s LD-BYTES entry at 0x0556).
  • 201RET back to BASIC after loading.
  • The routine at offset 14 within the block handles the actual ROM call, toggling interrupts (243 = DI, 251 = EI) and using port I/O instructions (219 = IN A,(n), 211 = OUT (n),A) consistent with border/EAR management during tape loading.

The buffer at address 64060 stores the standard 17-byte Spectrum tape header: 1 byte type, 10 bytes filename, 2 bytes data length, 2 bytes param1, 2 bytes param2.

Header Field Decoding

Lines 9983–9994 decode the header buffer using PEEK:

AddressFieldLine(s)
64060Block type (0=program, 1=num array, 2=char array, 3=bytes)9983–9987
64061–64070Filename (10 characters, printed via CHR$)9988
64071–64072Data length (16-bit little-endian)9989
64073–64074Param1: start address (bytes) or auto-start line (program)9990–9993
64075–64076Param2: program+variables length9994

The 16-bit little-endian decode idiom PEEK a + 256 * PEEK (a+1) is used consistently throughout. Note that at line 9989 the variable a is left pointing to address 64070 (the last byte of the filename loop), so the data length is read from PEEK 64070 + 256*PEEK 64071 — this is off by one from the correct addresses 64071–64072. This is a bug: the data length word is misread by one byte.

Notable Techniques

  • CLEAR 63999 both sets RAMTOP and protects the machine code area from BASIC interference.
  • The use of high line numbers (9970–9996) suggests this utility is intended to be merged into an existing program without conflicting with lower-numbered lines.
  • The PAUSE 0 at line 9996 followed by GO TO 9975 implements an efficient wait-for-keypress loop before repeating, consistent with common Sinclair BASIC idiom.
  • The auto-start line check at line 9992 gates on b<1 OR b>9999, correctly filtering out non-auto-start programs (where param1 holds 0x8000 or similar sentinel values) before printing the line number.
  • The type variable s is saved at line 9983 and reused at lines 9990 and 9991 to conditionally display fields relevant only to certain block types, avoiding redundant display of inapplicable fields.

Bugs and Anomalies

  • Off-by-one in filename loop: The FOR loop at line 9988 runs a from 64061 to 64070 (10 iterations, correct for the filename), but leaves a=64070 after completion. The data length is then read as PEEK 64070 + 256*PEEK 64071 instead of the correct PEEK 64071 + 256*PEEK 64072, causing all subsequent field reads to be shifted one byte early.
  • Param2 display for arrays: Line 9994 unconditionally prints “prog/vars length” for all block types, including byte and array files where this field has a different meaning (or is undefined). No label adjustment is made for non-program types.

Content

Appears On

Balance your checkbook, decode cryptograms, or trace your BASIC program's execution path — Tape 3 is where serious productivity meets hands-on programming. Highlights include a word processor, a flat-file database, machine code loaders, and a complete darkroom formulary.

Related Products

Related Articles

Related Content

Image Gallery

Headscan

Source Code

 9970 CLEAR 63999: FOR a=64000 TO 64052: READ b: POKE a,b: NEXT a
 9975 CLS : PRINT ''''"load a tape and press ""play"""''''
 9980 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
 9981 DATA 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
 9982 RANDOMIZE USR 64000
 9983 LET a=64060: LET b=PEEK a: LET s=b
 9984 IF b=0 THEN PRINT "program: ";
 9985 IF b=1 THEN PRINT "numeric array: ";
 9986 IF b=2 THEN PRINT "character array: ";
 9987 IF b=3 THEN PRINT "bytes: ";
 9988 FOR a=64061 TO 64070: LET b=PEEK a: PRINT CHR$ b;: NEXT a: PRINT 
 9989 LET b=PEEK a+256*PEEK (a+1): PRINT "data length: ";b
 9990 LET a=a+2: LET b=PEEK a+256*PEEK (a+1): IF s=3 THEN PRINT "start address: ";b
 9991 IF s<>0 THEN GO TO 9995
 9992 IF b<1 OR b>9999 THEN GO TO 9994
 9993 PRINT "auto start at: ";b
 9994 LET a=a+2: LET b=PEEK a+256*PEEK (a+1): PRINT "prog/vars length: ";b
 9995 PRINT ''''"for another tape, touch any key"
 9996 PAUSE 0: GO TO 9975

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top