Tape Examiner reads cassette tape headers and displays the metadata they contain, including file type, filename, data length, start address, and autostart line number. It uses a machine code routine POKEd into RAM at address 64000–64079, loaded from a decimal-encoded string, which interfaces with the Extended ROM’s R_TAPE module to capture incoming headers without loading the full file. The program uses ON ERR to handle tape errors gracefully and loop back for the next header. A DEF FN helper reads 16-bit little-endian values from the header buffer at address 64080. Special cases handle number arrays, character arrays, program files, and screen images (detected by checking for a 6912-byte block starting at address 16384).
Program Structure
The program is organized into several logical sections:
- Lines 10–80: Introduction and user prompt — explains the program’s function and asks whether to display results on screen or printer.
- Lines 90–130: Input handling — waits for keypress
"1"(screen) or"2"(printer), opening stream#2to the printer if needed. - Lines 140–290: Main examination loop — calls the machine code routine, reads the header buffer, and dispatches to type-specific subroutines.
- Lines 300–330: Setup subroutine — POKEs the machine code into RAM and returns.
- Lines 1000–1340: File-type subroutines, one per type: program (1000), number array (1100), character array (1200), bytes/code (1300).
- Line 9998: SAVE line for storing the program itself.
Machine Code Loader
The subroutine at line 300 loads 80 bytes of machine code into addresses 64000–64079 by decoding a long decimal string stored in a$ at line 320. Each byte is encoded as a zero-padded three-digit decimal value. The loop at line 330 uses VAL a$( TO PI) — which effectively evaluates a$(1 TO 3) since PI truncates to 3 — to extract three characters at a time, then advances a$ by slicing from position 4 onward. This is a compact idiom for sequential three-digit parsing without a separate index variable.
The machine code entry point is at address 64064, called via RANDOMIZE USR 64064 at line 150. The routine interfaces with the Extended ROM’s R_TAPE module to capture an incoming tape header and deposit it into a 17-byte buffer starting at address 64080 (b=64080). The header is not fully loaded — only the header block is captured, leaving tape data on the tape.
Header Buffer and DEF FN
Line 140 sets b=64080 as the base address of the header buffer and defines a function FN a(x) to read a 16-bit little-endian word: PEEK (b+x) + 256 * PEEK (b+x+1). This is used throughout the type-specific subroutines to extract length, start address, and autostart line fields from the standard tape header format.
File Type Dispatch
Line 160 reads the type byte at PEEK b (offset 0 of the header). Line 170 skips to the next header if the type byte is greater than 3, guarding against corrupt or non-standard headers. Line 210 dispatches to the appropriate subroutine using GO SUB 1000+100*c, mapping type values 0–3 to lines 1000, 1100, 1200, and 1300 respectively — a clean computed GO SUB pattern.
File Type Subroutines
| Line | Type byte | Description |
|---|---|---|
| 1000 | 0 | Program: prints total length, program length, and autostart line (or “Load only” if autostart > 9999) |
| 1100 | 1 | Number array: prints array length and original array name |
| 1200 | 2 | Character array: same as number array but appends $ to name |
| 1300 | 3 | Bytes/code: prints start address and length; detects screen images by checking for length 6912 and start 16384 |
Array Name Decoding
Line 1240 recovers the original array variable name from the header byte at offset 14 (d = PEEK (b+14)). The expression CHR$ VAL "(64+32*(d/32 - INT (d/32)))" extracts the lower 5 bits of d and adds 64, converting the encoded name byte back to a letter. The use of VAL on a string expression is an unusual but valid technique here to avoid a direct numeric expression that might cause issues, though it is slightly redundant.
Error Handling and Loop Control
Line 150 sets ON ERR GO TO 250 before each call to the machine code routine, so tape errors (such as the user pressing BREAK or a read failure) divert to the cleanup block at line 250. After printing header details, line 230 POKEs 255 into the type byte at b to invalidate the previous header, then line 240 loops back to line 150 to wait for the next header. Lines 250–290 handle shutdown: resetting the error handler with ON ERR RESET, closing stream #2, and stopping.
Notable Techniques
PAUSE NOT PIat line 80 evaluatesNOT PIas 0 (since PI is non-zero and thus logically true, NOT gives 0), producingPAUSE 0— an indefinite pause waiting for a keypress, a common idiom.- The computed
GO SUB 1000+100*cat line 210 avoids a chain of IF statements for type dispatch. CLEAR 63999at line 20 sets RAMTOP just below the machine code area, protecting it from being overwritten by BASIC.- The three-digit decimal encoding with
VAL a$( TO PI)is a compact, self-consuming string parsing technique that avoids a separate pointer variable.
Potential Anomalies
- Line 120 (
GO TO 80) is unreachable: lines 90–110 handle all cases whereINKEY$is in range, and line 90 loops until a valid key is pressed. The branch at line 120 can never be reached in normal execution. - The
OPEN #2,"p"at line 110 redirects output to the printer; however, thePRINTstatements in the main loop and subroutines do not explicitly address stream#2, so this relies on the default stream being redirected, which may not behave as intended on all system configurations.
Source Code
10 REM This tape was donated by theTORONTO TIMEX SINCLAIR USER'SGroupMODIFIED & COPIED BY ALGIS E.GEDRIS, 12/20/86
20 CLEAR 63999:GO SUB 300
30 CLS
40 PRINT "Tape Examiner. When you start this program, it will switch to R_TAPE module in the EXtended ROM and wait for an incoming header."
50 PRINT '"It will print out the details contained in the header:"'"File type; file name; bytes of data; special info; program","byte length."
60 PRINT '"It will also identify a screen image."
70 PRINT ''"Prepare a tape in the recorder and press 1 for screen or","2 for printer display."''"While the program is running you may press BREAK to Stop"
80 PAUSE NOT PI
90 IF INKEY$<"1" OR INKEY$>"2" THEN GO TO 90
100 IF INKEY$="1" THEN GO TO 130
110 IF INKEY$="2" THEN OPEN #2,"p":GO TO 130
120 GO TO 80
130 CLS
140 LET b=64080:DEF FN a(x)= PEEK (b+x)+256* PEEK (b+x+1)
150 ON ERR GO TO 250:RANDOMIZE USR 64064
160 LET c= PEEK b
170 IF c>3 THEN GO TO 150
180 PRINT "Filename: ";
190 FOR a=b+1 TO b+10:PRINT CHR$ PEEK a;:NEXT a
200 PRINT :PRINT TAB 4;"type: ";
210 GO SUB 1000+100*c
220 PRINT :PRINT
230 POKE b,255
240 GO TO 150
250 ON ERR RESET
260 CLOSE #2
270 ON ERR GO TO 250
280 ON ERR CONTINUE
290 STOP
300 REM TAPE EXAM
310 PRINT ''"Setting code"
320 LET a$="033252000205045250024023033104000205045250024015033142014245205045250241254128032003050194092058044250211244219255203191211255251201000243245219255203255211255219244050044250062001211244241233175055221033080250205000250201000000000000000000"
330 FOR f=64000 TO 64079:POKE f, VAL a$( TO PI):LET a$=a$(4 TO ):NEXT f:RETURN
1000 PRINT "program"
1010 PRINT "Total length: "; FN a(11);" bytes"
1020 PRINT "Program length: "; FN a(15);" bytes"
1030 IF FN a(13)>9999 THEN PRINT "Load only":RETURN
1040 PRINT "Runs from line "; FN a(13)
1050 RETURN
1100 PRINT "number array"
1110 LET a$="":GO TO 1220
1200 PRINT "character array"
1210 LET a$="$"
1220 PRINT "Array length: "; FN a(11);" bytes"
1230 LET d= PEEK (b+14)
1240 PRINT "Original array name: "; CHR$ VAL "(64+32*(d/32- INT (d/32)))";a$
1250 RETURN
1300 IF FN a(11)=6912 AND FN a(13)=16384 THEN PRINT "screen image":RETURN
1310 PRINT "bytes"
1320 PRINT "Start address: "; FN a(13)
1330 PRINT "Length: "; FN a(11);" bytes"
1340 RETURN
9998 SAVE "Tape EX" LINE 0Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
