Header Readers

Date: 198x
Type: Program
Platform(s): TS 2068

Header Readers is a dual-mode tape header analysis utility that reads and decodes the metadata blocks written at the start of tape-saved files. The program offers two operating modes: a streaming list mode (lines 10–270) that continuously reads and displays headers one after another, and a detailed single-header mode (lines 500–950) that decodes each field individually including program area size, variable area size, auto-start line, array variable names, and starting addresses. Both modes rely on short machine code routines POKEd into RAM at address 30000 (or 29957) to interface directly with the tape hardware. The list mode encodes its machine code as a space-delimited decimal string in n$ and uses VAL slicing to extract and POKE each byte, while the 2068 mode READs its routine from DATA statements. A menu at line 1000 lets the user choose between the two modes at startup.


Program Structure

The listing is divided into three functional blocks, selected from a menu at line 1000:

  1. Lines 10–270 — Streaming list mode: continuously reads tape headers and prints a compact table showing filename, length, and type remarks.
  2. Lines 500–950 — Single-header detail mode: reads one header at a time and decodes every field with descriptive labels, with an option to COPY the output or loop for another header.
  3. Lines 960–990 — A raw hex/decimal dump of the 17 bytes stored from address 30001, reachable from the detail mode flow (after line 900) but never explicitly branched to from the menu.

The entry point is line 1000, which presents a two-option menu. RUN on line 1040 restarts the whole program if neither option is chosen. Line 9900 contains a SAVE command to preserve the program.

Machine Code Injection — List Mode

Lines 40–60 install a machine code routine at address 30000. The bytes are encoded as a space-delimited decimal string in n$. The loop at line 60 iterates 16 times, each iteration calling VAL n$( TO PI) — using PI (≈3.14159) as the upper bound of a string slice, which VAL truncates to 3, effectively reading the first three characters of the remaining string — and then advances n$ forward by 4 characters with n$=a$(4 TO ). Note the reference to a$ here when the variable was named n$, which is a bug: the slice should read n$(4 TO ), so the loop as written will either error or produce incorrect POKEs depending on the value of a$ at runtime.

The DEF FN p(x) at line 40 also establishes p=30016 as a base pointer into the header buffer, used throughout lines 130–240 to PEEK decoded header fields.

Machine Code Injection — Detail Mode

Lines 510–580 use READ/DATA to POKE 44 bytes into addresses 29957–30000. The routine is invoked via RANDOMIZE USR 29957 at line 630. After the call, the decoded header byte at address 30001 holds the file type, and addresses 30002–30017 hold the filename and parameter fields.

Line 540 contains a DATA value of c (an unquoted variable reference) and NOT PI (which evaluates to 0) mixed with numeric literals. The c reference is suspicious — it would be read as the current value of the variable c during the FOR/READ loop, which is the loop control variable itself. This is almost certainly a typo for a numeric literal (likely 0 or another byte value).

Line 610 does POKE 30001,4 to pre-initialize the type byte to an invalid value (4 = “not a header”), then calls CLEAR with no argument — on most implementations this resets variables, which would undo the carefully POKEd routine. This line appears anomalous.

Header Field Decoding

Both modes decode the standard 17-byte tape header format. The field layout assumed by the program is:

OffsetFieldMode
+0File type (0=BASIC, 1=Num Array, 2=Char Array, 3=Code)Both
+1 to +10Filename (10 chars)Both
+11/+12Data length (16-bit LE)List mode
+12/+13Data length (detail: 30012/30013)Detail mode
+13/+14Parameter 1 (start address or auto-start line)List mode
+14/+15Parameter 1 (detail: 30014/30015)Detail mode
+15/+16Parameter 2 (program area length for BASIC)List mode
+16/+17Parameter 2 (detail: 30016/30017)Detail mode

Array Variable Name Decoding

In the list mode (lines 220–230), array variable names are recovered by reading the descriptor byte at p+14, dividing by 32 to get a “tier” value t, and computing the character code as s - 32*t + 96. If t=2 or t=6, a $ suffix is appended for string arrays.

In the detail mode (lines 850–860), the approach is different: for numeric arrays (type 1), two candidate names are computed as CHR$(PEEK 30015 - 64) and CHR$(PEEK 30015 - 32), joined with ” OR “; for character arrays (type 2), CHR$(PEEK 30015 - 128)+"$" and CHR$(PEEK 30015 - 96)+"$".

Notable BASIC Idioms

  • NOT PI evaluates to 0 (since PI is non-zero, NOT returns 0), used pervasively as a readable zero literal.
  • SGN PI evaluates to 1 (PI > 0), used as the FOR loop start value in line 60.
  • VAL "number" in GO TO and CLEAR statements is a standard memory-saving idiom.
  • TAB PI - LEN STR$ i at line 980 uses PI (≈3) to right-align single and double digit numbers in a column of width 3.
  • PAUSE NOT PI (line 90) pauses for 0 frames, which combined with the subsequent keypress check acts as a wait-for-key idiom.
  • Line 750 uses OVER 1 to overlay the filename a$ on an underline of underscores, producing an underlined appearance.

Bugs and Anomalies

  • Line 60 — wrong variable name: The loop advances the string with n$=a$(4 TO ) but the string being built is n$. This should be n$=n$(4 TO ). As written, this will read from a$ (likely empty or undefined), corrupting the POKEd machine code bytes after the first iteration.
  • Line 540 — bare c in DATA: The DATA statement contains the token c as a value. During the READ loop, c is the loop variable, so the byte POKEd will equal the current loop index rather than a fixed opcode. This is almost certainly a missing numeric literal.
  • Line 610 — bare CLEAR: CLEAR without an argument resets all variables. This executes after POKEing machine code into RAM, but the machine code occupies fixed numeric addresses so survives CLEAR; however, it also resets the RAMTOP, which may reclaim the POKEd area depending on implementation.
  • Line 900 — typo “Lenght”: The label prints as “Lenght:” instead of “Length:”.
  • Line 960 — unreachable from menu: The raw byte dump block at lines 960–990 is never branched to from the main menu or from the detail mode’s normal flow at line 910 (which goes to the INPUT prompt). It appears to be dead code or a debug remnant.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

    5 RUN VAL "999"
   10 REM tape analyser
   20 BORDER NOT PI:PAPER NOT PI:INK 7
   30 CLEAR VAL "29999"
   40 DEF FN p(x)= PEEK x+256* PEEK (x+1):LET p=30016
   50 LET n$="221 33 64117 17 17  0175 55205 86  5216195 48117"
   60 FOR i= SGN PI TO VAL "16":POKE 29999+i, VAL n$( TO PI):LET n$=a$(4 TO ):NEXT i
   70 PRINT INVERSE 1;"        *Tape Analyser*",
   80 PRINT "  NAME"; TAB VAL "10";"Length   Remarks"'':PLOT NOT PI, VAL "156":DRAW VAL "255", NOT PI
   90 PRINT #1;" Start tape, then press any key":PAUSE NOT PI
  100 PRINT #1; AT 1, NOT PI,,
  110 RANDOMIZE USR 30000
  120 LET n$=""
  130 LET type= PEEK p:LET l= FN p(p+11):FOR i=p+1 TO p+10:LET n$=n$+ CHR$ PEEK i:NEXT i
  140 LET l$= STR$ l:LET l$="     "(1 TO 6- LEN l$)+l$
  150 PRINT n$;l$;" ";
  160 IF type>3 THEN PRINT "Read Error":GO TO 110
  170 IF type<3 THEN GO TO 210
  180 LET s= FN p(p+13)
  190 IF s=16384 AND l=6192 THEN PRINT "SCREEN$ ":GO TO 210
  200 PRINT "CODE at: ";s:GO TO 110
  210 IF type=0 THEN GO TO 240
  220 LET s= PEEK (p+14):LET t= INT (s/32):LET l$= CHR$ (s-32*t+96):IF t=2 OR t=6 THEN LET l$=l$+"$"
  230 PRINT "Array: ";l$:GO TO 110
  240 LET l= FN p(p+13):LET s= FN p(p+15)
  250 PRINT "Basic";
  260 IF l<10000 THEN PRINT "  LINE  ";l;
  270 PRINT :GO TO 110
  500 REM "2068hdrrdr"-2068 Header Reader.
  510 BORDER NOT PI:PAPER NOT PI:INK 7:CLEAR VAL "29956":RESTORE VAL "500"
  520 FOR a=29957 TO 30000
  530 READ c:POKE a,c:NEXT a
  540 DATA 175,55,221,33,49,117,17,c, NOT PI,243
  550 DATA 245,219,255,203,255,211,255,219,244,50
  560  DATA 48,117,62,1,211,244,241,205,252,0
  570 DATA 58,48,117,211,244,219,255,203,191,211
  580 DATA 255,251,201,0
  610 POKE 30001,4:CLEAR 
  620 PRINT AT 10,8; "Start the tape-"
  630 RANDOMIZE USR 29957
  640 CLS 
  650 LET type= PEEK 30001
  660 IF type>3 THEN PRINT AT 10,9; FLASH 1;"Not a Header":GO TO 930
  670 LET a$=""
  680 FOR i=0 TO 9
  690 LET a$=a$+ CHR$ PEEK (30002+i)
  700 NEXT i
  710 LET len= VAL "PEEK 30012+256* PEEK 30013"
  720 LET start= VAL "PEEK 30014+256* PEEK 30015"
  730 LET pa= VAL "PEEK 30016+256* PEEK 30017"
  740 LET var=len-pa
  750 PRINT "Name:__________; "; OVER 1; AT 0,6;a$''
  760 IF type THEN GO TO 830
  770 PRINT "Program type:BASIC."''
  780 PRINT "Program area:";pa;"bytes."''
  790 PRINT "Variables area:";var;"bytes."''
  800 IF start>9999 THEN PRINT "no auto-start.":GO TO 910
  810 PRINT "auto-start at line";start;"."
  820 GO TO 910
  830 LET t$="Code Block."
  840 IF start=16384 AND len=6912 THEN LET t$="Screen."
  850 IF type=1 THEN LET t$="Numeric Array.":LET n$= CHR$ (PEEK 30015-64)+" OR "+ CHR$ (PEEK 30015-32)
  860 IF type=2 THEN LET t$="Character Array.":LET n$= CHR$ (PEEK 30015-128)+"$ OR "+ CHR$ (PEEK 30015-96)+"$"
  870 PRINT "type:";t$''
  880 IF type=1 OR type=2 THEN PRINT "Array Variable Name:";n$''
  890 IF type=3 THEN PRINT "Starting Address:";start''
  900 PRINT "Lenght:";len;"bytes."
  910 INPUT "Copy(y/n)?";r$
  920 IF r$="Y" OR r$="y" THEN COPY 
  930 INPUT "Read another header(y/n)?";r$
  940 IF r$="Y" OR r$="y" THEN GO TO 620
  950 STOP 
  960 CLS :PRINT "Bytes   Contents"; OVER 1; AT NOT PI, NOT PI;"____   ________"''   
  970 FOR i=1 TO 17
  980 PRINT TAB PI- LEN STR$ i;i; TAB 12- LEN STR$ PEEK (30000+i); PEEK (30000+i)
  990 NEXT i:GO TO 910
 1000 PAPER VAL "6":BORDER VAL "6":INK VAL "9":CLS 
 1010 PRINT '''' TAB VAL "10";"Header Readers"'''"      1  LIST Headers"''"      2  1 AT a time"
 1020 INPUT "Select 1, 2,  ";i:IF i= VAL "1" THEN GO TO VAL "10"
 1030 IF i= VAL "2" THEN GO TO VAL "500"
 1040 RUN 
 9900 SAVE "HdrReads 2" LINE 1

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top