FILE 1

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

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 RangePurpose
37000–37199Record data buffer start (li=37000, te=38000 logical start)
37200+Field name strings (9 bytes each)
37849–37889Field address table and type bytes
37880–37889File name (10 chars)
37993–37999Saved record count, buffer pointer, field count
60000–60649Machine 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 2023 form a numeric-string validator: z$ is checked character by character, and if valid, converted to numeric z via VAL z$.
  • Keypress wait: Lines 1011 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 70107025 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.

Content

Appears On

Capital Area Timex Sinclair User Group’s Library Tape.
One of a series of library tapes. Programs on these tapes were renamed to a number series. This tape contained

Related Products

Related Articles

Related Content

Image Gallery

FILE 1

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<te THEN LET c=te
   86 RETURN 
   90 LET d=PEEK d+256*PEEK (d+1): RETURN 
   95 POKE d,PEEK q: POKE d+1,PEEK r: RETURN 
  100 RANDOMIZE 26734: POKE 60152,PEEK q: POKE 60153,PEEK r: RANDOMIZE 27373: POKE 60155,PEEK q: POKE 60156,PEEK r: POKE 60158,32: RANDOMIZE USR 60151: RETURN 
  900 BORDER 0: POKE 23693,7: CLS : PRINT TAB 9;"    FILE 1   "'''';"1   Add to file"'"2   Sort file"'"3   Search/Edit file"'"4   Load file from tape"'"5   Save file to tape"'"6   Design new file"'"7   Stop work"
  905 LET a$=INKEY$: IF a$="" THEN GO TO 905
  910 IF a$<"1" OR a$>"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

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

People

No people associated with this content.

Scroll to Top