Tape Analyser

Developer(s): William Pierson
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Tape, Utility

This program is a tape analyzer utility that reads cassette tape headers and displays information about each file stored on the tape, including file name, length, type, and relevant metadata. It uses a 16-byte machine code routine POKEd into memory at address 30000, which calls the ROM’s tape-loading header routine at address 1366 (05 56 hex) and jumps to address 30000 on success. The BASIC then interprets the header block stored at address 30016 (held in variable `p`) to identify whether each file is a BASIC program, a numeric or string array, a CODE block, or a read error, and formats the output accordingly. It detects SCREEN$ blocks by checking for a CODE file starting at 16384 with length 6912. The program works across both ZX Spectrum and TS2068 hardware when in Spectrum mode.


Program Analysis

Program Structure

The program is organized into a clear sequence of phases: initialization, machine code installation, user interface setup, and a main loop that repeatedly reads tape headers and classifies them.

  1. Lines 10–11: Set display attributes and clear memory up to 29999.
  2. Lines 15–20: Define a helper function FN p(x) for reading 16-bit little-endian values, set pointer variable p to 30016, and POKE 16 machine code bytes starting at address 30000 (byte offset +1, since index starts at 1).
  3. Line 30: The DATA bytes for the machine code routine.
  4. Lines 40–65: Draw the UI header, title bar, column headings, and prompt the user to start the tape.
  5. Lines 70–230: Main loop — call machine code, parse the header buffer, classify and print each file’s details, then loop back to line 70.

Machine Code Routine

The 16 DATA bytes at line 30 are POKEd to addresses 30000–30015 (the loop runs i=1 TO 16, POKEing to 29999+i). Disassembly of the routine:

AddressBytes (hex)InstructionNotes
30000DD 21 40 75LD IX, 30016Point IX at header buffer (30016 = 0x7540)
3000411 11 00LD DE, 17Header block is 17 bytes
30007AFXOR AA=0 signals “load header”
3000837SCFCarry set = LOAD (not verify)
30009CD 56 05CALL 1366ROM LD-BYTES tape routine
30012D8RET CReturn if successful (carry set)
30013C3 30 75JP 30000Loop back on failure/timeout

On successful return, the 17-byte tape header is stored at address 30016 in the standard Spectrum format: byte 0 is the type, bytes 1–10 are the filename, bytes 11–12 are data length, and bytes 13–16 hold type-specific parameters.

Header Parsing and File Classification

After the machine code returns, BASIC at line 80 reads the type byte and filename from the buffer at p (30016). The FN p(x) function reads a 16-bit word from address x using PEEK x + 256*PEEK(x+1). The four standard Spectrum tape block types are handled:

  • Type 0 (BASIC): Lines 200–230 extract the autostart LINE and program length and print them.
  • Type 1 or 2 (Array): Lines 180–190 decode the variable name letter from the descriptor byte at p+14, constructing an array name that includes the program name for string/number arrays (types 1 and 2 both add the filename).
  • Type 3 (CODE): Lines 140–160 check for a SCREEN$ (load address 16384, length 6912) and print the load address for other CODE blocks.
  • Type >3 (Error): Line 110 prints “Read Error” for any unrecognized type byte.

Key BASIC Idioms and Techniques

  • Two-byte PEEK function: DEF FN p(x)=PEEK x+256*PEEK(x+1) at line 15 is a standard idiom for reading 16-bit little-endian integers from memory.
  • Right-justified length field: Line 90 constructs a right-justified 6-character length string using " "(1 TO 6-LEN l$)+l$, a common Spectrum BASIC string-padding trick.
  • Array name decoding: At line 180, the array descriptor byte at p+14 encodes the variable type in the high bits and the letter in the low 5 bits. INT(s/32) extracts the type and s-32*t+96 recovers the lowercase letter (ASCII offset from 96 to get ‘a’–’z’).
  • Loop re-entry via GO TO 70: Rather than using a BASIC FOR loop, the program uses explicit GO TO 70 at the end of each branch to restart the machine code call, allowing the loop to continue indefinitely without stack growth.
  • Double apostrophe in PRINT: Line 50 uses '' after the column headers to produce two newlines, separating the header from the file list.

Notable Observations and Anomalies

  • The RANDOMIZE USR 30000 at line 70 is a standard idiom for calling machine code that is expected to return normally; RANDOMIZE suppresses any error if USR returns a non-zero value.
  • Variable p is set to 30016 at line 15 inside the DEF FN statement using a comma-separated LET, which is a compact way to initialize a variable on the same line as a function definition.
  • The machine code loop at address 30013 (JP 30000) means that if the tape load fails or times out, the routine retries internally without returning to BASIC — the BASIC loop only sees successful header reads.
  • Line 65 uses PRINT #1;AT 1,0,, which clears the lower screen prompt after the keypress, though the trailing commas may produce unexpected formatting on some firmware versions.
  • Line 180 checks t=2 OR t=6 to decide whether to append the program name to the array variable letter. The Spectrum variable descriptor uses bits 5–7 for type: value 2 corresponds to string arrays and value 6 may be an extended or TS2068-specific array type.

Content

Appears On

Library tape from the Sinclair Computer Users Society (SINCUS).

Related Products

Related Articles

Related Content

Image Gallery

Tape Analyser

Source Code

    1 REM  'tape analyser'   for Spectrum mode but works for 2068 or Spectrum tapes
    2 REM Bill Pierson
   10 BORDER 0: PAPER 0: INK 7
   11 CLEAR 29999
   15 DEF FN p(x)=PEEK x+256*PEEK (x+1): LET p=30016
   20 FOR i=1 TO 16: READ a: POKE 29999+i,a: NEXT i
   30 DATA 221,33,64,117,17,17,0,175,55,205,86,5,216,195,48,117
   40 PRINT INVERSE 1;"        *Tape Analyser*         "; INVERSE 0
   50 PRINT "  NAME";TAB 10;"Length   Remarks"'': PLOT 0,156: DRAW 255,0
   60 PRINT #1;" Start tape, then press any key": PAUSE 0
   65 PRINT #1;AT 1,0,,
   70 RANDOMIZE USR 30000
   75 LET n$=""
   80 LET type=PEEK p: LET l=FN p(p+11): FOR i=p+1 TO p+10: LET n$=n$+CHR$ PEEK i: NEXT i
   90 LET l$=STR$ l: LET l$="     "(1 TO 6-LEN l$)+l$
  100 PRINT n$;l$;" ";
  110 IF type>3 THEN PRINT "Read Error": GO TO 70
  130 IF type<3 THEN GO TO 170
  140 LET s=FN p(p+13)
  150 IF s=16384 AND l=6912 THEN PRINT "SCREEN$ ": GO TO 170
  160 PRINT "CODE at: ";s: GO TO 70
  170 IF type=0 THEN GO TO 200
  180 LET s=PEEK (p+14): LET t=INT (s/32): LET l$=CHR$ (s-32*t+96): IF t=2 OR t=6 THEN LET l$=l$+n$
  190 PRINT "Array: ";l$: GO TO 70
  200 LET l=FN p(p+13): LET s=FN p(p+15)
  210 PRINT "Basic";
  220 IF l<10000 THEN PRINT " LINE ";l;
  230 PRINT : GO TO 70

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

Scroll to Top