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:
- Initialization and header display (lines 20–60): Clears variables, dimensions arrays, sets display attributes, peeks the disk directory base address into
EandF, defines a type-label stringD$, and prints the disk name, track/side/capacity summary. - 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$. - 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. - 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:
| Line | Type byte | LOAD form |
|---|---|---|
| 151 | 0 | LOAD /L$ (program) |
| 152 | 1 | LOAD /L$ DATA A() (numeric array) |
| 153 | 2 | LOAD /L$ DATA B$() (string array) |
| 154 | 3 | LOAD /L$ CODE |
| 155 | 4 | LOAD /L$ ABS (no RETURN — falls to 156) |
| 156 | 5 | LOAD /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 inF.USR (E+64): Fetches the next directory entry into theA$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 PIas a zero literal — avoids the number 0 in token form and is a common Sinclair space-saving trick.VAL "60",VAL "150"etc. inGO TO/GO SUBtargets — stores the line number as a short string rather than a 5-byte floating-point constant, saving memory.FOR /I TO J— the slash beforeIreuses the existing loop variable without reinitializing it, enabling paged scrolling from the current position.IF NOT B AND D>1E4at line 100 — suppresses printing of large filler values when the size fieldBis zero, printing 0 instead.POKE 32768/32769at line 130 — saves and restores a two-byte value across theGO SUB 30page-flip call, using RAM address 32768 as a scratch register.
Content
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.
