--- title: "Mini Data Base" id: 57498 type: "computer_media" slug: "mini-data-base" url: "http://localhost/computer_media/mini-data-base/" markdown_url: "http://localhost/computer_media/mini-data-base.md" published_at: "2024-10-05T21:05:30+00:00" modified_at: "2026-04-03T07:58:27+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/253_MDB.png" excerpt: "A complete five-function flat-file database packed into a single string variable, using ASCII delimiters and keyword search with optional printer output." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Database" slug: "database" taxonomy: "genre" url: "http://localhost/type/database/" media_contents: - id: 56737 title: "Timex Sinclair Public Domain Library Tape 1006" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1006/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/253_MDB.png" media_type_tags: "Database" --- This program implements a flat-file mini database for the ZX81/TS1000 that stores all records in a single string variable A$, using CHR$ 111 (“o”) as a record separator and CHR$ 67–70 as field delimiters within each record. It supports five operations: data entry (up to four fields per record), deletion, editing, keyword search, and saving the data string to cassette tape. The menu dispatches to subroutines using the idiom GOTO VAL Y$*1000, where the user’s key press (codes 29–34 representing digits 1–5 in ZX81 character set) selects the 1000-level line block. The search routine includes a COPY command triggered by pressing “Z” to produce a printer hard copy of found records. *** ## Program Analysis ### Program Structure The program is organised into discrete 1000-line blocks, each corresponding to a menu option. The main menu occupies lines 100–260, and execution branches via `GOTO VAL Y$*1000` at line 260, dispatching to one of five routines: 1. Lines 1000–1430: Data entry 2. Lines 2000–2320: Delete a data block 3. Lines 3000–3720: Edit a data block 4. Lines 4000–4799: Keyword search 5. Lines 5000–5080: Save data to cassette Lines 5060–5080 (`CLEAR`, `SAVE "1025\3"`, `RUN`) appear after the SAVE routine’s `GOTO 100` and are therefore unreachable during normal execution. They likely represent a vestigial attempt at saving the program itself. ### Data Storage Model All database records are stored in the single string variable `A$`, initialised at line 10 to `CHR$ 111` (the letter “o”). Each record consists of four fields separated by specific delimiter characters: | CHR$ code | Character | Role | | --- | --- | --- | | 111 | o | Record separator (start/end of record) | | 67 | C | End of Subject field | | 68 | D | End of Item 1 field | | 69 | E | End of Item 2 field | | 70 | F | End of Item 3 / Last Item field | This scheme is fragile: any user input containing the letters C, D, E, F, or lowercase “o” will corrupt the delimiter structure. This is a significant bug — the program does not sanitise or validate field input against these reserved characters. ### Menu Dispatch Idiom Line 230 gates valid input by checking `CODE Y$<29 OR CODE Y$>34`. On the ZX81, character codes 29–33 correspond to the digit characters 1–5. Line 260 uses `GOTO VAL Y$*1000` to jump directly to the appropriate 1000-block, a compact and efficient dispatch mechanism that avoids a chain of IF statements. ### Search and Navigation The keyword search at line 4000 scans `A$` with a `FOR/NEXT` loop comparing substrings of length `LEN C$`. When a match is found, the code walks backwards from position `N` to locate the preceding `CHR$ 111` record boundary (variable `A`), then forwards to find the closing boundary (variable `B`), isolating the record into `D$`. The subroutine at line 4800 prints up to the next delimiter and then trims `D$` in place using `LET D$=D$(N TO LEN D$)`. There is a bug at line 4360: the search for the second field delimiter uses `CODE D$(N)=63` (character “?”) rather than the expected `68` (“D”). This means Item 1 will never be correctly located or printed during a search display. ### Delete and Edit Logic Deletion (line 2310) reconstructs `A$` by concatenating the portion before the found record with the portion after it: `LET A$=A$( TO A)+A$(B+1 TO LEN A$)`. This correctly excises the block including its trailing `CHR$ 111` separator. The edit routine at line 3710 uses a similar splice: `LET A$=A$( TO A)+F$+A$(B TO LEN A$)`. Note that unlike the delete case, it preserves the trailing boundary by including `A$(B TO ...)` rather than `A$(B+1 TO ...)`, which is correct since the rebuilt `F$` does not include the closing `CHR$ 111`. The edit flow contains dead code: after locating the record into `D$` at line 3170, execution jumps to 3800 for confirmation, but a “not found” retry at line 3850 sends control to `3070` (the NEXT of the search loop), which may iterate incorrectly after the loop variable is exhausted. Line 3175 (`GOTO 3800`) causes lines 3180 onwards to be unreachable on the first pass; they are only reached from line 3840 (`IF INKEY$="Y" THEN GOTO 3180`), which is the intended confirmation path. ### FAST/SLOW Usage The program switches between `FAST` and `SLOW` modes frequently. `SLOW` is invoked before INPUT statements to enable the display (required for the user to see what they are typing on a ZX81), and `FAST` is restored for processing and printing loops. This is standard ZX81 practice to balance display visibility with execution speed. ### Notable Techniques - The prompt at line 210 is printed twice: first as normal video text, then overprinted in inverse video using the `%` escape sequences, producing a highlighted instruction without needing separate PRINT AT coordinates. - `PAUSE 40000` (lines 2290, 3830, 4705) is used as an indefinite wait for a keypress, exploiting the fact that on a ZX81 `PAUSE 40000` is effectively infinite until interrupted. - The cassette SAVE at line 5040 saves only the `A$` variable (the entire database), not the program itself, allowing the data to be reloaded independently. ## Source Code ``` 5 REM "DBAS%E" 10 LET A$=CHR$ 111 100 FAST 110 CLS 120 PRINT AT 1,10;"MINI DATA BASE" 130 PRINT AT 4,0;"TO ENTER DATA";TAB 30;1 140 PRINT AT 6,0;"TO DELETE DATA";TAB 30;2 150 PRINT AT 8,0;"TO EDIT DATA";TAB 30;3 160 PRINT AT 10,0;"TO SEARCH FOR DATA";TAB 30;4 170 PRINT AT 12,0;"TO SAVE DATA ON TAPE";TAB 30;5 200 SLOW 210 PRINT AT 21,6;" ENTER ONE OF ABOVE";AT 21,6;"% %E%N%T%E%R% %O%N%E% %O%F% %A%B%O%V%E% " 220 LET Y$=INKEY$ 230 IF CODE Y$<29 OR CODE Y$>34 THEN GOTO 210 240 FAST 250 CLS 260 GOTO VAL Y$*1000 1000 REM %D%A%T%A% %E%N%T%R%Y 1010 CLS 1020 PRINT 1030 PRINT "SUBJECT?" 1035 SLOW 1040 INPUT B$ 1045 PRINT ,,B$ 1050 IF B$="" THEN GOTO 100 1060 LET A$=A$+B$+CHR$ 67 1070 PRINT ,,"ITEM NO. 1?" 1080 INPUT B$ 1085 PRINT ,,B$ 1090 LET A$=A$+B$+CHR$ 68 1100 PRINT ,,"ITEM NO. 2?" 1110 INPUT B$ 1115 PRINT ,,B$ 1120 LET A$=A$+B$+CHR$ 69 1130 PRINT ,,"ITEM NO. 3?" 1140 INPUT B$ 1145 PRINT ,,B$ 1150 LET A$=A$+B$+CHR$ 70 1400 PRINT ,,"LAST ITEM?" 1410 INPUT B$ 1415 PRINT ,,B$ 1416 PAUSE 100 1420 LET A$=A$+B$+CHR$ 111 1430 GOTO 1000 2000 REM %D%E%L%E%T%E% %D%A%T%A% %B%L%O%C%K 2010 PRINT AT 10,0;"ENTER KEYWORD OF DATA TO BE DELETED :::" 2020 SLOW 2030 INPUT C$ 2040 FAST 2050 FOR N=1 TO LEN A$-LEN C$+1 2060 IF A$(N TO N+LEN C$-1)=C$ THEN GOTO 2200 2070 NEXT N 2080 CLS 2090 PRINT AT 10,0;"DATA NOT IN FILE :::" 2100 PAUSE 500 2120 GOTO 100 2200 FOR A=N TO 1 STEP -1 2210 IF CODE A$(A)=111 THEN GOTO 2230 2220 NEXT A 2230 FOR B=N TO LEN A$ 2240 IF CODE A$(B)=111 THEN GOTO 2260 2250 NEXT B 2260 CLS 2270 PRINT ,,A$(A+1 TO B-1) 2280 PRINT ,,"IS THIS THE DATA BLOCK YOU WANT DELETED?" 2290 PAUSE 40000 2300 IF INKEY$="N" THEN GOTO 2070 2310 LET A$=A$( TO A)+A$(B+1 TO LEN A$) 2320 GOTO 100 3000 REM %E%D%I%T% %D%A%T%A: 3010 PRINT ,,"ENTER KEYWORD?" 3020 SLOW 3030 INPUT C$ 3040 FAST 3050 FOR N=1 TO LEN A$-LEN C$+1 3060 IF A$(N TO N+LEN C$-1)=C$ THEN GOTO 3100 3070 NEXT N 3080 GOTO 2080 3100 FOR A=N TO 1 STEP -1 3120 IF CODE A$(A)=111 THEN GOTO 3140 3130 NEXT A 3140 FOR B=N TO LEN A$ 3150 IF CODE A$(B)=111 THEN GOTO 3170 3160 NEXT B 3170 LET D$=A$(A+1 TO B-1) 3175 GOTO 3800 3180 FOR I=1 TO LEN D$ 3190 IF CODE D$(I)=67 THEN GOTO 3210 3200 NEXT I 3210 CLS 3220 PRINT ,,"SUBJECT IS ";D$( TO I-1) 3230 PRINT ,,"ENTER NEW SUBJECT OR PRESS ENTERFOR NO CHANGE?" 3240 SLOW 3250 INPUT E$ 3260 IF E$="" THEN LET E$=D$( TO I-1) 3265 LET D$=E$+D$(I TO LEN D$) 3270 FAST 3275 LET F$=E$+CHR$ 67 3280 CLS 3290 LET C=I+1 3300 FOR I=C TO LEN D$ 3310 IF CODE D$(I)=68 THEN GOTO 3330 3320 NEXT I 3330 PRINT ,,"ITEM 1 IS ";D$(C TO I-1) 3340 PRINT ,,"ENTER NEW ITEM OR PRESS ENTER FOR NO CHANGE?" 3350 SLOW 3360 INPUT E$ 3370 IF E$="" THEN LET E$=D$(C TO I-1) 3380 LET F$=F$+E$+CHR$ 68 3390 FAST 3400 CLS 3410 LET C=I+1 3420 FOR I=C TO LEN D$ 3430 IF CODE D$(I)=69 THEN GOTO 3450 3440 NEXT I 3450 PRINT ,,"ITEM 2 IS ";D$(C TO I-1) 3460 PRINT ,,"ENTER NEW ITEM OR PRESS ENTER FOR NO CHANGE?" 3465 SLOW 3470 INPUT E$ 3480 IF E$="" THEN LET E$=D$(C TO I-1) 3490 LET F$=F$+E$+CHR$ 69 3500 FAST 3510 CLS 3520 LET C=I+1 3530 FOR I=C TO LEN D$ 3540 IF CODE D$(I)=70 THEN GOTO 3560 3550 NEXT I 3560 PRINT ,,"ITEM 3 IS ";D$(C TO I-1) 3575 SLOW 3580 INPUT E$ 3590 IF E$="" THEN LET E$=D$(C TO I-1) 3600 LET F$=F$+E$+CHR$ 70 3610 FAST 3620 CLS 3630 LET C=I+1 3640 PRINT ,,"LAST ITEM IS ";D$(C TO LEN D$) 3650 PRINT ,,"ENTER NEW ITEM OR PRESS ENTER FOR NO CHANGE?" 3655 SLOW 3660 INPUT E$ 3670 IF E$="" THEN LET E$=D$(C TO LEN D$) 3680 LET F$=F$+E$ 3690 FAST 3700 CLS 3710 LET A$=A$( TO A)+F$+A$(B TO LEN A$) 3720 GOTO 100 3800 CLS 3810 PRINT ,,D$ 3820 PRINT ,,,,"IS THIS THE CORRECT DATA BLOCK?" 3830 PAUSE 40000 3840 IF INKEY$="Y" THEN GOTO 3180 3850 GOTO 3070 4000 REM %K%E%Y%W%O%R%D% %S%E%A%R%C%H% 4010 CLS 4020 PRINT AT 10,0;"ENTER KEYWORD OR PHRASE?" 4030 SLOW 4040 INPUT C$ 4050 FAST 4060 CLS 4070 FOR N=1 TO LEN A$-LEN C$+1 4080 IF A$(N TO N+LEN C$-1)=C$ THEN GOTO 4200 4090 NEXT N 4100 PRINT AT 10,0;"NOT IN FILE :::" 4120 PAUSE 500 4130 GOTO 100 4200 LET NN=N 4205 FOR A=N TO 1 STEP -1 4210 IF A$(A)=CHR$ 111 THEN GOTO 4230 4220 NEXT A 4230 FOR B=N TO LEN A$ 4240 IF A$(B)=CHR$ 111 THEN GOTO 4300 4250 NEXT B 4300 LET D$=A$(A TO B) 4310 FOR N=1 TO LEN D$ 4320 IF CODE D$(N)=67 THEN GOTO 4340 4330 NEXT N 4340 GOSUB 4800 4350 FOR N=1 TO LEN D$ 4360 IF CODE D$(N)=63 THEN GOTO 4380 4370 NEXT N 4380 GOSUB 4800 4390 FOR N=1 TO LEN D$ 4400 IF CODE D$(N)=69 THEN GOTO 4420 4410 NEXT N 4420 GOSUB 4800 4430 FOR N=1 TO LEN D$ 4440 IF CODE D$(N)=70 THEN GOTO 4460 4450 NEXT N 4460 GOSUB 4800 4700 PRINT D$(2 TO LEN D$) 4705 PAUSE 40000 4710 IF INKEY$="Z" THEN COPY 4720 CLS 4730 FOR N=NN+1 TO LEN A$-LEN C$+1 4740 GOTO 100 4799 STOP 4800 PRINT D$(2 TO N-1) 4810 PRINT 4820 LET D$=D$(N TO LEN D$) 4830 RETURN 5000 REM % %S%A%V%E% %D%A%T%A% %O%N% %C%A%S%S%E%T%T%E% 5010 PRINT ,,"ENTER FILE NAME, PREPARE","RECORDER AND PRESS ENTER:::" 5020 SLOW 5030 INPUT Z$ 5040 SAVE Z$ 5050 GOTO 100 5060 CLEAR 5070 SAVE "1025%3" 5080 RUN ```