file0

Developer(s): Rudy A. Hilsmann
Date: 1986
Type: Program
Platform(s): TS 2068

This program is a disk catalog browser that reads directory information from a John L. Oliger disk, displaying file entries with their type, size, and record count. It uses machine code (loaded via the DATA statements at lines 300–350 and activated by GO TO 200) to perform low-level memory operations that populate the display arrays. The BASIC shell presents a scrollable list of up to 18 entries at a time, allowing the user to navigate with any key and press ENTER to load a selected file. The LOAD dispatch mechanism at lines 151–156 uses a computed GO SUB — jumping to line 150 plus a type byte — to issue the correct LOAD variant (plain, DATA array, CODE, ABS, or VAL) for each file type.


Program Analysis

Program Structure

The program divides into four logical sections:

  1. Initialization and header display (lines 20–60): Clears variables, dimensions arrays, sets display attributes, peeks the disk directory base address into E and F, defines a type-label string D$, and prints the disk name, track/side/capacity summary.
  2. Scrollable file list (lines 60–140): A paged browser showing up to 18 directory entries at a time, with highlight-and-select navigation driven by PAUSE 0/INKEY$.
  3. Load dispatch (lines 150–156): Computes the correct LOAD form from the selected entry’s type byte and jumps to it via a computed GO SUB.
  4. Machine code installer (lines 200–350): Pokes 111 bytes of Z80 machine code into address 26715, then self-destructs with DELETE 200, and writes a save reminder in comments.

Initialization Details (Line 20)

LET A=NOT PI evaluates to 0 (since NOT PI is 0 for any non-zero value), providing a convenient zero constant reused for B, C, D, and array indexing. DIM A$(179,20) allocates a 179-row, 20-character string array used as the directory buffer; rows 178 and 179 carry the disk name and geometry metadata respectively. E is computed as PEEK 23635 + 256*PEEK 23636 + 5, locating a system variable pointer, and F=USR E calls the machine code to return the entry count. The ~ character in the PRINT statement is the FREE keyword, displaying free space.

Navigation Loop (Lines 30–50, 60–140)

Line 60 initializes the page pointer I=2 and cursor J=0. Line 70 decides whether a full page of 18 entries fits; if so it advances J by 18 and falls through to line 90, otherwise line 80 clamps J to F+1. The FOR /I TO J construct (using the loop variable I implicitly starting from the current value) pages through entries. Within the loop, RANDOMIZE USR (E+64) at line 100 calls an entry-point 64 bytes into the machine code to fetch each directory record into A$.

The highlight at line 30 uses INVERSE 1: OVER 1 to reverse the current row, then waits with PAUSE 0/INKEY$. ENTER (CHR$ 13) branches to the load dispatcher; any other key advances the cursor via GO SUB 100 and NEXT.

Computed GO SUB Dispatch (Lines 150–156)

Line 150 extracts the filename into L$ from columns 1–10 of the selected row, then executes:GO SUB VAL "150" + CODE A$(A-1, 11) + VAL "1"

The type byte stored in column 11 (values 0–5) selects one of lines 151–156, each containing the appropriate LOAD variant:

LineType byteLOAD form
1510LOAD /L$ (program)
1521LOAD /L$ DATA A() (numeric array)
1532LOAD /L$ DATA B$() (string array)
1543LOAD /L$ CODE
1554LOAD /L$ ABS (no RETURN — falls to 156)
1565LOAD /L$ VAL

Note that line 155 has no RETURN, which means after an ABS load it will fall through to line 156’s LOAD /L$ VAL and then return — this is likely a bug, though ABS loads may transfer control elsewhere before that point.

Machine Code Installer (Lines 200–230)

Lines 200–220 read 111 bytes from the DATA statements and poke them into addresses 26715–26825 (just below the microdrive map area). POKE 26711,0 patches a nearby location before DELETE 200, removes the installer routine from the BASIC program, leaving only the browser. The comment at line 230 instructs the user to run line 200 once, then save the patched version with CLEAR: SAVE /0.

Machine Code Entry Points

Two entry points are used from BASIC:

  • USR E (base address + 0): Initializes directory structures and returns the file count in F.
  • USR (E+64): Fetches the next directory entry into the A$ buffer row selected by the loop counter.

The Z80 code at line 300 begins CD 0A 00 — a CALL $000A — which is the TS2068/Spectrum ROM restart for the channel output routine, suggesting the machine code interacts with the CHAN-OPEN system. Subsequent bytes manipulate BC/DE/HL pairs consistent with block-copy operations against the microdrive map.

Notable Idioms

  • LET A=NOT PI as a zero literal — avoids the number 0 in token form and is a common Sinclair space-saving trick.
  • VAL "60", VAL "150" etc. in GO TO/GO SUB targets — stores the line number as a short string rather than a 5-byte floating-point constant, saving memory.
  • FOR /I TO J — the slash before I reuses the existing loop variable without reinitializing it, enabling paged scrolling from the current position.
  • IF NOT B AND D>1E4 at line 100 — suppresses printing of large filler values when the size field B is zero, printing 0 instead.
  • POKE 32768/32769 at line 130 — saves and restores a two-byte value across the GO SUB 30 page-flip call, using RAM address 32768 as a scratch register.

Content

Related Products

Related Articles

Related Content

Image Gallery

file0

Source Code

   10 REM 1                               2                               3                               12345678901234
   15 REM USE KEYWORDS FOR D$ IN          LINE 20
   20 CLEAR : LET A=NOT PI: LET B=A: LET C=A: LET D=A: DIM A$(179,20): DIM B$(32): DIM C$(608): INK 9: PAPER A: BORDER A: CLS : LET E=PEEK 23635+256*PEEK 23636+5: LET F=USR E: LET D$=" LINE DATA DATA CODE ABS VAL ": PRINT "DISK NAME:  ";A$(178)'CODE A$(179,1);" TRKS, ";CODE A$(179,2);" SIDES=";CODE A$(179,3)*5;"K  ~";CODE A$(179,5)*5;"K"'"--------------------------------": GO TO VAL "60"
   30 LET G=3: LET H=1: FOR /I TO J: PRINT AT G,0; INVERSE 1; OVER 1;B$: PAUSE 0: LET Q$=INKEY$: IF Q$=CHR$ 13 THEN GO TO 150
   40 PRINT AT G,0; INVERSE 0;B$: GO SUB 100: NEXT : PRINT AT 3,0;C$: IF A>F THEN RANDOMIZE USR E: GO TO 60
   50 RETURN 
   60 BEEP .08,35: LET I=2: LET J=0
   70 LET G=3: LET H=NOT PI: IF F-I>18 THEN LET J=J+18: GO TO 90
   80 LET J=F+1
   90 LET K=PEEK 32768+256*PEEK 32769: FOR /I TO J
  100 RANDOMIZE USR (E+64): PRINT AT G,0;A$(A-1, TO 10);" ";D$(B+1);TAB 17;: IF NOT B AND D>1E4 THEN PRINT 0;: GO TO 120
  110 PRINT D;
  120 PRINT TAB 22;",";C: LET G=G+1: IF H THEN RETURN 
  130 NEXT : POKE 32768,K-256*INT (K/256): POKE 32769,INT (K/256): GO SUB 30: IF A-1<=F THEN LET I=A
  140 GO TO VAL "70"
  150 LET L$=A$(A-VAL "1", TO VAL "10"): GO SUB VAL "150"+CODE A$(A-VAL "1",VAL "11")+VAL "1": STOP 
  151 LOAD /L$: RETURN 
  152 LOAD /L$ DATA A(): RETURN 
  153 LOAD /L$ DATA B$(): RETURN 
  154 LOAD /L$CODE : RETURN 
  155 LOAD /L$ABS 
  156 LOAD /L$VAL : RETURN 
  200 RESTORE 300: FOR X=26715 TO 26825
  210 READ L: POKE X,L
  220 NEXT X: POKE 26711,0: DELETE 200,
  230 REM **********************   * USE GOTO 200 TO ACTIVATE  *   *     THEN SAVE USING       *   *     CLEAR: SAVE /0        *   *****************************
  250 REM from  SMUG BYTES 12-87                by R. A. Hilsmann
  300 DATA 205,10,0,42,75,92,17,32,0,25,235,33,32,38,1,212,13
  310 DATA 237,176,33,16,38,14,16,237,176,229,235,17,4,0,25,235,33,0,38,14,16,237
  320 DATA 176,225,17,20,0,62,128,190,40,4,3,25,48,249,42,75,92,17,42,0,25,34
  330 DATA 0,128,223,42,75,92,17,9,0,25,237,91,0,128,26,119,213,17,6,0,25,209
  340 DATA 19,26,119,19,35,26,119,213,17,5,0,25,209,19,26,119,19,35,26,119,33,16
  350 DATA 0,25,34,0,128,201

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

Scroll to Top