R-Header 2068

Developer(s): Real Gagnon
Date: 1985
Type: Program
Platform(s): TS 2068
Tags: Utility

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:

  1. Initialization (lines 1–10): Clears memory above 31999, sets display attributes, and calls the machine code setup subroutine at line 1000.
  2. 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 RUN at line 190.
  3. Display border subroutine (line 600): Draws a rectangular border using PLOT/DRAW and prints the title banner.
  4. Machine code loader (lines 1000–1030): Reads 31 bytes of DATA and POKEs them into addresses 32000–32030.
  5. 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)MnemonicPurpose
DD 21 00 7ELD IX, 32256Set IX to destination buffer for header
AFXOR AA=0: signal flag byte (header, not data)
11 11 00LD DE, 17Read 17 bytes (standard header length)
37SCFSet carry: LOAD (not VERIFY)
01 FE FELD BC, 0FEFEhTape input port setup
CD 99 64CALL 25753ROM LD-BYTES: reads bytes from tape
CD FC 00CALL 252ROM routine (likely error check/return)
BACP DCompare accumulator with D
20 F0JR NZ, -16Retry if header not confirmed
DB FFIN A,(255)Read ULA port
CB BFRES 7,AClear bit 7 (EAR input)
D3 FFOUT (255),AWrite back to ULA port
AFXOR AA=0
D3 F4OUT (244),ATS2068 port: likely audio/tape control
C9RETReturn 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:

OffsetBASIC expressionField
+0PEEK IXFile type (0=Program, 1=Num Array, 2=Str Array, 3=Bytes)
+1 to +10FOR n=ix+1 TO ix+10Filename (10 characters)
+11, +12PEEK(ix+11)+256*PEEK(ix+12)Data length (16-bit little-endian)
+13, +14PEEK(ix+13)+256*PEEK(ix+14)Auto-run line (Program) or Block Origin (Bytes)
+14PEEK(ix+14)Array variable letter (Arrays)
+15, +16PEEK(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 extra 64* 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 RUN rather than GO TO 50; this re-executes from line 1 but because line 1 only contains a REM, effectively jumps forward. Actually RUN restarts from line 1, meaning it re-does the border setup but skips the machine code re-POKEing (which is handled only in the GO SUB 1000 at line 10). On reflection, RUN here 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 0 waits indefinitely until any key is pressed, and the following IF 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 RUN to loop back, which restarts from line 1 and re-executes the machine code POKE routine (via GO SUB 1000 at line 10). While functionally harmless since the same data is written each time, a GO TO 50 would 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 STOP statements that serve as subroutine guards in case of fall-through, a common defensive coding practice.
  • The CALL 252 in 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

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

Scroll to Top