This program scans a tape and catalogs the file headers found on it, printing each file’s type, name, and estimated tape position to both the screen and a printer. Machine code is loaded into RAM at address 64000 via a DATA block and called with RANDOMIZE USR 64000; the routine reads a tape header block using the ROM’s tape-loading entry points and stores the result in a small buffer at address 64060 onwards. The tape position estimate is calculated by dividing a counter accumulated in system variables 23672/23673 by a tunable `factor` constant (set to 130), giving an approximate location in seconds or tape units. After displaying each header, the program pauses for a duration proportional to the file’s length (derived from the two-byte value at 64071/64072) before looping back to search for the next header, allowing the tape to advance to the next file.
Program Analysis
Program Structure
The program is organised into four broad phases:
- Initialisation (lines 20–60): Clears memory above 63999, POKEs 53 bytes of machine code into addresses 64000–64052, and configures system variables.
- Machine-code call (line 80): Executes the tape-reading routine via
RANDOMIZE USR 64000. - Header display (lines 90–150): Reads the buffer written by the machine code and prints the file type, 10-character name, and estimated tape position to screen and printer.
- Pause and loop (line 170): Waits a time proportional to the file’s data length, then jumps back to line 80 to catch the next header.
Machine Code Routine
The 53 bytes loaded at 64000–64052 form a Z80 routine. Reconstructing it from the DATA values:
55— SCF (set carry flag, signalling ROM tape loader to load rather than verify)62,0— LD A,0 (request header block, type 0xFF in ROM terms — actually A=0 selects header)221,33,60,250— LD IX,64060 (destination buffer for the 17-byte header)17,17,0— LD DE,17 (length of a tape header block)205,14,250— CALL 64014 (an inner sub-routine within the block, or the ROM LD-BYTES entry via a relay)201— RET
The second DATA line (line 50) contains the relay/counter sub-routine starting near 64027. It uses 243 (DI), 251 (EI), 219,255 (IN A,(255)) and 211,244/255 (OUT) sequences that interact with the tape input port and an EAR-sampling loop, incrementing the 16-bit counter stored in system variables 23672–23673 to track elapsed tape time.
The ROM tape-loading entry point is invoked indirectly. The header data (flag byte, file type, 10-char name, length, and parameters) ends up at address 64060 onward, giving the BASIC program easy access via PEEK.
Buffer Layout at Address 64060
| Address | Content |
|---|---|
| 64060 | File type byte (0=Program, 1=Numeric array, 2=Character array, 3=Bytes) |
| 64061–64070 | 10-character filename |
| 64071–64072 | Data length (2 bytes, little-endian) |
Tape Position Estimation
Lines 60 and 150 implement a software tape-position counter. POKE 23673,0: POKE 23672,110 initialises the 16-bit counter in the system variable area to 110. After the machine code runs, the routine in line 150 reads back the counter with PEEK 23672+256*PEEK 23673, divides by factor (130), and rounds to the nearest integer to give an estimated position. The factor variable on line 70 is deliberately exposed so the user can calibrate it for their tape deck speed.
Pause Duration Proportional to File Length
Line 160 reads the two-byte data-length field from addresses 64071–64072. Line 170 then computes PAUSE 300+p/3.6, pausing roughly proportional to how long the file’s data block will take to load, so the machine-code loop is re-entered just as the next header becomes due. This is an elegant timing heuristic rather than a precise synchronisation.
Dual Output
Every PRINT statement on lines 100–150 is mirrored by a corresponding LPRINT, sending the catalog simultaneously to screen and to a printer. The POKE 23692,255 on line 60 disables the scroll prompt (“scroll?”) so the screen output is never interrupted.
Key BASIC Idioms
RANDOMIZE USR 64000— standard idiom for calling machine code without caring about the return value.LET loc=INT(.5+…)— rounding to nearest integer via adding 0.5 before truncation.- Chained
IF b=n THEN …statements (lines 100–130) used instead of a computed branch, since there are only four file types to handle.
Potential Issues
- If the tape routine fails (e.g., no signal or wrong block type),
RANDOMIZE USR 64000may return an error or hang, as there is no error-trapping logic. - The
factorcalibration value of 130 is machine- and deck-specific; no guidance is provided in the program for adjusting it. - The counter initialisation in line 60 sets 23672 to 110 rather than 0; this appears intentional as a pre-offset to account for the time before the first header, but its exact purpose is not documented in the listing.
Content
Image Gallery
Source Code
10 REM save"catalog" line 10
20 CLEAR 63999: FOR a=64000 TO 64052: READ b: POKE a,b: NEXT a
30 CLS : PRINT "Run the tape to be scanned": PRINT AT 0,0;
40 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
50 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
60 POKE 23692,255: POKE 23673,0: POKE 23672,110
70 LET factor=130
80 RANDOMIZE USR 64000
90 LET a=64060: LET b=PEEK a: LET s=b
100 IF b=0 THEN LPRINT "Program: ";: PRINT "Program: ";
110 IF b=1 THEN LPRINT "Numeric array: ";: PRINT "Numeric array: ";
120 IF b=2 THEN LPRINT "Character array: ";: PRINT "Character array: ";
130 IF b=3 THEN LPRINT "Bytes: ";: PRINT "Bytes: ";
140 FOR a=64061 TO 64070: LET b=PEEK a: LPRINT CHR$ b;: PRINT CHR$ b;: NEXT a
150 LET loc=INT (.5+((PEEK 23672+256*PEEK 23673)/factor)): PRINT TAB 22;"at ";loc: LPRINT TAB 22;"at ";loc
160 LET p=PEEK 64071+256*PEEK 64072
170 PAUSE 300+p/3.6: GO TO 80
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.