--- title: "Tape Examiner" id: 70952 type: "computer_media" slug: "tape-examiner" url: "http://localhost/computer_media/tape-examiner/" markdown_url: "http://localhost/computer_media/tape-examiner.md" published_at: "2026-08-24T23:12:53+00:00" modified_at: "2026-08-24T23:18:02+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/tape-ex.png" alt: "Tape Examiner screen" excerpt: "Decode what's on any cassette tape by reading its header metadata — filename, file type, length, and start address — without loading the full file." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Algis Gedris" slug: "algis-gedris" taxonomy: "indiv" url: "http://localhost/indiv/algis-gedris/" genre: - name: "Tape" slug: "tape" taxonomy: "genre" url: "http://localhost/type/tape/" media_type: "Program" programmers: - name: "Algis Gedris" slug: "algis-gedris" taxonomy: "indiv" url: "http://localhost/indiv/algis-gedris/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Tape%20Examiner%20%28198x%29%28Gedris%2C%20Algis%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/tape-ex.png" alt: "Tape Examiner screen" media_type_tags: "Tape" --- # Tape Examiner 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: 1. **Lines 10–80:** Introduction and user prompt — explains the program’s function and asks whether to display results on screen or printer. 2. **Lines 90–130:** Input handling — waits for keypress `"1"` (screen) or `"2"` (printer), opening stream `#2` to the printer if needed. 3. **Lines 140–290:** Main examination loop — calls the machine code routine, reads the header buffer, and dispatches to type-specific subroutines. 4. **Lines 300–330:** Setup subroutine — POKEs the machine code into RAM and returns. 5. **Lines 1000–1340:** File-type subroutines, one per type: program (1000), number array (1100), character array (1200), bytes/code (1300). 6. **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 PI` at line 80 evaluates `NOT PI` as 0 (since PI is non-zero and thus logically true, NOT gives 0), producing `PAUSE 0` — an indefinite pause waiting for a keypress, a common idiom. - The computed `GO SUB 1000+100*c` at line 210 avoids a chain of IF statements for type dispatch. - `CLEAR 63999` at 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 where `INKEY$` 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, the `PRINT` statements 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 0 ```