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:
- Lines 1000–1430: Data entry
- Lines 2000–2320: Delete a data block
- Lines 3000–3720: Edit a data block
- Lines 4000–4799: Keyword search
- Lines 5000–5080: Save data to cassette
Lines 5060–5080 (CLEAR, SAVE "1025", 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 ZX81PAUSE 40000is 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.
Content
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
