--- title: "R-Header 2068" id: 56116 type: "computer_media" slug: "r-header-2068" url: "http://localhost/computer_media/r-header-2068/" markdown_url: "http://localhost/computer_media/r-header-2068.md" published_at: "2024-07-19T01:44:33+00:00" modified_at: "2026-03-30T21:44:53+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-sull.png" excerpt: "This cassette header reader decodes all four tape file types—program, numeric array, string array, and bytes block—straight from raw header data with a 31-byte machine code loader." 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: "Real Gagnon" slug: "real-gagnon" taxonomy: "indiv" url: "http://localhost/indiv/real-gagnon/" genre: - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_type: "Program" programmers: - name: "Real Gagnon" slug: "real-gagnon" taxonomy: "indiv" url: "http://localhost/indiv/real-gagnon/" download_url: "https://archive.org/download/timex-sinclair-software-archive/lect2068%20%28198x%29%28Gagnon%2C%20Real%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-sull.png" - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-stxh.png" article_media: - id: 16068 title: "Header-Reader for 2068" type: "article" url: "http://localhost/article/header-reader-for-2068/" media_type_tags: "Utility" --- # R-Header 2068 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) | 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 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. ## 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" ```