--- title: "file0" id: 53863 type: "computer_media" slug: "file0" url: "http://localhost/computer_media/file0/" markdown_url: "http://localhost/computer_media/file0.md" published_at: "2024-05-13T19:05:04+00:00" modified_at: "2026-03-30T21:42:43+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A disk catalog browser with machine code internals lets you scroll through stored files and load any entry with a single keypress — dispatch included." 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: "Rudy A. Hilsmann" slug: "r-a-hilsmann" taxonomy: "indiv" url: "http://localhost/indiv/r-a-hilsmann/" genre: - name: "Disk Drive Systems" slug: "disk-drive-systems" taxonomy: "genre" url: "http://localhost/type/disk-drive-systems/" media_contents: - id: 55396 title: "SINCUS Exchange Tape 103 – Potpourri" type: "computer_media" url: "http://localhost/computer_media/sincus-exchange-tape-103-potpourri/" media_type: "Program" programmers: - name: "Rudy A. Hilsmann" slug: "r-a-hilsmann" taxonomy: "indiv" url: "http://localhost/indiv/r-a-hilsmann/" mediadate: "1986" images: - url: "http://localhost/wp-content/uploads/2024/05/file-0.png" media_type_tags: "Disk Drive Systems" --- # file0 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: | 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 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. ## 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 ```