Check is a checkbook balancing program that stores up to 207 transaction entries and 207 payee names in parallel arrays, encoding check numbers and dates together into single floating-point values using a base-1,000,000 scheme. Each transaction record in the two-dimensional array A() packs the payee index combined with the check number, the reconciliation status combined with the date, and the amount in cents as integer. The program supports four main functions: entry input, search (by date, check number, account name, or outstanding items), reconciliation, and an edit mode accessed via option 6. A machine language presence check at line 2595 using USR 32736 tests whether a specific ROM or utility is loaded before proceeding. Amounts are stored as integer cents internally and divided by 100 for display, avoiding floating-point precision issues on dollar values.
Program Analysis
Program Structure
The program is organized into several logical sections accessed via a main menu at line 60–160:
- Initialization (lines 2300–2690): Sets up all constants, dimensions arrays, and prompts for starting check number and balance.
- Main Menu (lines 60–160): Displays current balance and date, offers options 1 (input entry), 2 (search), 3 (reconcile), 6 (edit).
- Entry Input (lines 170–1020): Collects payee name, check number, date, and amount; stores them in arrays.
- Reconciliation (lines 1030–1550): Searches by check number or deposit, marks items reconciled, and computes outstanding bank statement balance.
- Search (lines 1590–2140): Searches by date, check number, account name, or outstanding status; doubles as the edit entry point for option 6.
- Display Subroutine (lines 2190–2290): Common record display used by search and reconcile routines.
- Save/Quit (lines 2700–2860): Handles full-array warnings, tape save with data preserved, and a final unused
PRINT PEEKat line 2860.
Constant Initialization and Naming
The initialization subroutine at line 2300 defines all numeric constants as single-letter variables to save memory and speed up execution. This is a classic ZX81 BASIC optimization:
| Variable | Value | Role |
|---|---|---|
B | 207 | Max entries / max payee names |
A | 0 | Zero constant |
C | 1 | One constant |
D | 2 | Two / array column index |
E | 3 | Three / array column index |
G | 5 | Five / row for AT positioning |
I | 7 | Seven |
K | 9 | Nine |
L | 10 | Ten |
M | 207 | Payee name array size |
N | 2 | Current entry count pointer |
O | 28 | Lower bound for valid digit CODE check |
P | 39 | Upper bound for valid digit CODE check |
Q | 2 | Current payee name count pointer |
R | 1E6 | Packing base (1,000,000) |
S | reconciled marker | Computed from (L*L+O+C)*R = 129,000,000 |
Data Encoding Scheme
The most technically interesting aspect of this program is how it packs multiple values into single floating-point array cells using base 1,000,000 (R = 1E6). The array A(N, 3) stores three fields per transaction:
A(W,C)— encodes payee index × 1,000,000 + check number. The check number is extracted withA(W,C) - INT(A(W,C)/R)*R(i.e., modulo 1,000,000), and the payee index isINT(A(W,C)/R).A(W,D)— encodes reconciliation marker × 1,000,000 + date. The reconciliation flagS = 129,000,000is added to the date when an item is reconciled (line 1570). An item is outstanding whenINT(A(W,D)/R)*R = Sis false (i.e., the high part is 0).A(W,E)— stores the amount in integer cents (dollars × 100).
Deposits are identified by a payee index of 1 (the reserved A$(1) = "DEPOSIT" entry) and stored with A(W,C) = R (i.e., index 1 × 1,000,000 + 0).
Balance and Amount Handling
The variable BA holds the running balance in integer cents throughout (BA = BA * 100 at line 2655). Deposits add to BA, checks subtract. The display consistently uses BA/100 or X/100 to present dollar values. The reconciliation routine at lines 1440–1500 computes BAL as the sum of all outstanding items and prints the implied bank statement balance as (BA - BAL) / 100.
Input Validation
Numeric input is validated by checking CODE Z$ against the character code range for digits. On the ZX81, digits ‘0’–’9′ have codes 28–37. The bounds O = 28 and P = 39 (slightly generous upper bound) are used in guards like:
IF CODE Z$<=O OR CODE Z$>=P THEN GOTO ...
This rejects non-digit input but allows ENTER (code 0, handled separately by checking CODE Z$ = A, i.e., = 0, meaning the user pressed ENTER and the system returned an empty string).
Edit Mode (Option 6)
Edit mode is accessed by entering “6” at the main menu and routes through the search subroutine. When a record is found and confirmed wrong (HI = D, i.e., = 2), control jumps to line 182 which reverses the financial effect of the old entry (removing its amount from BA and zeroing the record fields) before re-entering new data. The variable HI is used as a flag: it is set to 0 (A) normally and 2 (D) when the user indicates a record needs correction.
Machine Language Check
Line 2595 executes USR 32736 and compares the result to 40059. Address 32736 is near the top of the ZX81’s 32KB ROM space, and this appears to be a check for the presence of a specific ROM version or utility. If the check fails, the program prints a small error marker ("? ") but continues executing regardless.
Payee Name Deduplication
When a new payee name is entered, lines 870–910 scan the existing A$() array for a match. If found, the existing index W is reused; if not, the name is appended at position Q and Q is incremented. The encoded check number field then becomes R * W + check_number, linking the transaction to its payee by index.
Capacity Limits and Overflow Handling
The arrays are dimensioned for 207 entries (B = 207) and 207 payee names (M = 207). Lines 742–757 check these limits before accepting new data, and lines 2700–2720 display appropriate warnings when either limit is reached. When full, the program routes to the save/quit prompt rather than continuing to accept entries.
Notable Bugs and Anomalies
- Line 155 routes option “6” to
GOSUB 1590(search), the same as option “2”. This is intentional — search is the entry point for edit mode, with behavior diverging based onR$ = "6". - Line 780 is referenced by
GOTO 780implicitly via line 750’sIF CODE Z$=A THEN GOTO 780, but the label is actually line 782. There is no line 780; line 782 is the next line after 750’s conditional, so after falling through line 750 (when the condition is true), execution would naturally reach 782 — but sinceGOTO 780targets a non-existent line, the interpreter will run from the next line after 780, which is 782. This is a deliberate technique. - Line 2860 (
PRINT PEEK 16386 - PEEK 16412 + 256*(PEEK 16387-PEEK 16413)-50) is unreachable dead code, likely a debugging remnant that computed available memory. - The prompt string
O$at line 2594 reads “INFO DELETED–ENTER CORRECT INFO” but deletions are actually implemented by zeroing array fields rather than physically removing records, so deleted entries remain in the array as zero-valued placeholders.
Content
Source Code
7 SAVE "CHEC%K"
10 FAST
20 GOSUB 2300
30 CLS
40 PRINT D$;AT A,K-C;"TODAYS ";F$,"ENTER IN THIS ORDER.","YEAR MONTH DAY",,"USE ONLY SIX DIGITS TOTAL"
50 INPUT DA
60 CLS
70 PRINT "% % % %P%R%O%G%R%A%M% %B%Y% %D%A%L%E% %F% %L%I%P%I%N%S%K%I% % % ";"% % : CHECK BOOK :% : DATE :% : ";DA;TAB L+L+K;" :% % ";M$;"BALANCE $ ";BA/100;TAB A;"LAST ";G$;LC,M$
80 PRINT "NOTE: IF AT ANY TIME YOU LOSE","'''''''' CONTROL AND GET THE PROGRAM";TAB G;"PRESS %G%O%T%O 60 %E%N%T%E%R.",M$;,,,,C$,,"1= INPUT ENTRY",,"2= SEARCH",,"3= RECONCILE",,"6= ";N$,,E$
95 LET HI=C
100 INPUT R$
120 IF R$="0" THEN GOTO 2730
130 IF R$="1" THEN GOTO 170
140 IF R$="2" THEN GOSUB 1590
150 IF R$="3" THEN GOSUB 1030
155 IF R$="6" THEN GOSUB 1590
160 GOTO 60
170 REM
180 CLS
181 IF R$<>"6" THEN GOTO 190
182 IF (INT (A(W,C)/R))=C THEN LET BA=BA-A(W,E)
183 LET A(W,D)=A
184 IF (INT (A(W,C)/R))>=D THEN LET BA=BA+A(W,E)
185 LET A(W,E)=A
186 LET A(W,C)=A
187 CLS
188 PRINT TAB I;N$,O$
190 PRINT C$,,"1= CHECK ENTRY",,"5= ";A$(C),,,E$
210 INPUT Z$
225 IF R$="6" AND Z$="0" THEN CLS
226 IF R$="6" AND Z$="0" THEN PRINT "YOU MUST ENTER VALUES TO AN EDIT"
227 IF R$="6" AND Z$="0" THEN GOTO 190
230 IF Z$="0" THEN GOTO 60
240 IF Z$="1" THEN GOSUB 330
250 IF Z$="5" THEN GOSUB 270
260 GOTO 170
270 REM
280 CLS
290 LET Y$=A$(C)
300 LET Y=A
310 GOSUB 490
320 RETURN
330 REM
340 CLS
350 PRINT D$;H$;"NAME?",,,C$,,M$
370 INPUT Y$
385 IF CODE Y$=A THEN GOTO 340
386 IF R$="6" AND Y$="0" THEN GOTO 340
390 IF Y$="0" THEN RETURN
400 CLS
410 PRINT G$;LC+C;,,,,TAB A;J$,M$
430 INPUT Z$
450 IF CODE Z$=A THEN LET Y=LC+C
460 IF CODE Z$=A THEN GOTO 500
470 IF CODE Z$<=O OR CODE Z$>=P THEN GOTO 400
480 IF CODE Z$<>A THEN LET Y=VAL Z$
490 REM
500 CLS
510 IF Y$=A$(C) THEN PRINT A$(C)
520 PRINT F$;DA,,,,J$,C$,,M$
540 INPUT Z$
560 IF CODE Z$=A THEN LET Z=DA
570 IF CODE Z$=A THEN GOTO 610
580 IF Z$="0" THEN RETURN
590 IF CODE Z$<=O OR CODE Z$>=P THEN GOTO 500
600 IF CODE Z$<>A THEN LET Z=VAL Z$
610 CLS
620 PRINT D$;"AMOUNT?",M$
640 INPUT Z$
660 IF CODE Z$<=O OR CODE Z$>=P THEN GOTO 610
670 LET X=(VAL Z$)*100
680 CLS
690 PRINT Y$;TAB A;F$;Z,,G$;Y,,"$ ";X/100;TAB A,,,,K$,M$
700 IF Y$<>A$(C) AND BA-X<A THEN PRINT "%O%V%E%R%D%R%A%F%T",,M$
720 INPUT Z$
740 IF Y$<>A$(C) AND BA-X<A THEN RETURN
742 IF N>=B THEN GOTO 752
744 IF Q-C>=M THEN GOTO 752
750 IF CODE Z$=A THEN GOTO 780
752 CLS
755 PRINT "INFO %N%O%T ENTERED ",,,
756 IF Q-C>=M THEN GOTO 2705
757 IF N>=B THEN GOTO 2710
760 IF Y$=A$(C) THEN GOTO 510
770 GOTO 350
782 IF R$="6" THEN LET NN=N
784 IF R$="6" THEN LET N=W
790 IF Y$<>A$(C) THEN GOTO 840
800 LET AA=R
810 LET BA=BA+X
820 LET W=C
830 GOTO 920
840 LET BA=BA-X
850 IF Y>LC THEN LET LC=Y
860 LET A$(Q)=Y$
870 FOR W=D TO Q-C
880 IF A$(Q)=A$(W) THEN GOTO 902
890 NEXT W
900 LET Q=Q+C
910 LET AA=R*W
920 LET A(N,C)=AA+Y
930 LET A(N,D)=R+Z
940 LET A(N,E)=X
942 IF R$="6" THEN LET N=NN-C
950 LET N=N+C
955 PRINT AT G,L;"INFO ENTERED",M$;L$
956 IF R$="6" THEN PRINT AT G+C,E;N$;" CORRECTION",M$
957 PRINT ,,"BALANCE = $";BA/100
970 INPUT Z$
980 IF Z$="1" THEN COPY
990 IF N-C>=B OR Q-C>=M THEN GOTO 2705
992 IF R$="6" THEN LET R$="0"
994 IF R$="0" THEN RETURN
1000 IF CODE Z$=A AND Y$=A$(C) THEN GOTO 500
1010 IF CODE Z$=A OR Z$="1" THEN GOTO 340
1020 RETURN
1030 REM
1040 CLS
1050 PRINT "ENTER ";G$;"TO BE RECONCILED";,,C$,,M$;E$
1070 INPUT Y$
1077 IF CODE Y$<=O OR CODE Y$>=P THEN GOTO 1040
1080 IF Y$="0" THEN GOTO 1180
1090 LET Z=VAL Y$
1092 FOR W=D TO N
1094 CLS
1095 IF A(W,C)-(INT (A(W,C)/R)*R)=Z THEN GOSUB 2190
1100 IF A(W,C)-(INT (A(W,C)/R)*R)=Z THEN PRINT AT G,A;M$;K$
1105 IF A(W,C)-(INT (A(W,C)/R)*R)<>Z THEN NEXT W
1115 IF W>=N THEN PRINT "NOT FOUND"
1116 IF W>=N THEN GOTO 1050
1120 INPUT Z$
1130 IF Z$="0" THEN NEXT W
1160 GOSUB 1560
1170 GOTO 1040
1180 CLS
1190 PRINT AT A,A;"ENTER ";A$(C);AT A,G+K;"TO BE RECONCILED. ENTER BY DATE",,,,C$,,M$;E$
1210 INPUT Y$
1218 IF CODE Y$<=O OR CODE Y$>=P THEN GOTO 1180
1220 IF Y$="0" THEN GOTO 1320
1230 LET Z=VAL Y$
1235 FOR W=D TO N
1240 CLS
1242 IF INT (A(W,C)/R)<>C THEN NEXT W
1245 IF INT (A(W,C)/R)=C AND A(W,D)-(INT (A(W,D)/R)*R)=Z THEN GOSUB 2190
1250 IF INT (A(W,C)/R)=C AND A(W,D)-(INT (A(W,D)/R)*R)=Z THEN PRINT AT G,A;M$;K$
1255 IF A(W,D)-(INT (A(W,D)/R)*R)<>Z THEN NEXT W
1265 IF W>=N THEN PRINT AT L,G;"NOT FOUND"
1270 IF W>=N THEN GOTO 1190
1275 INPUT Z$
1290 IF Z$="0" THEN NEXT W
1300 GOSUB 1560
1310 GOTO 1180
1320 LET Z=S
1330 FOR W=D TO N
1340 CLS
1345 IF (INT (A(W,D)/R)*R)<>Z THEN GOSUB 2190
1350 IF (INT (A(W,D)/R)*R)<>Z THEN PRINT AT G,A;"OUTSTANDING ITEMS ",M$;C$;" ","5= SKIP OUTSTANDING ITEMS LIST",L$(O+P TO ),M$;E$
1355 IF (INT (A(W,D)/R)*R)=Z THEN NEXT W
1357 IF W>=N THEN GOTO 1440
1370 INPUT Z$
1390 IF Z$="0" THEN RETURN
1400 IF Z$="5" OR W=N-D THEN GOTO 1440
1410 NEXT W
1440 LET BAL=A
1450 FOR X=C TO N-C
1460 IF (INT (A(X,D)/R)*R)<>S AND (INT (A(X,C)/R))=C THEN LET BAL=BAL+A(X,E)
1470 IF (INT (A(X,D)/R)*R)<>S AND (INT (A(X,C)/R))<>C THEN LET BAL=BAL-A(X,E)
1480 NEXT X
1490 CLS
1500 PRINT "BANK STATEMENT IS $ ";(BA-BAL)/100;TAB A;M$;L$
1520 INPUT Z$
1530 IF Z$="1" THEN COPY
1550 RETURN
1560 REM
1570 LET A(W,D)=(A(W,D)-(INT (A(W,D)/R)*R))+S
1580 RETURN
1590 REM
1600 CLS
1610 IF R$="6" THEN PRINT AT A,K;N$
1615 PRINT AT C,L;"SEARCH",M$;C$,,"1= BY ";F$,,"2= BY ";G$,"3= BY ";H$,,"4= BY OUTSTANDING ITEM",E$
1617 IF R$="6" THEN PRINT AT I,A;E$,"BE SURE TO COPY ALL INFO THAT ISCHANGED"
1630 INPUT Z$
1650 IF CODE Z$<=O OR CODE Z$>=P THEN GOTO 1600
1660 IF Z$="0" THEN RETURN
1662 IF Z$="4" AND R$="6" THEN GOTO 1600
1663 IF R$="6" THEN GOTO 1730
1665 IF Z$="4" THEN GOTO 2080
1670 PRINT ,,"0= SPECIFIC ITEM","%E%N%T%E%R= ALL ITEMS",E$
1690 INPUT Y$
1720 IF CODE Y$=A THEN GOTO 2150
1730 PRINT ,,D$;"ITEM?"
1750 INPUT Y$
1765 IF Z$="3" THEN GOTO 1970
1770 IF CODE Y$<=O OR CODE Y$>=P THEN GOTO 1600
1780 IF Z$="1" THEN GOTO 1820
1790 IF Z$="2" THEN GOTO 1900
1810 GOTO 1600
1820 REM
1830 LET Z=VAL Y$
1840 FOR W=C TO N-D
1850 REM
1860 IF A(W,D)-(INT (A(W,D)/R)*R)=Z THEN GOSUB 2190
1865 IF HI=A AND R$="6" THEN GOTO 182
1870 NEXT W
1885 CLS
1887 PRINT AT C,E+E;"END"
1890 GOTO 1610
1900 REM
1910 LET Z=VAL Y$
1920 FOR W=D TO N-C
1930 IF A(W,C)-(INT (A(W,C)/R)*R)=Z THEN GOSUB 2190
1935 IF HI=A AND R$="6" THEN GOTO 182
1940 NEXT W
1955 CLS
1957 PRINT "END"
1960 GOTO 1610
1970 LET A$(Q)=Y$
1980 FOR X=C TO N-C
1990 IF A$(Q)=A$(X) THEN GOTO 2020
1995 IF HI=A AND R$="6" THEN GOTO 182
2000 NEXT X
2005 CLS
2007 PRINT "END"
2010 GOTO 1610
2020 LET Z=X*R
2030 FOR W=D TO N-C
2040 IF (INT (A(W,C)/R)*R)=Z THEN GOSUB 2190
2050 NEXT W
2055 CLS
2057 PRINT "END"
2060 GOTO 1610
2070 REM
2080 LET Z=S
2090 FOR W=D TO N-C
2100 REM
2110 IF (INT (A(W,D)/R)*R)<>Z THEN GOSUB 2190
2120 IF R$="3" THEN RETURN
2125 IF Z$="0" THEN GOTO 1600
2130 NEXT W
2135 CLS
2137 PRINT "END"
2140 GOTO 1610
2150 FOR W=D TO N-C
2160 GOSUB 2190
2165 IF HI=D THEN GOTO 2175
2170 NEXT W
2175 CLS
2177 PRINT AT C,E+E;"END"
2180 GOTO 1610
2190 REM
2200 CLS
2210 LET T=INT (A(W,D)/R)
2220 IF T=C THEN LET T=A
2230 PRINT F$;A(W,D)-(INT (A(W,D)/R)*R);TAB O;W,G$;A(W,C)-(INT (A(W,C)/R)*R),,H$;A$(INT (A(W,C)/R));TAB A;"$ ";(A(W,E)/100);TAB A,,"% DENOTES RECONCILED ITEM",L$
2235 IF INT (A(W,D)/R)*R=S THEN PRINT AT A,O-D;"% "
2240 IF R$="3" THEN RETURN
2245 IF R$="6" THEN PRINT AT G+C,A;"PRESS 0 %E%N%T%E%R IF NOT CORRECT";AT I+D,A;M$;N$,"DELETION"
2260 INPUT Z$
2265 LET HI=A
2270 IF Z$="1" THEN COPY
2275 IF Z$="0" THEN LET HI=D
2285 IF HI=A AND R$="6" THEN GOTO 182
2290 RETURN
2300 REM
2310 LET B=207
2320 LET A=B-B
2330 LET C=B/B
2340 LET D=C+C
2350 LET E=D+C
2360 LET G=E+D
2370 LET I=G+D
2380 LET K=I+D
2390 LET L=K+C
2400 LET M=207
2410 LET N=D
2420 LET O=L+L+I
2430 LET P=O+L+C
2440 LET Q=D
2450 LET R=1E6
2460 LET S=(L*L+O+C)*R
2470 DIM A(B,E)
2480 DIM A$(M,22)
2490 LET C$="0= NO ENTRY "
2500 LET D$="WHAT IS THE "
2510 LET E$="% %E%N%T%E%R% %A%N%S%W%E%R% %N%U%M%B%E%R% "
2520 LET F$="DATE "
2530 LET G$="CHECK NO. "
2540 LET H$="ACCOUNT "
2550 LET I$="PRESS %E%N%T%E%R IF THIS IS CORRECT "
2560 LET J$=I$+"IF NOT ENTER NUMBER"
2570 LET K$=I$+"PRESS 0 %E%N%T%E%R IF NOT CORRECT"
2580 LET L$="PRESS 0 %E%N%T%E%R TO RETURN PRESS 1 %E%N%T%E%R TO COPY PRESS %E%N%T%E%R TO CONTINUE"
2590 LET M$="''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"
2592 LET N$="EDIT MODE"
2594 LET O$="INFO DELETED--ENTER CORRECT INFO"+M$
2595 IF USR 32736=40059 THEN PRINT "?% % % "
2600 PRINT D$;"FIRST ";G$,"TO BE USED IN THIS PROGRAM?"
2620 INPUT LC
2630 LET LC=LC-C
2640 PRINT ,,"WHAT IS THE BEGINNG BALANCE?"
2650 INPUT BA
2655 LET BA=BA*100
2660 LET A(C,C)=R
2670 LET A(C,D)=S
2680 LET A$(C)="DEPOSIT"
2690 RETURN
2700 CLS
2705 IF Q>=M-C THEN PRINT "NO ROOM FOR ";H$;"NAME"
2710 IF N>=B-C OR Q>=M-C THEN PRINT "CHECK BOOK IS FULL",M$
2720 GOTO 2740
2730 CLS
2740 PRINT "ARE YOU FINISHED?",M$;"0= YES",,"1= NO",,E$
2760 INPUT Z$
2780 IF Z$<>"0" THEN GOTO 60
2800 PRINT ,,M$;"PLUG TAPE RECORDER IN.","START RECORDING.","PRESS %E%N%T%E%R",,M$
2810 INPUT Z$
2830 IF CODE Z$<>A THEN GOTO 60
2840 SAVE "CHEC%K"
2850 GOTO 30
2860 PRINT PEEK 16386-PEEK 16412+256*(PEEK 16387-PEEK 16413)-50
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
