R-HEADER 2068 is a cassette tape header reader that loads a raw header block from tape and decodes its fields for display. A short machine code routine (31 bytes, POKEd into RAM at address 32000) uses the TS2068’s ROM routines to read the tape header into memory starting at address 32256, after which BASIC parses and displays the header fields: file type, filename (10 bytes), data length, auto-run line or block origin, and program length. The program recognizes all four standard header types—Program, Numeric Array, String Array, and Bytes Block—and decodes array variable names and the dollar-sign suffix for string arrays. A bordered title screen drawn with PLOT/DRAW calls and a flashing “START CASSETTE” prompt guide the user through the process, and after each header read the user can press S to stop or ENTER to read another.
Program Analysis
Program Structure
The program is organized into distinct functional blocks:
- Initialization (lines 1–10): Clears memory above 31999, sets display attributes, and calls the machine code setup subroutine at line 1000.
- Main loop (lines 50–190): Prompts the user, invokes the machine code to read a tape header, then decodes and displays all header fields before looping via
RUNat line 190. - Display border subroutine (line 600): Draws a rectangular border using
PLOT/DRAWand prints the title banner. - Machine code loader (lines 1000–1030): Reads 31 bytes of DATA and POKEs them into addresses 32000–32030.
- Save/verify block (line 9991): SAVEs the program to tape and verifies it.
Machine Code Routine
The 31-byte routine is POKEd at address 32000 and invoked via RANDOMIZE USR 32000 at line 60. Disassembling the DATA bytes reveals its operation:
| Bytes (hex) | Mnemonic | Purpose |
|---|---|---|
| DD 21 00 7E | LD IX, 32256 | Set IX to destination buffer for header |
| AF | XOR A | A=0: signal flag byte (header, not data) |
| 11 11 00 | LD DE, 17 | Read 17 bytes (standard header length) |
| 37 | SCF | Set carry: LOAD (not VERIFY) |
| 01 FE FE | LD BC, 0FEFEh | Tape input port setup |
| CD 99 64 | CALL 25753 | ROM LD-BYTES: reads bytes from tape |
| CD FC 00 | CALL 252 | ROM routine (likely error check/return) |
| BA | CP D | Compare accumulator with D |
| 20 F0 | JR NZ, -16 | Retry if header not confirmed |
| DB FF | IN A,(255) | Read ULA port |
| CB BF | RES 7,A | Clear bit 7 (EAR input) |
| D3 FF | OUT (255),A | Write back to ULA port |
| AF | XOR A | A=0 |
| D3 F4 | OUT (244),A | TS2068 port: likely audio/tape control |
| C9 | RET | Return to BASIC |
The routine calls ROM LD-BYTES (address 25753 / 0x6499) to read 17 bytes of raw header data directly into address 32256. The use of OUT (244),A is specific to the TS2068’s auxiliary hardware ports, distinguishing this program from a standard Spectrum implementation.
Header Decoding Logic
After the machine code returns, the BASIC code at lines 80–170 interprets the header buffer starting at IX (32256) according to the standard tape header format:
| Offset | BASIC expression | Field |
|---|---|---|
| +0 | PEEK IX | File type (0=Program, 1=Num Array, 2=Str Array, 3=Bytes) |
| +1 to +10 | FOR n=ix+1 TO ix+10 | Filename (10 characters) |
| +11, +12 | PEEK(ix+11)+256*PEEK(ix+12) | Data length (16-bit little-endian) |
| +13, +14 | PEEK(ix+13)+256*PEEK(ix+14) | Auto-run line (Program) or Block Origin (Bytes) |
| +14 | PEEK(ix+14) | Array variable letter (Arrays) |
| +15, +16 | PEEK(ix+15)+256*PEEK(ix+16) | Program length (Program type only) |
Key BASIC Idioms
- Boolean string selection: Lines 90, 110, 130, 140, and 160 use expressions like
("Program:" AND type=0)+("Bytes Block:" AND type=3). Since a true condition returns 1 and false returns 0 in Sinclair BASIC, multiplying a string by 1 returns it unchanged and by 0 returns an empty string, neatly selecting the correct label without IF/THEN branching. - Array variable name decoding (line 130): The expression
CHR$(PEEK(ix+14)-32-64*(PEEK(ix+14)>192))recovers the variable letter. For string arrays, whose variable byte has bit 6 set (value ≥192), the extra64*term subtracts it back out, yielding the correct ASCII letter.("$" AND type=2)appends the dollar sign for string arrays. - Loop-free restart: Line 190 uses
RUNrather thanGO TO 50; this re-executes from line 1 but because line 1 only contains a REM, effectively jumps forward. ActuallyRUNrestarts from line 1, meaning it re-does the border setup but skips the machine code re-POKEing (which is handled only in theGO SUB 1000at line 10). On reflection,RUNhere restarts from line 1, so it will re-execute line 10 and redo the POKE loop — this is correct behavior but slightly wasteful. - PAUSE 0 / INKEY$ idiom (line 180):
PAUSE 0waits indefinitely until any key is pressed, and the followingIF INKEY$=check reads the key still held immediately after.
Display Subroutine
The subroutine at line 600 uses PLOT 0,0: DRAW 255,0: DRAW 0,175: DRAW -255,0: DRAW 0,-175 to trace a rectangle around the full screen border, then overlays the title text using PRINT INVERSE 1; AT for a highlighted banner effect. The double apostrophe '' at the end of line 600 prints two blank lines before the RETURN at line 610.
Anomalies and Notes
- Line 190 uses
RUNto loop back, which restarts from line 1 and re-executes the machine code POKE routine (viaGO SUB 1000at line 10). While functionally harmless since the same data is written each time, aGO TO 50would be more efficient. - Line 199 (
STOP) is unreachable because line 190 always transfers control; it appears to be a safety guard. - Lines 699 and 8999 contain isolated
STOPstatements that serve as subroutine guards in case of fall-through, a common defensive coding practice. - The
CALL 252in the machine code (address 0x00FC) targets a low ROM address; in the TS2068 context this likely leads to an error-handling or continuation vector rather than a meaningful subroutine, and its exact role depends on the TS2068 ROM mapping in effect at runtime.
Content
Source Code
1 REM __________R-HEADER 2068 BY GAGNON-o85___________
2 CLEAR 31999
5 BORDER 0: PAPER 0: BRIGHT 1: INK 5: CLS
10 GO SUB 1000
50 CLS : GO SUB 600: BEEP .01,10: PRINT AT 10,9; INK 0; PAPER 6; FLASH 1;"START CASSETTE"
60 RANDOMIZE USR 32000
65 BEEP .01,45
69 CLS : GO SUB 600
70 LET IX=32256
80 LET type=PEEK IX
90 PRINT INVERSE 1'("Program:" AND type =0)+("Numeric Array:" AND type=1)+("String Array:" AND type=2)+("Bytes Block:" AND type =3);
100 PRINT INVERSE 0;" ";: FOR n=ix+1 TO ix+10: PRINT CHR$ PEEK n;: NEXT n
110 PRINT INVERSE 1''+("Prog. + Variable " AND type=0)+("Code " AND type);" Length:";
120 PRINT INVERSE 0;" ";PEEK (ix+11)+256*PEEK (ix+12)
130 IF type=1 OR type=2 THEN PRINT INVERSE 1'"Variable:";: PRINT INVERSE 0;" ";CHR$ (PEEK (ix+14)-32-64*(PEEK (ix+14)>192))+("$" AND type=2): GO TO 175
140 PRINT ': PRINT INVERSE 1;("Auto-Run Line:" AND type=0)+("Block Origin:" AND type=3);
150 PRINT INVERSE 0;" ";PEEK (ix+13)+256*PEEK (ix+14)
155 IF type=3 THEN GO TO 175
160 PRINT INVERSE 1;("Program Length:" AND type =0);
170 PRINT INVERSE 0;" ";PEEK (ix+15)+256*PEEK (ix+16)
175 PRINT ''' OVER 1;"................................"
180 PRINT '' OVER 1;" S: STOP ENTER:CONTINUE ": PAUSE 0: IF INKEY$="s" OR INKEY$="S" THEN STOP
190 RUN
199 STOP
600 PLOT 0,0: DRAW 255,0: DRAW 0,175: DRAW -255,0: DRAW 0,-175: PRINT INVERSE 1;AT 0,9;"R-HEADER 2068";AT 1,9;"GAGNON-o 85"''
610 RETURN
699 STOP
1000 REM _____________the M.C.
1010 DATA 221,33,0,126,175,17,17,0,55,1,254,254,205,153,100,205,252,0,186,32,240,219,255,203,191,211,255,175,211,244,201
1020 FOR i=32000 TO 32030: READ a: POKE i,a: NEXT i
1030 RETURN
8999 STOP
9991 SAVE "lect2068": PRINT "Rewind & press ENTER to verify": PAUSE 0: VERIFY "lect2068"
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

