--- title: "FILE 1" id: 54570 type: "computer_media" slug: "file-1" url: "http://localhost/computer_media/file-1/" markdown_url: "http://localhost/computer_media/file-1.md" published_at: "2024-06-02T17:41:52+00:00" modified_at: "2026-03-30T21:42:43+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/SCR-20240714-fbhn.png" excerpt: "A complete menu-driven database system lets you design custom record layouts, sort, search, edit, and archive data to tape using embedded machine code routines." 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/" genre: - name: "Business" slug: "business" taxonomy: "genre" url: "http://localhost/type/business/" - name: "Database" slug: "database" taxonomy: "genre" url: "http://localhost/type/database/" media_contents: - id: 51214 title: "CATS Library Tape 8" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-8/" - id: 55923 title: "Timex Sinclair Public Domain Library Tape 2003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-2003/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240714-fbhn.png" related_content: - id: 55947 title: "Merge 1" type: "computer_media" url: "http://localhost/computer_media/merge-1/" media_type_tags: "Business, Database" --- # FILE 1 This program is a menu-driven flat-file database manager that supports up to 15 user-defined fields per record, with field lengths of one or two “lines” (23 or 55 bytes each). It stores records in a memory buffer starting at address 37000, using machine code routines loaded separately at address 60000 to handle sorting, searching, and memory block operations. Field layout coordinates are encoded as offsets from a base address (26734) and unpacked into screen row/column positions by dividing by 32. The program saves and loads its data buffer as a raw CODE block to tape, embedding the current record count and pointer values into fixed memory locations before the SAVE command. *** ## Program Analysis ### Program Structure The program is organized as a collection of numbered subroutine groups dispatched from a central menu at line `900`. Line `1` is a bootstrap `GO TO 900`, and line `3` initializes key system constants. Menu options 1–7 jump to lines 1000–7000 in steps of 1000, implemented via `GO TO 1000*VAL a$` at line `920`. Low-numbered lines (2–100) serve as reusable subroutines. - **Lines 2–100:** Utility subroutines (input handling, machine code invocation, screen display, pointer arithmetic) - **Lines 900–920:** Main menu display and dispatch - **Lines 1000–1230:** Add record to file - **Lines 1500–1510:** Buffer-full warning - **Lines 2000–2060:** Sort file by field - **Lines 3000–3120:** Search and edit/delete records - **Lines 4000–4030:** Load file from tape - **Lines 5000–5060:** Save file to tape - **Lines 6000–6280:** Design a new file structure - **Lines 7000–7030:** Stop / save program to tape ### Memory Layout The program uses a carefully partitioned memory map. The database record buffer begins at address `te`=37000 and grows upward toward a ceiling of `c2`=59600. Machine code routines occupy addresses 60000–60650. Field metadata (field count, addresses, lengths) is stored in the 37800–37999 region, and the filename is stored at 37880–37889. | Address Range | Purpose | | --- | --- | | 37000–37199 | Record data buffer start (`li`=37000, `te`=38000 logical start) | | 37200+ | Field name strings (9 bytes each) | | 37849–37889 | Field address table and type bytes | | 37880–37889 | File name (10 chars) | | 37993–37999 | Saved record count, buffer pointer, field count | | 60000–60649 | Machine code routines (loaded separately) | ### Machine Code Interface All computationally intensive operations are delegated to an external machine code block loaded at address 60000. The BASIC side communicates with the machine code by POKEing parameters into fixed addresses within the MC block before calling `RANDOMIZE USR` at specific entry points. Results are read back by PEEKing those same addresses afterward. The `RANDOMIZE`/`PEEK q`/`PEEK r` idiom (lines 30, 35, 45, 95, 100, etc.) is the standard Sinclair BASIC technique for extracting the low and high bytes of a 16-bit value: `RANDOMIZE n` stores `n` as a two-byte seed at system variables `q`=23670 (`SEED` low byte) and `r`=23671 (`SEED` high byte), which are then POKEd to the desired machine code parameter location. Identified machine code entry points and their apparent functions: - `USR 60000` – block move / memory operation (subroutine at line 30) - `USR 60014` – secondary block move variant (subroutine at line 35) - `USR 60028` – search for key text in record buffer (line 3010) - `USR 60151` – render field labels to screen (line 100) - `USR 60185` – sort records by field number (line 2050) - `USR 60500` – initialize/clear record area (line 61) - `USR 60570` – unknown; invoked during search/edit flow (line 80) ### Field Layout Encoding Field screen positions are packed as offsets from the base address 26734 (which corresponds to screen row 0, column 0 in the coordinate scheme used). At line `1030`, the formula `z = PEEK(37848+j*2) + 256*PEEK(37849+j*2) - 26734 + 64` recovers the field’s packed position; dividing by 32 gives the row and the remainder gives the column. The value 26734 is also used as a `RANDOMIZE` seed at line `100`, confirming it is the base screen address in this coordinate system. Field type bytes at `37889+j` encode the field length: value `1` gives a 23-byte (single-line) field, value `2` gives a 55-byte (double-line) field, and value `3` adds a further 32 bytes (line 1050). This is enforced during file design at line `6230` with the constants 23 and 55. ### Key BASIC Idioms - **Menu dispatch:**`GO TO 1000*VAL a$` at line `920` cleanly maps digit keys “1”–”7″ to entry points without a chain of `IF` statements. - **Input validation subroutine:** Lines `20`–`23` form a numeric-string validator: `z$` is checked character by character, and if valid, converted to numeric `z` via `VAL z$`. - **Keypress wait:** Lines `10`–`11` implement a flush-then-wait pattern: line `10` spins until `INKEY$` is empty (debounce), then line `11` waits for a keypress. - **Pointer dereference:** Subroutine at line `90` reads a 16-bit little-endian pointer from address `d`: `LET d = PEEK d + 256*PEEK(d+1)`. - **16-bit POKE via RANDOMIZE:** Subroutine at line `95` writes the current `SEED` bytes (set by a prior `RANDOMIZE`) into addresses `d` and `d+1`. - **Buffer sentinel:** End-of-buffer is marked with byte value 143 POKEd at line `1140`, and field separators use bytes 131 and 139 (line `1120`). ### File Design Module (Lines 6000–6280) The file design section allows the user to define up to 15 fields, each with a 9-character name and a length of 1 or 2. Field names are stored in a `DIM c$(a+1,10)` array, where the 10th character holds the length code and the `(a+1)`th row holds the filename. After confirmation, a logging phase (lines 6200–6280) encodes field addresses using the `RANDOMIZE`/PEEK idiom and writes field names, address tables, and type bytes into the 37200–37999 region. A guard at line `6225` prevents more than 20 total field-lines (fields + double-length fields). ### Tape Save/Load (Lines 4000–5060) The save routine at line `5050` POKEs the current buffer pointer `c` and record count `c5` into fixed addresses (37998–37999 and 37993–37994) before the `SAVE z$CODE li,c-36999` command, so the entire database state — including metadata — is captured in a single CODE block starting at `li`=37000. On load (line `4030`), these values are recovered with corresponding `PEEK` calls. The save is immediately followed by a `VERIFY` for data integrity. ### Anomalies and Notes - Line `2` ends with `RETURN`, making it a subroutine that assigns a long space-padded string to `a$`. The string “346688…5644…” appears to be a screen layout template or field-separator pattern printed at row 1. - Line `3` initializes constants and then does `GO TO 1`, which goes to line `1`‘s `GO TO 900`; this is the normal startup path when the program is run fresh (as opposed to a warm restart after file design). - Lines `7010`–`7025` form a self-save sequence that saves the BASIC program and the machine code block separately, followed by verifies. Line `7025` uses `CAT` and a comma after the filename, which is non-standard syntax and likely a TS2068 extension or a program quirk. - The variable `c5` tracks the record count but is never explicitly initialized in the main flow — it is set to 1 at line `6280` (after file design) or restored from tape at line `4030`. - The search module (line 3005) stores the search string at addresses 37000–37009 (i.e., `36999+j`), which overlaps with the start of the `li`=37000 record buffer. This works because the search key is consumed by the MC routine before any record data at that location would be needed — but it is a tight coupling worth noting. ## Source Code ``` 1 GO TO 900 2 LET a$="346688 5644 ": RETURN 3 LET te=38000: LET li=37000: LET q=23670: LET r=23671: LET c=te: LET c2=59600: GO TO 1 10 IF INKEY$<>"" THEN GO TO 10 11 LET b$=INKEY$: IF b$="" THEN GO TO 11 12 BEEP .003,0: RETURN 20 LET x=0: IF LEN z$=0 THEN LET x=1: RETURN 21 FOR j=1 TO LEN z$: IF z$(j)<"0" OR z$(j)>"9" THEN LET x=1 22 NEXT j: IF x=1 THEN RETURN 23 LET z=VAL z$: RETURN 30 GO SUB 85: RANDOMIZE v1: POKE 60001,PEEK q: POKE 60002,PEEK r: RANDOMIZE v2: POKE 60004,PEEK q: POKE 60005,PEEK r: RANDOMIZE v3: POKE 60009,PEEK q: POKE 60010,PEEK r: RANDOMIZE USR 60000: RETURN 35 GO SUB 85: RANDOMIZE v1: POKE 60015,PEEK q: POKE 60016,PEEK r: RANDOMIZE v2: POKE 60018,PEEK q: POKE 60019,PEEK r: RANDOMIZE v3: POKE 60023,PEEK q: POKE 60024,PEEK r: RANDOMIZE USR 60014: RETURN 40 LET a=PEEK 37997: LET b=37880: PRINT AT 0,10; INVERSE 1;" ";: FOR l=b TO b+9: PRINT INVERSE 1;CHR$ PEEK l;: NEXT l: PRINT INVERSE 1;" " 41 PRINT AT 21,0;"record ";c5;" ";TAB 13;"file space ";c2-c;" " 42 LET a=PEEK 37997: FOR l=1 TO a: LET z=PEEK (37848+l*2)+256*PEEK (37849+l*2)-26670: LET m=INT (z/32): LET n=z-m*32 43 PRINT OVER 1;AT m-1,n; INVERSE 1;" ": NEXT l 44 RETURN 45 LET v1=PEEK 37995+256*PEEK 37996: LET v2=26734: LET v3=37200: GO SUB 30: RETURN 50 GO SUB 2: PRINT AT 1,0;a$: RETURN 55 GO SUB 2: PRINT OVER 1;AT 1,0;a$: RETURN 60 RANDOMIZE te: LET d=60496: GO SUB 95: GO SUB 40 61 GO SUB 100: GO SUB 45: RANDOMIZE USR 60500: GO SUB 50: GO SUB 42 62 INPUT z$: LET d=60496: GO SUB 90: LET c3=d+1: RANDOMIZE c3: LET d=60496: GO SUB 95: IF c3>=c THEN RETURN 63 IF z$="c" THEN COPY 64 IF z$="z" THEN RETURN 65 GO TO 61 70 INPUT "Key C if OK ";b$: RETURN 80 RANDOMIZE c: LET d=60567: GO SUB 95: RANDOMIZE USR 60570: POKE 60496,PEEK 60135: POKE 60497,PEEK 60136: GO SUB 61: RETURN 85 IF c"7" THEN GO TO 900 920 CLS : GO TO 1000*VAL a$ 1000 IF PEEK 37997=0 THEN GO TO 1 1010 GO SUB 100: GO SUB 50: IF c>c2 THEN GO TO 1500 1020 GO SUB 40: GO SUB 45: GO SUB 55 1030 LET c4=c: FOR j=1 TO a: LET z=PEEK (37848+j*2)+256*PEEK (37849+j*2)-26734+64: LET m=INT (z/32): LET n=z-m*32+9 1040 PRINT FLASH 1;AT m-1,n;" ";AT m-1,n;: LET b=n: LET f=0: LET b1=23+c: IF PEEK (37889+j)=2 THEN LET b1=55+c 1050 IF PEEK (37889+j)=3 THEN LET b1=b1+32 1060 GO SUB 10: IF CODE b$>127 OR CODE b$<32 THEN GO TO 1200 1070 IF c=b1 THEN GO TO 1060 1080 PRINT b$;: LET b=b+1: IF b=32 THEN LET b=0: LET f=1: LET m=m+1 1090 POKE c,CODE b$: LET c=c+1 1100 GO TO 1060 1110 BEEP .1,24: PRINT AT m-1,n;SCREEN$ (m-1,n) 1120 POKE c,32: POKE c+1,131: POKE c+2,139: LET c=c+2 1130 NEXT j: LET c=c+1 1140 BEEP .1,0: GO SUB 70: IF b$="z" THEN LET c=c4: POKE c,143: POKE c+1,143: GO TO 1 1150 IF b$<>"c" THEN BEEP .1,-12: LET c=c4: GO SUB 45: GO TO 1010 1160 BEEP .1,12: LET c5=c5+1: GO SUB 45: GO TO 1010 1200 IF CODE b$=13 THEN GO TO 1110 1210 IF CODE b$<>12 OR (b=n AND f=0) THEN GO TO 1060 1220 LET b=b-1: IF b<0 THEN LET b=31: LET m=m-1: LET f=0 1230 PRINT AT m-1,b;" ";AT m-1,b;: LET c=c-1: GO TO 1060 1500 PRINT FLASH 1;AT 9,9;" BUFFER FULL ": BEEP .1,20: IF INKEY$="z" THEN GO TO 1 1510 GO TO 1500 2000 IF c=te THEN GO TO 1 2010 CLS : RANDOMIZE te: POKE 60173,PEEK q: POKE 60174,PEEK r 2020 LET z=0: POKE 60179,z: POKE 60180,z: POKE 60183,z 2030 INPUT "Which field?";z$: IF z$="" THEN GO TO 1 2040 GO SUB 20: IF x=1 OR z=0 OR z>a THEN GO TO 2030 2050 POKE 60182,z: RANDOMIZE USR 60185: IF PEEK 60183<>0 THEN PRINT ''"Error at ";PEEK 60173+256*PEEK 60174: INPUT z$: GO TO 2010 2060 GO SUB 60: GO TO 2010 3000 IF c=te THEN GO TO 1 3005 PRINT " SEARCH ": INPUT "Key text";a$: LET u=LEN a$: FOR j=1 TO u: POKE 36999+j,CODE a$(j): NEXT j: RANDOMIZE te-1: LET d=60135: GO SUB 95 3010 RANDOMIZE li: LET d=60137: GO SUB 95: RANDOMIZE c+1: LET d=60139: GO SUB 95: RANDOMIZE li+u: LET d=60141: GO SUB 95: POKE 60143,0: RANDOMIZE USR 60028 3020 IF PEEK 60143=0 THEN PRINT " END OF FILE ": INPUT z$: GO TO 1 3030 GO SUB 80 3035 INPUT "REPEAT";a$: IF a$<>"" THEN LET d=60565: GO SUB 90: LET te1=d: RANDOMIZE te1: LET d=60135: GO SUB 95: GO TO 3010 3040 INPUT "DELETE";a$: IF a$<>"" THEN GO TO 3100 3050 INPUT " COPY ";a$: IF a$<>"" THEN COPY 3060 INPUT "ESCAPE";a$: IF a$<>"" THEN GO TO 1 3070 GO TO 3035 3100 CLS : PRINT FLASH 1;AT 9,9;"ARE YOU SURE?": INPUT z$: IF z$<>"y" THEN GO TO 1 3110 LET d=60135: GO SUB 90: LET v2=d: LET d=60565: GO SUB 90: LET v3=d+1: LET v1=c-d+1: GO SUB 30 3120 LET d=60565: GO SUB 90: LET z=d: LET d=60135: GO SUB 90: LET c=c-(z-d)-1: LET c5=c5-1: PRINT ''TAB 10;" DELETED ": BEEP 1,-20: GO TO 1 4000 CLS : PRINT " LOADING FILE " 4030 LOAD ""CODE : LET c=PEEK 37998+256*PEEK 37999: LET c5=PEEK 37993+256*PEEK 37994: GO TO 1 5000 IF c=te THEN GO TO 1 5005 CLS : PRINT " RECORDING FILE " 5010 INPUT "File name?";z$ 5050 RANDOMIZE c: POKE 37998,PEEK q: POKE 37999,PEEK r: RANDOMIZE c5: POKE 37993,PEEK q: POKE 37994,PEEK r 5060 SAVE z$CODE li,c-36999: CLS : PRINT "Rewind for verify": VERIFY ""CODE : GO TO 1 6000 INK 9: PRINT " FORM DESIGN "''"How many fields?" 6010 INPUT z$: IF z$="z" THEN GO TO 1 6020 GO SUB 20: IF x=1 OR z=0 OR z>15 THEN GO TO 6010 6030 LET a=z: PRINT 'a;" fields"': DIM c$(a+1,10) 6040 FOR j=1 TO a: PRINT "field ";j;TAB 10; PAPER 6;" ";AT j+4,10; 6045 INPUT a$: IF a$="" THEN GO TO 6045 6050 LET c$(j, TO 9)=a$: PRINT PAPER 6;c$(j, TO 9); 6055 INPUT "Length 1 or 2?";b$: IF b$<>"1" AND b$<>"2" THEN GO TO 6055 6060 LET c$(j,10)=b$: PRINT TAB 21;"length ";b$: NEXT j 6070 INPUT "File name?";c$(a+1): PRINT AT 0,16; INVERSE 1;" ";c$(a+1): GO SUB 70: IF b$="c" THEN GO TO 6200 6080 LET x=5 6090 PRINT FLASH 1;AT x,9;" "; 6100 GO SUB 10: IF b$<>"z" AND b$<>"e" THEN GO TO 6100 6110 IF b$="e" THEN GO TO 6160 6120 PRINT AT x,9;" "; 6130 LET x=x+1 6140 IF x=5+a THEN GO TO 6080 6150 GO TO 6090 6160 LET j=x-4: INPUT a$: IF a$="" THEN GO TO 6160 6170 LET c$(j, TO 9)=a$: PRINT AT x,10; PAPER 6;c$(j, TO 9) 6180 INPUT "Length 1 or 2?";a$: IF a$<>"1" AND a$<>"2" THEN GO TO 6180 6190 LET c$(j,10)=a$: PRINT AT x,28;a$;AT x,9;" ": GO TO 6070 6200 PRINT ' FLASH 1;"Logging File Details" 6210 LET y=37200: LET x=0: FOR j=1 TO a: RANDOMIZE y-37200+26734: POKE 37849+j*2-1,PEEK q: POKE 37849+j*2,PEEK r: POKE 37889+j,VAL c$(j,10) 6215 LET y1=y-37200: LET y1=y1-32*INT (y1/32) 6220 FOR k=1 TO 9: POKE y,CODE c$(j,k): LET y=y+1: NEXT k: IF c$(j,10)="2" THEN LET x=x+1 6225 IF x+a>20 THEN PRINT AT 20,6;"Too many ""2""fields": GO TO 6080 6230 LET z=7: LET y1=y-37200: LET z=55: IF c$(j,10)="1" THEN LET z=23 6250 FOR k=1 TO z: POKE y,32: LET y=y+1: NEXT k 6260 NEXT j: RANDOMIZE y-37200: POKE 37995,PEEK q: POKE 37996,PEEK r: POKE 37997,a 6270 FOR j=1 TO 10: POKE 37879+j,CODE c$(a+1,j): NEXT j 6280 LET c=te: LET c5=1: GO TO 1 7000 PRINT AT 9,9;"END OF PROGRAM": STOP 7010 SAVE "FILE1" LINE 7030: SAVE "MC"CODE 60000,650: VERIFY "": VERIFY ""CODE 7020 STOP 7025 CLEAR 36999: CAT "file1.bin",: GO TO 3 7030 REM CLEAR 36999: LOAD ""CODE : GO TO 3 ```