This program reads and decodes tape headers by loading a short machine code routine into RAM at address 64000 and executing it via RANDOMIZE USR. The machine code interacts with the tape hardware directly, capturing the 17-byte standard header block into memory starting at address 64060. After execution, the BASIC code inspects the header fields using PEEK to extract and display the file type (Program, Numeric Array, Character Array, or Bytes), the 10-character filename, data length, start address, and program/variables length. Output is sent simultaneously to both the screen and printer via paired PRINT and LPRINT statements. The program loops continuously, prompting the user to load another tape after each header is read.
Program Analysis
Program Structure
The program is self-contained and loops indefinitely. Its flow is:
- Lines 9970–9971: Load machine code into RAM at addresses 64000–64052 (the DATA lines are read twice due to the duplicate loop — a likely bug, see below).
- Lines 9975, 9980–9982: Clear the screen, prompt the user, define the machine code data, and execute it with
RANDOMIZE USR 64000. - Lines 9983–9994: Inspect the captured header bytes at address 64060 onward using
PEEK, then display results on screen and printer. - Lines 9995–9996: Prompt for another tape and loop back to line 9975.
Machine Code Routine
The machine code (53 bytes, addresses 64000–64052) is poked from the three DATA statements at lines 9980–9981. It is called via RANDOMIZE USR 64000. The routine interacts directly with the tape input hardware: it drives the EAR port and reads the MIC port, capturing the standard 17-byte Spectrum tape header block and storing it at address 64060 (hex FA3C). The use of DI/EI instructions (opcodes 0xF3/0xFB) and direct I/O port operations (opcodes 0xD3/0xDB with ports 0xFF and 0xF4) are characteristic of low-level tape loading routines.
Header Decoding
Once the machine code returns, the BASIC code decodes the standard Spectrum tape header format stored from address 64060:
| Offset from 64060 | Field | Size |
|---|---|---|
| 0 | File type (0=Program, 1=Num Array, 2=Char Array, 3=Bytes) | 1 byte |
| 1–10 | Filename | 10 bytes |
| 11–12 | Data length | 2 bytes (little-endian) |
| 13–14 | Parameter 1 (start address or autostart line) | 2 bytes (little-endian) |
| 15–16 | Parameter 2 (program+variables length) | 2 bytes (little-endian) |
Little-endian 16-bit values are reconstructed with the idiom PEEK a + 256 * PEEK (a+1). The autostart line is only displayed for Program blocks (s=0) and only when the value is in the range 1–9999, matching the Spectrum’s convention where values outside this range indicate no autostart.
Dual Output
Every display statement is mirrored with a corresponding LPRINT statement, producing simultaneous output to both the screen and a connected printer. This suggests the tool was intended for archival or cataloguing use, where a paper record of tape contents was desirable.
Notable Techniques
- The machine code is positioned at address 64000, safely above the typical BASIC/variable area and below the system variables at 65536.
- The variable
scaptures the file type byte at line 9983 and is referenced later at lines 9990–9994 to conditionally display type-specific fields. - The
PAUSE 0at line 9996 waits for any keypress before looping, consistent with the standard Sinclair keypress-wait idiom. - The filename is printed character by character using
CHR$ binside a FOR loop (line 9988), correctly handling the fixed 10-character field.
Bugs and Anomalies
- Lines 9970 and 9971 are identical: both read the same DATA and poke it to the same addresses. The second pass is redundant and wastes time; it is likely a copy-paste error. Since DATA is read sequentially, the second execution of the FOR loop will attempt to read additional DATA values that do not exist, which on the Spectrum would cause an “Out of DATA” error (error code E). This is a definite bug — the duplicate line 9971 should be deleted.
- The variable
ais reused throughout as both the POKE address counter (lines 9970–9971) and the header field pointer (lines 9983–9994), which is functional but makes the code harder to follow. - At line 9989,
PEEK auses the value ofaleft over from the filename loop (line 9988), which ends witha=64071— correctly pointing to the data length field. This implicit reliance on loop variable state is fragile but intentional.
Content
Source Code
9969 REM "header" for reading tape headers
9970 CLEAR 63999: FOR a=64000 TO 64052: READ b: POKE a,b: NEXT a
9971 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: ";: LPRINT "program: ";
9985 IF b=1 THEN PRINT "Numeric Array: ";: LPRINT "Numeric Array: ";
9986 IF b=2 THEN PRINT "Character Array: ";: LPRINT "Character Array: ";
9987 IF b=3 THEN PRINT "Bytes: ";: LPRINT "Bytes: ";
9988 FOR a=64061 TO 64070: LET b=PEEK a: PRINT CHR$ b;: LPRINT CHR$ b;: NEXT a: PRINT : LPRINT
9989 LET b=PEEK a+256*PEEK (a+1): PRINT "Data Length: ";b: LPRINT "Data Length: ";b
9990 LET a=a+2: LET b=PEEK a+256*PEEK (a+1): IF s=3 THEN PRINT "Start Address: ";b: LPRINT "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: LPRINT "Auto Start at: ";b
9994 LET a=a+2: LET b=PEEK a+256*PEEK (a+1): PRINT "Prog/Vars Length: ";b: LPRINT "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.
