File Manager

This file is part of and Timex Sinclair Public Domain Library Tape 1001. Download the collection to get this file.
Developer(s): Russell King
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Database

File Manager is a general-purpose flat-file database program for the Timex/Sinclair 1000, published in Microcomputing Magazine (March 1983) by Russell King. It supports up to nine menu-driven operations: adding, changing, deleting, listing, searching, sorting, and saving records, plus field configuration and a field-listing view. Records are stored in a two-dimensional string array I$(M,J), with a companion array D(N1,2) holding the start and end character positions of each field within a record row, allowing variable-width fields packed into fixed-length strings. The bubble sort routine (lines 6170–6260) uses FAST/SLOW bracketing to speed up the inner loop, and the GOSUB dispatch at line 570 uses the arithmetic expression A*1000 to jump directly to the handler for the chosen menu option.


Program Analysis

Program Structure

The program is organized into discrete subroutine blocks, each anchored at a round-thousands line number corresponding to a menu choice. The main menu occupies lines 200–580; each numbered option maps directly to a subroutine at N*1000:

Menu OptionFunctionEntry Point
1Add Record1000
2Change Record2000
3Delete Record3000
4List All Records4000
5Search For A Record5000
6Sort Records6000
7Save Records To Tape7000
8Set Up New List File8000
9List Fields9000

The computed dispatch at line 570 (GOSUB A*1000) is the central routing mechanism. Each subroutine ends with RETURN back to line 580, which loops to the main menu at line 210.

Data Model

The database is implemented entirely with string arrays. I$(M,J) is a two-dimensional array where M is the maximum number of records and J is the total character width of all fields combined. The companion two-dimensional numeric array D(N1,2) stores the start (D(I,1)) and end (D(I,2)) character positions of each field within a record row. Field names of up to K characters are stored in N$(N1,K).

This packing scheme lets the program access any field of any record with a slice expression such as I$(N,D(I,1) TO D(I,2)), which appears throughout the listing. Direct assignment to a slice (INPUT I$(N,D(I,1) TO D(I,2))) writes into the record in-place, which is a concise way to update a field without rebuilding the whole string.

Setup Routine (lines 8000–8950)

The setup routine prompts for a list name (L$), the number of fields (N1), a maximum description width (K, capped at 15), and then iterates over each field to collect its name and length. It builds the D array by accumulating a running offset J. After all fields are defined, it estimates the maximum number of records with the formula:

M = INT((8000 - N1*(31-L+12)) / J)

This heuristic attempts to compute available RAM headroom. The user may then choose a smaller value, and DIM I$(M,J) is executed to allocate the record array dynamically at runtime.

Computed GOSUB Dispatch

Line 570 uses GOSUB A*1000, evaluating the arithmetic expression at runtime to branch into the correct subroutine. This is a well-known idiom that replaces a chain of IF … THEN GOTO tests with a single line. The input is validated at line 520 to ensure A is in the range 1–9 before the dispatch executes.

Bubble Sort (lines 6000–6950)

The sort routine is a straightforward O(n²) bubble sort using two nested FOR loops over record indices J and K. The comparison is performed on the selected field slice of each record. When a swap is needed, the entire record string is exchanged via a temporary string B$:

  • LET B$=I$(J)
  • LET I$(J)=I$(K)
  • LET I$(K)=B$

The entire sort loop is bracketed in FAST/SLOW to suppress display refresh during the potentially lengthy operation.

Search Routine (lines 5000–5950)

The search asks the user to select a field by answering Y/N to each field name in turn, then accepts a search string. The string is clipped to the field width if necessary. The comparison at line 5170 tests whether the entered string matches the beginning of the field content using a computed end position: A = LEN(A$) - 1 + D(I,1), enabling prefix matching. A flag Q allows early exit if the user responds N to “Continue Search?”, and flag F tracks whether any match was found.

Delete Routine (lines 3000–3950)

Deletion shifts all records above the deleted index down by one position in a FOR loop (LET I$(I)=I$(I+1)), then decrements N. This is also bracketed in FAST/SLOW. If the record to be deleted is the last one (A=N), the shift loop is skipped entirely and only N is decremented.

List and Print Routines (lines 4000–4950)

The listing routine allows the user to select which fields to display. Selected fields are stored in the array A(N1) by index, and the outer loop iterates over records while the inner loop iterates over the selected fields. Both screen output and printer output (via LPRINT) paths are provided, chosen by the user entering S or P.

Tape Save (lines 7000–7900)

The save routine uses SAVE L$ to save the entire program (including its variable arrays containing the data) under the list name as the filename. After saving, it jumps to line 100, which does not exist — this is an intentional technique to force a program restart or error exit after the save.

Notable Techniques and Idioms

  • GOSUB A*1000 — arithmetic expression as subroutine address, replacing a multi-branch IF chain.
  • Packed field slices in a 2-D string array with a separate offset table, emulating a record structure.
  • Direct INPUT into a string slice to update a field in place.
  • FAST/SLOW bracketing around sort, delete shift, and list-selection loops for performance.
  • Frequent SCROLL calls throughout, used instead of CLS to preserve context on the 1000’s small screen.
  • Line 9910 (SAVE "1000%8") with inverse-video digit in the filename is an auto-run save of the program itself.

Bugs and Anomalies

  • Line 3270 uses N$(1) (hardcoded index 1) instead of N$(I) when displaying the record to be deleted. This means only the first field name is shown for every field during delete confirmation, rather than the correct label for each field.
  • Line 1150 checks A$(1)="Y" after inputting A$ at line 1140, but if the user enters an empty string, accessing A$(1) on a zero-length string will cause an error. The same pattern appears in several other routines (e.g., lines 2250, 3140, 3310).
  • At line 1030, if N exceeds M the code sets N=M and prints an error, but does not decrement N back; the pre-increment at line 1010 has already run, so the count becomes inconsistent.
  • The maximum-records formula at line 8380 is a rough heuristic and does not precisely reflect the ZX81/TS1000 memory model; it may over- or under-estimate available space depending on the RAMTOP setting.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10001 – 10050.

Related Products

Related Articles

Think the Sinclair “toy” computer is too small for serious applications? Well, here’s a file manager program that lest you...

Related Content

Image Gallery

File Manager

Source Code

   1 REM "FILE MANAGER"
   2 REM 
   3 REM RUSSELL KING
   4 REM MICROCOMPUTING MAGAZINE
   5 REM MARCH 1983
   6 REM 
   7 REM A LISTS PROGRAM FOR THE         TIMEX/SINCLAIR 1000
   8 REM 
 200 REM %M%A%I%N% %M%E%N%U% % % % % % % % % % % % % % 
 210 CLS 
 215 SLOW 
 220 PRINT TAB 7;"FILE MANAGER"
 230 PRINT 
 240 PRINT "1..ADD RECORD"
 250 PRINT 
 260 PRINT "2..CHANGE RECORD"
 270 PRINT 
 280 PRINT "3..DELETE RECORD"
 290 PRINT 
 300 PRINT "4..LIST ALL RECORDS"
 310 PRINT 
 320 PRINT "5..SEARCH FOR A RECORD"
 330 PRINT 
 340 PRINT "6..SORT RECORDS"
 350 PRINT 
 360 PRINT "7..SAVE RECORDS TO TAPE"
 370 PRINT 
 380 PRINT "8..SET UP NEW LIST FILE"
 390 PRINT 
 395 PRINT "9..LIST FIELDS"
 400 PRINT 
 410 PRINT 
 500 PRINT "WHICH DO YOU WISH TO DO?";
 510 INPUT A
 515 LET A=INT A
 520 IF A>0 AND A<10 THEN GOTO 560
 530 CLS 
 540 PRINT "PLEASE CHOOSE 1-9"
 550 GOTO 230
 560 CLS 
 570 GOSUB A*1000
 580 GOTO 210
\n1000 REM %A%D%D% %R%E%C%O%R%D%S% % % % % % % % % % % % 
\n1010 LET N=N+1
\n1015 SCROLL 
\n1020 PRINT "RECORD NUMBER ";N
\n1030 IF N<=M THEN GOTO 1060
\n1032 LET N=M
\n1035 SCROLL 
\n1040 PRINT "NO MORE RECORDS CAN BE ADDED"
\n1050 GOTO 1920
\n1060 FOR I=1 TO N1
\n1070 SCROLL 
\n1080 PRINT N$(I);
\n1090 INPUT I$(N,D(I,1) TO D(I,2))
\n1095 PRINT I$(N,D(I,1) TO D(I,2));
\n1100 SCROLL 
\n1110 NEXT I
\n1120 SCROLL 
\n1130 PRINT "CHANGE ANYTHING? ";
\n1140 INPUT A$
\n1145 SCROLL 
\n1150 IF A$(1)="Y" THEN GOTO 1060
\n1160 SCROLL 
\n1170 PRINT "RECORD ";N;" ADDED"
\n1180 SCROLL 
\n1900 PRINT "ADD MORE RECORDS?";
\n1910 INPUT A$
\n1920 SCROLL 
\n1930 IF A$(1)="Y" THEN GOTO 1000
\n1950 RETURN 
\n2000 REM %C%H%A%N%G%E% %R%E%C%O%R%D% % % % % % % % % % 
\n2010 SCROLL 
\n2011 SCROLL 
\n2020 PRINT "TO CHANGE A RECORD, YOU MUST"
\n2030 SCROLL 
\n2040 PRINT "ENTER THE RECORD NUMBER FOR"
\n2050 SCROLL 
\n2060 PRINT "THAT RECORD. DO YOU WISH TO"
\n2070 SCROLL 
\n2080 PRINT "SEARCH FOR THE RECORD NUMBER?";
\n2090 INPUT A$
\n2095 SCROLL 
\n2100 IF A$(1)="Y" THEN GOSUB 5000
\n2110 CLS 
\n2120 SCROLL 
\n2130 PRINT "RECORD NUMBER TO CHANGE: ";
\n2140 INPUT A
\n2145 LET A=INT A
\n2150 SCROLL 
\n2160 IF A>0 AND A<=N THEN GOTO 2200
\n2170 PRINT "INVALID RECORD NUMBER"
\n2180 GOTO 2900
\n2200 FOR I=1 TO N1
\n2210 SCROLL 
\n2220 PRINT N$(I);I$(A,D(I,1) TO D(I,2))
\n2230 SCROLL 
\n2240 PRINT TAB 10;"CHANGE?";
\n2250 INPUT A$
\n2255 SCROLL 
\n2260 IF A$(1)<>"Y" THEN GOTO 2300
\n2270 PRINT N$(I);
\n2280 INPUT I$(A,D(I,1) TO D(I,2))
\n2285 PRINT I$(A,D(I,1) TO D(I,2))
\n2290 SCROLL 
\n2300 NEXT I
\n2800 SCROLL 
\n2810 PRINT "FINISHED WITH RECORD ";A
\n2900 SCROLL 
\n2910 PRINT "CHANGE OTHER RECORDS?";
\n2920 INPUT A$
\n2930 SCROLL 
\n2940 IF A$(1)="Y" THEN GOTO 2000
\n2950 RETURN 
\n3000 REM %D%E%L%E%T%E% %A% %R%E%C%O%R%D% % % % % % % % 
\n3010 IF N>0 THEN GOTO 3060
\n3020 SCROLL 
\n3030 PRINT "NO RECORDS IN FILE"
\n3040 GOTO 3330
\n3060 SCROLL 
\n3070 PRINT "ITEMS ARE DELETED BY RECORD"
\n3080 SCROLL 
\n3090 PRINT "NUMBER. RECORD NUMBERS MAY"
\n3100 SCROLL 
\n3110 PRINT "CHANGE AFTER AN ITEM IS DELETED"
\n3120 SCROLL 
\n3130 PRINT "DO YOU WISH TO SEARCH FOR THE"
\n3132 SCROLL 
\n3135 PRINT "RECORD?";
\n3140 INPUT A$
\n3150 SCROLL 
\n3160 IF A$(1)="Y" THEN GOSUB 5000
\n3170 SCROLL 
\n3180 PRINT "RECORD NUMBER TO DELETE:";
\n3190 INPUT A
\n3195 LET A=INT A
\n3200 IF A>0 AND A<=N THEN GOTO 3250
\n3210 SCROLL 
\n3220 PRINT "INVALID RECORD NUMBER"
\n3230 GOTO 3900
\n3250 FOR I=1 TO N1
\n3260 SCROLL 
\n3270 PRINT N$(1);I$(A,D(I,1) TO D(I,2))
\n3280 NEXT I
\n3290 SCROLL 
\n3300 PRINT "DELETE THIS RECORD?";
\n3310 INPUT A$
\n3320 IF A$(1)="Y" THEN GOTO 3360
\n3330 SCROLL 
\n3340 PRINT "DELETE CANCELLED"
\n3350 GOTO 3900
\n3360 IF A=N THEN GOTO 3450
\n3400 FAST 
\n3405 FOR I=A TO N-1
\n3410 LET I$(I)=I$(I+1)
\n3420 NEXT I
\n3430 SLOW 
\n3450 LET N=N-1
\n3460 SCROLL 
\n3470 PRINT "RECORD DELETED"
\n3900 SCROLL 
\n3910 PRINT "DELETE ANY OTHER RECORDS?";
\n3920 INPUT A$
\n3930 SCROLL 
\n3940 IF A$(1)="Y" THEN GOTO 3010
\n3950 RETURN 
\n4000 REM %L%I%S%T% %R%E%C%O%R%D%S% % % % % % % % % % % 
\n4010 SCROLL 
\n4015 LET A=0
\n4020 PRINT "PRINT ALL RECORD FIELDS?";
\n4030 INPUT A$
\n4040 IF A$(1)="Y" THEN LET A=1
\n4045 SCROLL 
\n4050 FAST 
\n4060 FOR I=1 TO N1
\n4070 LET A(I)=A
\n4075 IF A=1 THEN LET A(I)=I
\n4080 NEXT I
\n4090 SLOW 
\n4095 LET J=N1
\n4100 IF A=1 THEN GOTO 4220
\n4110 SCROLL 
\n4120 PRINT "ENTER Y FOR EACH FIELD"
\n4121 SCROLL 
\n4123 PRINT "YOU WANT PRINTED:"
\n4125 LET J=0
\n4130 SCROLL 
\n4140 FOR I=1 TO N1
\n4150 SCROLL 
\n4160 PRINT N$(I);" ?";
\n4170 INPUT A$
\n4175 PRINT A$(1)
\n4180 IF A$(1)<>"Y" THEN GOTO 4210
\n4190 LET J=J+1
\n4200 LET A(J)=I
\n4210 NEXT I
\n4220 SCROLL 
\n4230 PRINT "PRINT TO SCREEN OR PRINTER (S/P)"
\n4240 INPUT A$
\n4250 IF A$(1)="P" THEN GOTO 4600
\n4300 SCROLL 
\n4305 SCROLL 
\n4310 PRINT TAB 5;L$;" LIST"
\n4320 SCROLL 
\n4330 FOR I=1 TO N
\n4340 FOR K=1 TO J
\n4350 SCROLL 
\n4360 PRINT N$(A(K));I$(I,D(A(K),1) TO D(A(K),2))
\n4370 NEXT K
\n4380 SCROLL 
\n4390 NEXT I
\n4400 GOTO 4900
\n4600 FAST 
\n4610 LPRINT TAB 5;L$;" LIST"
\n4620 LPRINT 
\n4630 FOR I=1 TO N
\n4640 FOR K=1 TO J
\n4650 LPRINT 
\n4660 LPRINT N$(A(K));I$(I,D(A(K),1) TO D(A(K),2))
\n4670 NEXT K
\n4680 LPRINT 
\n4690 NEXT I
\n4700 SLOW 
\n4900 SCROLL 
\n4910 PRINT "ANOTHER LIST?";
\n4920 INPUT A$
\n4930 IF A$(1)="Y" THEN GOTO 4000
\n4950 RETURN 
\n5000 REM %S%E%A%R%C%H% %F%O%R% %R%E%C%O%R%D% % % % % % 
\n5010 SCROLL 
\n5020 PRINT "WHICH FIELD TO SEARCH ON:"
\n5030 FOR I=1 TO N1
\n5040 SCROLL 
\n5050 PRINT N$(I);" ?";
\n5060 INPUT A$
\n5070 IF A$(1)="Y" THEN GOTO 5100
\n5080 NEXT I
\n5085 SCROLL 
\n5090 PRINT "SELECTION CANCELLED"
\n5095 GOTO 5900
\n5100 SCROLL 
\n5110 PRINT "ENTER THE SEARCH STRING";
\n5120 INPUT A$
\n5122 LET A=LEN A$
\n5125 IF A<1 THEN GOTO 5110
\n5126 IF A<=(D(I,2)-D(I,1)+1) THEN GOTO 5129
\n5127 LET A=(D(I,2)-D(I,1)+1)
\n5128 LET A$=A$(1 TO A)
\n5129 LET A=A-1+D(I,1)
\n5130 LET F=0
\n5140 LET Q=0
\n5150 FAST 
\n5160 FOR J=1 TO N
\n5170 IF A$=I$(J,D(I,1) TO A) THEN GOSUB 5500
\n5180 IF Q=1 THEN GOTO 5200
\n5190 NEXT J
\n5200 SLOW 
\n5210 SCROLL 
\n5220 PRINT "SEARCH COMPLETE"
\n5230 SCROLL 
\n5240 IF F=0 THEN PRINT "RECORD NOT FOUND"
\n5250 GOTO 5900
\n5500 SCROLL 
\n5504 SCROLL 
\n5505 PRINT "RECORD NUMBER ";J
\n5510 FOR K=1 TO N1
\n5520 SCROLL 
\n5530 PRINT N$(K);I$(J,D(K,1) TO D(K,2))
\n5540 NEXT K
\n5550 SCROLL 
\n5560 PRINT "CONTINUE SEARCH?";
\n5570 INPUT B$
\n5580 IF B$(1)="N" THEN LET Q=1
\n5590 LET F=1
\n5600 RETURN 
\n5900 SCROLL 
\n5910 PRINT "ANOTHER SEARCH?";
\n5920 INPUT A$
\n5930 IF A$(1)="Y" THEN GOTO 5000
\n5950 RETURN 
\n6000 REM %B%U%B%B%L%E% %S%O%R%T% %R%E%C%O%R%D%S% % % % 
\n6010 SCROLL 
\n6020 PRINT "SORTING TIME DEPENDS ON"
\n6021 SCROLL 
\n6025 PRINT "NUMBER OF RECORDS"
\n6030 SCROLL 
\n6040 PRINT "WHICH FIELD TO SORT BY:"
\n6050 SCROLL 
\n6060 FOR I=1 TO N1
\n6070 SCROLL 
\n6080 PRINT N$(I);" ?";
\n6090 INPUT A$
\n6100 IF A$(1)="Y" THEN GOTO 6150
\n6110 NEXT I
\n6120 SCROLL 
\n6130 PRINT "SORT CANCELLED"
\n6140 GOTO 6900
\n6150 SCROLL 
\n6160 FAST 
\n6170 FOR J=1 TO N-1
\n6180 FOR K=J TO N
\n6190 IF I$(J,D(I,1) TO D(I,2))<=I$(K,D(I,1) TO D(I,2)) THEN GOTO 6250
\n6200 LET B$=I$(J)
\n6210 LET I$(J)=I$(K)
\n6220 LET I$(K)=B$
\n6250 NEXT K
\n6260 NEXT J
\n6270 SLOW 
\n6280 SCROLL 
\n6290 PRINT "SORT COMPLETE"
\n6900 SCROLL 
\n6910 PRINT "SORT ON ANOTHER FIELD?";
\n6920 INPUT A$
\n6925 SCROLL 
\n6930 IF A$(1)="Y" THEN GOTO 6000
\n6950 RETURN 
\n7000 REM %S%A%V%E% %P%R%O%G%R%A%M% %T%O% %T%A%P%E% % % 
\n7010 SCROLL 
\n7020 PRINT "PUT CASSETTE IN TAPE RECORDER"
\n7030 SCROLL 
\n7040 PRINT "BEGIN RECORDING, THEN"
\n7042 SCROLL 
\n7045 PRINT "PRESS ANY KEY TO SAVE LIST";
\n7050 IF INKEY$="" THEN GOTO 7050
\n7060 SCROLL 
\n7070 SAVE L$
\n7900 GOTO 100
\n8000 REM %S%E%T% %U%P% %N%E%W% %L%I%S%T% % % % % % % % 
\n8010 SCROLL 
\n8020 PRINT "WHAT LIST NAME TO USE?";
\n8030 INPUT L$
\n8040 SCROLL 
\n8050 PRINT "HOW MANY RECORD FIELDS?";
\n8060 INPUT N1
\n8061 PRINT N1
\n8070 SCROLL 
\n8080 LET K=15
\n8090 PRINT "MAXIMUM NUMBER OF CHARACTERS IN"
\n8100 SCROLL 
\n8110 PRINT "DESCRIPTION IS (0-15)?";
\n8120 INPUT K
\n8130 IF K>15 THEN LET K=15
\n8135 IF K<0 THEN LET K=0
\n8140 SCROLL 
\n8150 LET L=31-K
\n8160 PRINT "MAXIMUM SIZE OF DATA FIELD"
\n8170 SCROLL 
\n8180 PRINT "IS ";L;" CHARACTERS"
\n8182 DIM N$(N1,K)
\n8184 DIM A(N1)
\n8186 DIM D(N1,2)
\n8190 SCROLL 
\n8195 SCROLL 
\n8200 PRINT "NOW ENTER THE FIELD DESCRIPTIONS"
\n8210 SCROLL 
\n8220 PRINT "AND FIELD LENGTHS:";
\n8225 LET J=1
\n8230 SCROLL 
\n8240 FOR I=1 TO N1
\n8250 SCROLL 
\n8255 LET A$=""
\n8260 PRINT "FIELD ";I;":";
\n8270 INPUT A$
\n8272 PRINT A$
\n8275 LET A$=A$+"                    "
\n8280 LET N$(I)=A$
\n8290 SCROLL 
\n8300 PRINT "FIELD LENGTH (1-";L;"):";
\n8310 INPUT A
\n8312 IF A<1 THEN LET A=1
\n8315 IF A>L THEN LET A=L
\n8317 PRINT A
\n8320 LET D(I,1)=J
\n8330 LET J=J+A
\n8335 LET D(I,2)=J-1
\n8340 SCROLL 
\n8350 NEXT I
\n8360 LET J=J-1
\n8370 LET N=0
\n8380 LET M=INT ((8000-N1*(31-L+12))/J)
\n8390 SCROLL 
\n8400 PRINT "MAXIMUM NUMBER OF RECORDS"
\n8410 SCROLL 
\n8420 PRINT "POSSIBLE IS ABOUT ";M
\n8430 SCROLL 
\n8435 PRINT "HOW MANY DO YOU WANT?";
\n8437 INPUT A
\n8439 LET A=INT A
\n8440 IF A>0 AND A<M THEN LET M=A
\n8450 DIM I$(M,J)
\n8900 SCROLL 
\n8910 PRINT L$;" LIST SET UP"
\n8920 SCROLL 
\n8930 PRINT "PRESS ANY KEY TO RETURN TO MENU"
\n8940 IF INKEY$="" THEN GOTO 8940
\n8950 RETURN 
\n9000 REM %L%I%S%T% %F%I%E%L%D%S% % % % % % % % % % % % 
\n9020 SCROLL 
\n9030 PRINT TAB 5;L$;" LIST FIELDS"
\n9040 SCROLL 
\n9050 FOR I=1 TO N1
\n9060 SCROLL 
\n9065 SCROLL 
\n9070 PRINT N$(I)
\n9080 NEXT I
\n9090 SCROLL 
\n9100 PRINT "PRESS ANY KEY TO RETURN TO MENU"
\n9105 SCROLL 
\n9110 IF INKEY$="" THEN GOTO 9110
\n9900 RETURN 
\n9905 CLEAR 
\n9910 SAVE "1000%8"
\n9920 RUN 

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

Scroll to Top