Capital Area Timex Sinclair Users Group Membership Files

Developer(s): Mark Fisher
Date: 1987
Type: Program
Platform(s): TS 2068
Tags: Database

This program is a membership database manager for CATS, written in 1983 by Ned Beeler and revised in 1985 by Mark Fisher. It stores up to 214 member records across parallel string arrays covering name, street, city, state, ZIP, home phone, office phone, job, equipment, and a payment-status field. The main menu offers seven functions: adding records, searching and editing records with a cursor-driven update screen, listing to screen, printing mailing labels, printing a formatted roster, printing a ZIP-code-sorted list, and running a Shell sort on any chosen array variable. Payment currency is determined by comparing a stored date field against today’s date using a month-offset formula (VAL I$(TO 2)+12*VAL I$(4 TO 5)-12), and lapsed members receive an LPRINT-formatted “LAST ISSUE” renewal notice with a $18.00 dues amount. Printer control uses Epson-style escape sequences embedded in LPRINT statements, and two POKEs at addresses 64256 and 64260 configure printer hardware settings.


Program Analysis

Program Structure

The program is organized as a dispatch table centered on a main menu loop at line 10001499. Menu choices 1–7 are routed via the expression GO SUB VAL J$*500+G (line 1200), where G is a constant (likely 1000), mapping digit “1” to subroutine 1500, “2” to 2000 (search/update), “3” to 2500 (list), “4” to 3000 (label print), “5” to 3500 (roster print), “6” to 4000 (ZIP list), and “7” to 4500 (sort). Lines 0–14 and 50–96 form a field-entry subroutine bank, where GO SUB I inside a FOR I=O TO 13 loop dispatches to the matching line number for each field.

Key Constants and Variables

Several single-letter variables serve as constants throughout, established before the listing begins:

  • L = 1 (used as numeric constant one and loop start)
  • O = 0 (used as zero in AT coordinates and loop bounds)
  • D = 10 (column offset and field index)
  • M = 1 (used in PRINT AT for membership number row)
  • G = 1000 (base for menu dispatch GO SUB)
  • W$ = a separator/ruler string printed between sections
  • X$ holds the current sort key description displayed on the menu
  • N = current number of records; K = last membership number assigned

Data Storage

Records are held in parallel string arrays, each dimensioned to at least 214 elements:

ArrayField
N$(J)Name
R$(J)Street address
C$(J)City
S$(J)State
Z$(J)ZIP code
H$(J)Home phone
P$(J)Office phone
O$(J)Job/occupation
E$(J)Equipment owned
D$(J)Payment record (date in columns 1–5, status in column 10)

The D$(J) array is multipurpose: columns 1–5 store the payment date (MO-YR format), column 6 onward stores the membership number as a string, and column 10 stores a single-character payment status code.

Payment Expiry Logic

At lines 1000 and 3010, the current date is converted to a month-offset integer using VAL I$(TO L+L)+12*VAL I$(4 TO 5)-12. In the label-print routine (line 32203225), each member’s stored payment date undergoes the same conversion and is compared to Q. Members with status "0" are silently skipped; those with status above "4" are treated as currently paid; those whose date offset does not exceed Q (i.e., paid within 12 months) are also skipped. All others receive an LPRINT-formatted renewal notice including Epson escape sequences for emphasis and a $18.00 dues reminder, and their status is set to "0".

Cursor-Driven Update Screen

The update/delete routine (lines 21002499) implements a simple full-screen record editor. A highlighted bar (FLASH, OVER) is drawn at row Q using W$. The user moves the bar with keys "6" (down) and "7" (up), checked via INKEY$ in a tight loop at line 2220. Row position Q maps to specific actions:

  • Rows 0–13: dispatch GO SUB Q to the matching field-entry subroutine
  • Row 15: forward to next record (GO SUB O, i.e., GO SUB 0)
  • Rows 16–17: navigate forward/backward through records
  • Row 19: delete with confirmation
  • Row >19: return to menu

Bounds wrapping at line 2230 uses the expression Q+(Q<0)-(Q>20), adding or subtracting 1 when the cursor goes out of range — a common Sinclair BASIC boolean arithmetic idiom.

Shell Sort Implementation

Lines 45004999 implement a Shell (diminishing-increment) sort. The gap sequence is computed by halving Q repeatedly (Q=INT(Q/2)), with odd-gap enforcement at line 4530. The sort variable is user-selectable: choices “1” and “2” map to N$(J) and Z$(J) respectively, or the user may type any array expression directly. The comparison at line 4545 uses a remarkable runtime string construction technique:

IF VAL$ J$<=VAL$ (J$(TO 4)+"+Q"+J$(5 TO))

This builds a string like "N$(J+Q)" by splicing "+Q" into the sort-key expression string at position 5, then evaluates it with VAL$ — effectively an eval-based comparison that avoids hardcoding the sort field. When a swap is needed, all ten parallel arrays are swapped together using temporary variables (A$ through Y$).

Printer Control

All print routines use Epson-style escape codes embedded as string concatenation: CHR$ 27+"N" (normal pitch), CHR$ 27+"!" (condensed), CHR$ 27+"X..."+CHR$ 27+"Y..."+CHR$ 27+"Q..." for formatted renewal notices. The two POKEs at addresses 64256 and 64260 configure the TS2068’s printer interface port settings, switching between different printer modes across routines.

Initialization and Auto-Start

Line 5060 is the program’s auto-start entry point (used in the SAVE I$ LINE 5060 at line 5010). It sets system variables via POKEs (scroll count at 23561/23562, caps lock at 23658, and flags at 23609), configures the display with BORDER 0: PAPER 0: INK 7, and prompts for the date. The date-parsing loop at lines 50805110 finds the first non-digit character (the separator) and normalizes the date to a two-digit month, hyphen, two-digit year format stored in I$.

Notable Techniques and Anomalies

  • The field-dispatch loop FOR I=O TO 13: GO SUB I: NEXT I targets line numbers 0–13 directly, keeping field routines at the very top of the program for fast lookup.
  • Lines 2, 8, and 11 are bare RETURN statements acting as stubs for unimplemented fields (family, and two phone-adjacent fields).
  • Lines 9094 are commented out with REM, preserving an earlier screen-clearing loop that was apparently superseded.
  • Line 90009050 is a maintenance utility (with the print statement itself also REMmed out at line 9010) that bulk-deletes records by calling GO SUB 2400 in reverse order — reverse traversal is necessary to avoid index shifting during deletion.
  • Line 9500 is a one-off data-migration snippet that replaces status code "Q" with "4" across all records.
  • At line 3570, the roster print routine uses IF D$(J,D)<"4" THEN NEXT J — placing NEXT J inside an IF statement, which is valid in Sinclair BASIC and skips non-current members without a GO TO.
  • Line 2275 reads IF Q=15 THEN GO SUB O, calling GO SUB 0 (the payment status subroutine at line 0) when the cursor is on the “UPDATE” row — the row numbering on screen and the Q values do not align intuitively with the displayed menu rows.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Capital Area Timex Sinclair Users Group Membership Files

Source Code

    0 LET D$(J,D)="5": PRINT AT L,D-L;D$(J,D): LET D$(J, TO 5)=I$: PRINT AT L,16;D$(J, TO 5): RETURN 
    1 INPUT "PAYMENT STATUS ";D$(J,D): PRINT AT L,D-L;D$(J,D): RETURN 
    2 RETURN 
    3 INPUT "NAME  ";N$(J): PRINT AT 3,D;N$(J): RETURN 
    4 INPUT "STREET ";R$(J): PRINT AT 4,D;R$(J): RETURN 
    5 INPUT "CITY ";C$(J): PRINT AT 5,D;C$(J): RETURN 
    6 INPUT "STATE ";S$(J): PRINT AT 6,D;S$(J): RETURN 
    7 INPUT "ZIP CODE ";Z$(J): PRINT AT 7,D;Z$(J): RETURN 
    8 RETURN 
    9 INPUT "HOME PHONE ";H$(J): PRINT AT D-L,D;H$(J): RETURN 
   10 INPUT "OFFICE PHONE ";P$(J): PRINT AT D,D;P$(J): RETURN 
   11 RETURN 
   12 INPUT "JOB  ";O$(J): PRINT AT 12,D;O$(J): RETURN 
   13 GO SUB 50: INPUT "EQUIPMENT";E$(J): PRINT AT 13,D;E$(J)
   14 RETURN 
   50 PRINT AT 17,O;"A.TS1000  F.E-PROM K.FLOPPY    "
   52 PRINT "B.TS1500  G.16KRAM L.STRNGY    "
   54 PRINT "C.TS2068  H.32KRAM M.          "
   56 PRINT "D.PRINTER I.64KRAM N.          "
   58 PRINT "E.MODEM   J.DISK   O.          "
   62 RETURN 
   90 REM FOR I=16 TO D+D+L
   92 REM PRINT AT I,O;"                                "
   94 REM NEXT I
   96 RETURN 
  100 PRINT "NAME     :"';"STREET   :"';"CITY     :"';"STATE    :"';"ZIP CODE :"
  120 PRINT W$
  130 PRINT "H-PHONE  :"'"O-PHONE  :"'"FAMILY   :"'"JOB      :"'"EQUIP.   :"
  140 PRINT W$
  150 RETURN 
  999 STOP 
 1000 LET Q=VAL I$( TO L+L)+12*VAL I$(4 TO 5)-12
 1006 POKE 64256,L: POKE 64260,O
 1010 CLS 
 1020 PRINT ''"******* MEMBERSHIP FILES *******       by Ned Beeler  1983          revised by M. Fisher '85    "
 1025 PRINT "    TODAY'S DATE """;I$;""""
 1030 PRINT '"1. ADD NEW NAME"''"2. OR """"; UPDATE/DELETE RECORD"''"3. LIST RECORDS"''
 1050 PRINT "4. LPRINT LABELS"''"5. LPRINT ROSTER"''"6. LPRINT ZIP LIST "''
 1075 PRINT "7. SORT: curr. by ";X$''"8. SAVE program"
 1090 INPUT "SELECT YOUR CHOICE";J$
 1095 CLS 
 1151 IF LEN J$<L THEN LET J$="2"
 1160 IF CODE J$<49 OR CODE J$>58 THEN GO TO G
 1200 GO SUB VAL J$*500+G
 1499 GO TO G
 1500 IF N>=214 THEN PRINT "FILE FULL": PAUSE D*D: RETURN 
 1505 LET N=N+L: LET K=K+L: LET J=N
 1510 CLS 
 1520 PRINT "FILE NO. ";N,"MEMBSHP NO. ";K
 1525 PRINT "----PAID  ------------DATE -----"
 1530 PRINT W$
 1535 GO SUB D*D
 1546 LET D$(N,6 TO D-L)=STR$ K: PRINT AT M,28;D$(N,6 TO D-L)
 1550 FOR I=O TO 13: GO SUB I: NEXT I
 1655 GO SUB D*D-D
 1680 INPUT "IS ALL CORRECT (Y/N) ";J$
 1700 IF J$="N" THEN GO SUB 2100
 1720 CLS 
 1730 INPUT "ANY MORE NAMES?  (Y/N)  ";J$
 1750 IF J$="Y" THEN GO TO 1500
 1999 RETURN 
 2010 CLS 
 2020 INPUT "SEARCH NAME ? ";J$
 2030 FOR J=L TO N
 2040 IF J$=N$(J, TO LEN J$) THEN GO TO 2100
 2050 NEXT J
 2055 CLS 
 2060 INPUT " NO NAME-PRESS ENTER TO RETURN ";J$
 2070 RETURN 
 2100 LET Q=15
 2105 CLS 
 2110 PRINT "FILE NO. ";J;" MEMBERSHIP NO. ";D$(J,6 TO D-L)
 2120 PRINT "PAID---- ";D$(J,D);" DATE ";D$(J, TO 5)
 2125 PRINT W$
 2130 GO SUB D*D
 2150 PRINT AT 3,D;N$(J);AT 4,D;R$(J);AT 5,D;C$(J)
 2160 PRINT AT 6,D;S$(J);AT 7,D;Z$(J)
 2170 PRINT AT D-L,D;H$(J);AT D,D;P$(J)
 2180 PRINT AT 12,D;O$(J);AT 13,D;E$(J)
 2195 PRINT W$
 2200 PRINT "UPDATE"'"FOREWARD"'"BACK"'"        "'" DELETE "'"        "'">--- RETURN TO MENU ---> OR (M)";
 2210 PRINT #1;AT 0,0;"PUT CURSOR ON LINE TO BE CHANGEDPRESS <ENTER> WHEN READY"
 2215 PRINT OVER L; FLASH L;AT Q,O;W$;
 2216 PAUSE O
 2217 PRINT OVER L; FLASH O;AT Q,O;W$
 2220 LET Q=Q+(INKEY$="6")-(INKEY$="7")
 2230 LET Q=Q+(Q<0)-(Q>20)
 2240 IF INKEY$=CHR$ 13 THEN GO TO 2270
 2245 IF INKEY$="M" THEN RETURN 
 2250 GO TO 2215
 2275 IF Q=15 THEN GO SUB O
 2280 IF Q=16 OR Q=17 THEN LET J=J+(Q=16 AND J<215)-(Q=17 AND J>1): GO TO 2105
 2310 IF Q>19 THEN RETURN 
 2320 IF Q<14 THEN GO SUB Q
 2380 IF Q=19 THEN INPUT "DELETE: ARE YOU SURE? (Y/N) ";J$: IF J$="Y" THEN GO TO 2400
 2398 GO TO 2210
 2400 FOR Z=J TO N
 2402 LET N$(Z)=N$(Z+L)
 2404 LET R$(Z)=R$(Z+L)
 2406 LET C$(Z)=C$(Z+L)
 2408 LET S$(Z)=S$(Z+L)
 2410 LET H$(Z)=H$(Z+L)
 2412 LET P$(Z)=P$(Z+L)
 2414 LET Z$(Z)=Z$(Z+L)
 2416 LET O$(Z)=O$(Z+L)
 2420 LET E$(Z)=E$(Z+L)
 2422 LET D$(Z)=D$(Z+L)
 2430 NEXT Z
 2440 LET N=N-L
 2499 RETURN 
 2504 LET Z=0
 2505 FOR J=L TO N
 2510 PRINT J;" ";N$(J, TO 18);D$(J): LET Z=Z+1
 2550 NEXT J
 2555 PRINT Z
 2560 PAUSE 0
 2990 RETURN 
 3000 CLS 
 3010 LET Q=VAL I$( TO 2)+12*VAL I$(4 TO 5)-12
 3100 POKE 64256,O: POKE 64260,D
 3200 LPRINT CHR$ 27+"N TEST XXXXX"''''''
 3205 PAUSE 0
 3210 FOR J=L TO N
 3220 IF D$(J,D)>"4" THEN LPRINT : GO TO 3230: REM current year's member
 3221 IF D$(J,D)="0" THEN GO TO 3330: REM previously crossed off list
 3222 IF VAL D$(J, TO L+L)+12*VAL D$(J,4 TO 5)>Q THEN LPRINT : GO TO 3230: REM less than 12 months since paid - still current
 3225 LPRINT CHR$ 27+"XLAST ISSUE"+CHR$ 27+"Y-"+CHR$ 27+"Q$18.00; we need your support."+CHR$ 27+"N": LET D$(J,D)="0"
 3230 LPRINT N$(J);" pd ";D$(J, TO 5);" ";D$(J,D)
 3250 LPRINT R$(J)
 3270 LPRINT C$(J);" ";
 3280 LPRINT S$(J);"  ";Z$(J)
 3310 LPRINT ''
 3330 NEXT J
 3340 RETURN 
 3500 REM LPRINT "..."
 3506 POKE 64256,O: POKE 64260,D
 3507 LPRINT CHR$ 27+"N"+CHR$ 27+"!"
 3510 LPRINT "CATS MEMBERSHIP, ";I$
 3515 LPRINT "Name                     #    Address                           H#      O#"'
 3520 LET LINE=3
 3560 FOR J=L TO N
 3566 IF LINE=57 THEN LET LINE=2: PAUSE 0: LPRINT CHR$ 27+"NName                     #    Address                           H#      O#"'
 3569 IF N$(J,1)<>N$(J-1,1) THEN LET LINE=LINE+1: LPRINT 
 3570 IF D$(J,D)<"4" THEN NEXT J
 3575 LET LINE=LINE+1
 3580 LPRINT CHR$ 27;"N";
 3590 LPRINT N$(J);CHR$ 27;"Q   ";D$(J,6 TO );"  ";
 3600 LPRINT R$(J);"  ";
 3610 LPRINT C$(J);" ";
 3620 LPRINT S$(J);"  ";Z$(J);"  ";H$(J);"  ";P$(J)
 3630 NEXT J
 3640 RETURN 
 4000 LPRINT CHR$ 27+"N"
 4002 POKE 64256,O: POKE 64260,D
 4003 LPRINT CHR$ 27+"N"+CHR$ 27+"!"
 4004 LET J$=Z$(L)
 4010 LPRINT "CATS members, sorted by ZIP, ";I$
 4020 FOR J=L TO N
 4025 IF D$(J,10)<"3" THEN GO TO 4050
 4028 IF J$( TO 3)<>Z$(J, TO 3) THEN LPRINT : LET LINE=LINE+1
 4030 LPRINT N$(J);CHR$ 27+"Q"+C$(J)+CHR$ 27+"N"+S$(J);" ";Z$(J)
 4040 LET J$=Z$(J)
 4050 NEXT J
 4100 RETURN 
 4500 CLS : PRINT "SORT BY:","(1) N$(J) NAME"'',"(2) Z$(J) ZIP"
 4510 INPUT "SELECT SORT,OR ANY VAR";J$: PRINT ''"SORTED BY: ";J$
 4512 IF J$="1" THEN LET J$="N$(J)"
 4513 IF J$="2" THEN LET J$="Z$(J)"
 4515 LET T=N
 4520 LET Q=T
 4525 LET Q=INT (Q/2): IF Q<L THEN LET X$=J$: RETURN 
 4530 IF Q/2=INT (Q/2) THEN LET Q=Q+1
 4532 PRINT Q;" ";
 4535 FOR I=L TO T-Q
 4540 LET J=I
 4545 IF VAL$ J$<=VAL$ (J$( TO 4)+"+Q"+J$(5 TO )) THEN GO TO 4750
 4546 BEEP .02,50-Q
 4550 LET A$=N$(J)
 4552 LET B$=R$(J)
 4554 LET G$=C$(J)
 4556 LET X$=S$(J)
 4558 LET K$=H$(J)
 4560 LET L$=P$(J)
 4562 LET M$=D$(J)
 4564 LET Q$=O$(J)
 4566 LET T$=E$(J)
 4570 LET Y$=Z$(J)
 4600 LET N$(J)=N$(J+Q)
 4602 LET R$(J)=R$(J+Q)
 4604 LET C$(J)=C$(J+Q)
 4606 LET S$(J)=S$(J+Q)
 4610 LET H$(J)=H$(J+Q)
 4612 LET P$(J)=P$(J+Q)
 4614 LET D$(J)=D$(J+Q)
 4620 LET O$(J)=O$(J+Q)
 4622 LET E$(J)=E$(J+Q)
 4626 LET Z$(J)=Z$(J+Q)
 4700 LET N$(J+Q)=A$
 4702 LET R$(J+Q)=B$
 4704 LET C$(J+Q)=G$
 4706 LET S$(J+Q)=X$
 4708 LET H$(J+Q)=K$
 4710 LET P$(J+Q)=L$
 4712 LET D$(J+Q)=M$
 4714 LET O$(J+Q)=Q$
 4716 LET E$(J+Q)=T$
 4720 LET Z$(J+Q)=Y$
 4730 LET J=J-Q
 4740 IF J>0 THEN GO TO 4544
 4750 NEXT I
 4999 GO TO 4525
 5000 CLS : PRINT "WILL BE SAVED AS; """;I$;""""
 5010 SAVE I$ LINE 5060: PRINT ''"Rewind tape and VERIFY"
 5020 VERIFY I$
 5030 RETURN 
 5060 POKE 23561,30: POKE 23562,2: POKE 23658,8: POKE 23609,3: BORDER 0: PAPER 0: INK 7
 5065 POKE 64260,0: FOR J=64263 TO 64265: POKE J,0: NEXT J
 5070 INPUT "TODAY'S DATE? (MO-YR) ";I$
 5080 FOR I=L TO 5: IF I$(I)<"0" THEN GO TO 5100
 5090 NEXT I
 5100 LET I$( TO L+L)=I$( TO I-L): LET I$(3 TO )="-"+I$(I+L TO )
 5110 GO TO G
 8996 STOP 
 9000 FOR J=N TO 196 STEP -1
 9010 REM IF D$(J,10)<"3" THEN PRINT "OUT NO.";J,N$(J): GO SUB 2400
 9020 GO SUB 2400
 9050 NEXT J: STOP 
 9500 FOR J=L TO N: IF D$(J,10)="Q" THEN LET D$(J,10)="4":
 9510 NEXT J

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

Scroll to Top