Budgeter is a home accounts program that tracks budgeted and actual expenditures across 18 spending categories, including food, clothing, rent, utilities, and savings. It uses a two-dimensional numeric array T(18,2) to store budget and actual figures, and a string array C$(18,10) for category names. A machine code routine embedded in the REM statement at line 100 is called via USR at lines 1070 and 1170, toggling six consecutive memory locations (16707–16712) between 128 and 0, likely to control screen borders or perform a display-clearing operation. The program supports tape save and load operations, allowing budget data to persist across sessions, and includes a listing screen that displays all 18 categories with their budget totals, actual totals, and remaining balance.
Program Analysis
Program Structure
The program is organized into several functional blocks, each accessed via GOSUB from the main flow. The top-level logic begins at line 262 after a deliberate initialization guard, progresses through date verification at line 274, and then enters the main menu loop starting around line 500.
| Line Range | Purpose |
|---|---|
| 100 | Machine code in REM statement |
| 140–160 | Halt/warning message (machine code clobber guard) |
| 200–260 | POKE loop utility (manual data entry via INPUT) |
| 262–299 | Initialization, date entry, intro screen |
| 500–580 | Main program loop |
| 1000–1080 | Subroutine: set display bytes to 128 (inverse), call USR |
| 1100–1180 | Subroutine: set display bytes to 0, call USR |
| 1200–1299 | Title/intro screen subroutine |
| 1300–1399 | Master menu display and category selection |
| 1400–1499 | Function menu (Budget/Actual adjust) |
| 2000–2499 | Amount entry and confirmation logic |
| 2500–2999 | Update and display new budget/actual values |
| 7000–7099 | Error message for oversized input |
| 8000–8100 | Initialize category name strings |
| 8500–8530 | Quit with confirmation and optional save |
| 8800–8899 | Short delay loop (FOR N=1 TO 20) |
| 9000–9060 | Save to tape |
| 9100–9240 | Full listing display of all 18 categories |
Machine Code in REM
Line 100 contains an extensive machine code routine embedded in a REM statement, a well-known technique for storing executable Z80 code within BASIC. The routine is invoked via USR 16720 at lines 1070 and 1170. The two subroutines at lines 1000 and 1100 differ only in the values POKEd into addresses 16707–16712 (128 vs. 0) before the USR call, suggesting the machine code performs a display or memory operation whose behavior is parameterized by those six bytes. The pattern of setting multiple consecutive memory locations to \x80 (binary 10000000) or \x00 is consistent with border color or attribute manipulation.
Data Structures
The program uses two arrays declared at lines 264–266:
DIM C$(18,10)— a string array holding the 10-character names of 18 budget categories, populated in the subroutine at line 8000.DIM T(18,2)— a numeric array with two columns per category: column 1 for budgeted amount and column 2 for actual expenditure.
The variables Z and G accumulate budget and actual totals respectively in the listing loop (lines 9105–9133), and F holds the remaining balance computed as Z-G.
Key BASIC Idioms
The program uses several characteristic idioms:
- INKEY$ polling loops: Lines like
IF INKEY$="" THEN GOTO 1442busy-wait for a keypress without consuming an INPUT prompt, giving a responsive feel to menu navigation. - VAL A$ for string-to-number conversion: Used at line 1385 to convert the menu selection string to a numeric index.
- AT coordinates in PRINT: Extensively used throughout for precise screen layout.
- Inverse video in PRINT strings: The listing screen header at line 9112 uses inverse characters (e.g.,
%A%C%C%O%U%N%T) to create highlighted column headers.
Navigation and Menu Flow
The main loop (lines 500–580) calls subroutines in sequence: a screen clear (1000), the main display init (8000), another clear, the master menu (1300), then the function menu (1400). After function selection, control passes to the add/adjust logic at line 2000 if B is 1 or 2. The loop returns to line 560 repeatedly, with GOTO 550 used as a soft reset to the top of the main loop.
Budget Adjustment Logic
Lines 2542–2544 perform the actual update: T(A,B)=T(A,B)+P is written twice, once for B=1 and once for B=2, which is functionally redundant — a single unconditional statement would suffice since only one branch executes. The same pattern is repeated for the undo at lines 2562–2564. This redundancy appears to be a defensive coding style rather than a bug.
Tape Save
The SAVE at line 9050 uses the filename "HOMEACC%T", where %T represents the inverse-video character T. This is the auto-run flag technique. The save routine prompts the user to prepare the recorder before executing the save, then returns to the main flow at line 270.
Initialization Guard and POKE Utility
Lines 140–160 display a warning message if the program is run without jumping past them. The comment at line 152 instructs the user to GOTO 262 if they wish to zero all values, or to reload the tape if data has been clobbered — indicating the machine code at line 100 can overwrite data if the program starts from the beginning. Lines 200–260 implement a raw POKE loop via INPUT, presumably used during development to install or patch the machine code bytes interactively.
Bugs and Anomalies
- Line 2047 references
GOTO 2044, but line 2044 does not exist. Execution would fall through to the next line, making the error branch non-functional. The intent was likely to re-prompt for input after the “TOO BIG” message. - Line 1383 is a REM comment:
REM IF A$<"1" OR A$>"18" THEN GOTO 550. This range check was disabled, replaced by the numeric check at line 1390, but the string comparison would not have worked correctly for two-digit values anyway (e.g., “9” > “18” lexicographically). - Lines 8517–8519 in the quit handler have a logic issue:
IF INKEY$="N" THEN GOTO 270fires immediately after settingL$=INKEY$, meaning the same keypress is re-read. This can cause unpredictable behavior depending on key release timing. - Lines 3000 and 3999 define an empty subroutine that is never called, likely a placeholder left from development.
- The delay subroutine at lines 8800–8899 loops only 20 times, which provides a negligible pause — effectively no delay at normal execution speed.
Content
Source Code
100 REM 2A C4023 159 1ED4A3680C9 0 0 0 02A C4023 159 1ED4A 6 F36802B 5C29D402A C4023 159 1ED4A 6 F368023 5C2AF40C9 0 0 0 0 0 0 0 0 0 0 0 0 02A C4023 12A 1ED4A 6 F368023 5C2CF40C9 0 0 0 0 0 0 0 0 0 E162A C40231121 0 620ED5A22FF40368823 5C2F0402AFF40 DC2E940C9 561 0 0 0 0 0 0 0 0 02A C4023 1 F 01121 0ED4A E15ED5A3680 DC21841C9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0275B 6 0 0 1 0808080808080 0 0 0 0 0 0 0ED5B C4013ED533C413E 6323E413E163240413E 5324141ED4B3E41214341EDB03A41413DC265412A3C411121 0ED5A223C41EB3A40413D3240413C47 5C26341C9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
140 SLOW
150 PRINT "HALTED--TYPE GOTO 262 IF YOU"
152 PRINT "WISH TO ZERO ALL VALUES"
153 PRINT
154 PRINT "OTHERWISE RELOAD TAPE AS YOUR "
156 PRINT "DATA HAS BEEN CLOBBERED"
160 STOP
200 INPUT A
220 INPUT B
230 PRINT B
240 POKE A,B
250 LET A=A+1
260 GOTO 220
262 LET D$="MM/YY"
264 DIM C$(18,10)
266 DIM T(18,2)
267 LET Z=0
268 LET G=0
270 GOSUB 1000
272 GOSUB 1200
273 GOSUB 1000
274 PRINT AT 2,2;"THIS TAPE IS FOR ";D$
276 PRINT AT 4,2;"IS THIS OK ? Y/N"
278 IF INKEY$="" THEN GOTO 276
279 IF INKEY$="Y" THEN GOTO 500
280 IF INKEY$<>"N" THEN GOTO 276
281 DIM C$(18,10)
282 PRINT AT 6,2;"TYPE IN NEW DATE E.G. 02/82"
283 DIM T(18,2)
284 INPUT D$
286 PRINT D$
288 GOSUB 1000
299 GOTO 274
500 GOSUB 1000
505 GOSUB 8000
520 GOSUB 1000
550 GOSUB 1300
560 GOSUB 1400
570 IF B=1 THEN GOSUB 2000
576 IF B=2 THEN GOSUB 2000
580 GOTO 560
999 STOP
1000 REM
1010 POKE 16707,128
1020 POKE 16708,128
1030 POKE 16709,128
1040 POKE 16710,128
1050 POKE 16711,128
1060 POKE 16712,128
1070 LET C=USR 16720
1080 RETURN
1100 REM
1110 POKE 16707,0
1120 POKE 16708,0
1130 POKE 16709,0
1140 POKE 16710,0
1150 POKE 16711,0
1160 POKE 16712,0
1170 LET C=USR 16720
1180 RETURN
1200 REM
1210 PRINT AT 10,12;"HOME"
1220 PRINT AT 11,10;"ACCOUNTS"
1228 PRINT AT 20,2;"HIT ENTER WHEN READY"
1229 INPUT Y$
1240 GOTO 1299
1241 PRINT AT 1,1;"DO YOU NEED INSTRUCTIONS ?"
1244 INPUT A$
1246 PRINT AT 3,1;A$
1248 IF A$="N" THEN GOTO 550
1250 GOTO 7000
1299 RETURN
1300 REM
1310 GOSUB 1000
1320 PRINT AT 1,9;"HOME ACCOUNTS"
1330 PRINT AT 2,6;"MASTER MENU DISPLAY"
1340 PRINT AT 18,1;"ENTER NUMBER/LETTER OF TYPE"
1342 PRINT AT 4,1;"1=FOOD 10=EDUCATION"
1344 PRINT AT 5,1;"2=CLOTHING 11=TELEPHONE"
1346 PRINT AT 6,1;"3=RENT/MORT.12=AUTO"
1348 PRINT AT 7,1;"4=HEAT 13=ENTERTAIN"
1350 PRINT AT 8,1;"5=ELECTRIC 14=VACATION"
1352 PRINT AT 9,1;"6=WATER 15=GOODS"
1354 PRINT AT 10,1;"7=TAXES 16=SERVICES"
1356 PRINT AT 11,1;"8=DENTAL 17=INSURANCE"
1358 PRINT AT 12,1;"9=MEDICAL 18=SAVINGS"
1367 PRINT AT 14,1;"ENTER L TO LIST ALL ENTRIES"
1368 PRINT AT 15,1;"ENTER S TO SAVE TO TAPE"
1369 PRINT AT 16,1;"ENTER Q TO QUIT "
1370 INPUT A$
1372 PRINT AT 19,1;A$
1379 IF A$="L" THEN GOTO 9100
1380 IF A$="Q" THEN GOTO 8500
1382 IF A$="S" THEN GOTO 9000
1383 REM IF A$<"1" OR A$>"18" THEN GOTO 550
1385 LET A=VAL A$
1390 IF A<1 OR A>18 THEN GOTO 550
1399 RETURN
1400 REM
1410 GOSUB 1000
1420 PRINT AT 1,8;"FUNCTION DISPLAY"
1422 PRINT AT 3,1;"ENTER NUMBER FOR FUNCTION"
1424 PRINT AT 4,1;"YOU WISH TO PERFORM"
1425 PRINT AT 6,5;"TYPE= ";C$(A)
1426 PRINT AT 10,2;"1=ADJUST BUDGET ";C$(A)
1428 PRINT AT 12,2;"2=ADJUST ACTUAL ";C$(A)
1442 PRINT AT 18,2;"9=QUIT THIS SECTION"
1443 IF INKEY$="" THEN GOTO 1442
1444 IF INKEY$<>"1" AND INKEY$<>"2" AND INKEY$<>"9" THEN GOTO 1442
1445 LET B=VAL INKEY$
1446 PRINT AT 20,1;B
1448 IF B=9 THEN GOTO 550
1450 IF B<1 OR B>4 THEN GOTO 560
1499 RETURN
2000 REM ADD LOGIC
2010 GOSUB 1000
2020 PRINT AT 1,10;C$(A)
2022 IF B=1 THEN LET S$="BUDGET"
2024 IF B=2 THEN LET S$="ACTUAL"
2025 PRINT AT 2,8;S$;" DISPLAY"
2040 PRINT AT 5,0;"ENTER ADJUST AMOUNT "
2045 INPUT P
2046 IF P>999999 THEN GOSUB 7000
2047 IF P>999999 THEN GOTO 2044
2048 PRINT AT 6,0;"% % % % % % % "
2049 PRINT AT 6,0;P
2050 PRINT
2100 PRINT AT 20,1;"ALL ABOVE CORRECT ? Y/N"
2110 IF INKEY$="" THEN GOTO 2110
2112 LET Y$=INKEY$
2120 PRINT Y$
2130 IF Y$="N" THEN GOTO 2000
2152 GOSUB 2500
2220 GOSUB 1000
2232 PRINT "HIT ENTER KEY WHEN READY"
2234 INPUT G$
2236 GOSUB 1000
2499 RETURN
2500 REM
2510 GOSUB 1000
2520 PRINT AT 2,2;C$(A)
2530 PRINT
2535 PRINT "PREVIOUS BUDGET= ";T(A,1)
2538 PRINT "PREVIOUS ACTUAL= ";T(A,2)
2540 PRINT
2542 IF B=1 THEN LET T(A,B)=T(A,B)+P
2544 IF B=2 THEN LET T(A,B)=T(A,B)+P
2546 PRINT "NEW BUDGET= ";T(A,1)
2548 PRINT "NEW ACTUAL= ";T(A,2)
2552 PRINT "AMOUNT REMAINING= ";T(A,1)-T(A,2)
2554 PRINT AT 20,2;"OK ? Y/N"
2556 IF INKEY$="" THEN GOTO 2556
2557 LET Y$=INKEY$
2558 PRINT Y$
2560 IF Y$="Y" THEN GOTO 560
2562 IF B=1 THEN LET T(A,B)=T(A,B)-P
2564 IF B=2 THEN LET T(A,B)=T(A,B)-P
2566 GOTO 560
2999 RETURN
3000 REM
3999 RETURN
7000 REM
7010 PRINT AT 6,0;"TOO BIG"
7099 RETURN
8000 REM
8010 LET C$(1)="FOOD"
8020 LET C$(2)="CLOTHING"
8022 LET C$(3)="RENT/MORT."
8024 LET C$(4)="HEAT"
8026 LET C$(5)="ELECTRIC"
8028 LET C$(6)="WATER"
8030 LET C$(7)="TAXES"
8032 LET C$(8)="DENTAL"
8034 LET C$(9)="MEDICAL"
8036 LET C$(10)="EDUCATION"
8038 LET C$(11)="TELEPHONE"
8040 LET C$(12)="AUTO"
8042 LET C$(13)="ENTERTAIN"
8044 LET C$(14)="VACATION"
8046 LET C$(15)="GOODS"
8048 LET C$(16)="SERVICES"
8050 LET C$(17)="INSURANCE"
8052 LET C$(18)="SAVINGS"
8100 RETURN
8500 GOSUB 1000
8510 PRINT AT 2,2;"ARE YOU SURE QUIT IS"
8512 PRINT AT 3,2;"WHAT YOU WANT ? Y/N"
8514 IF INKEY$="" THEN GOTO 8514
8515 LET L$=INKEY$
8516 PRINT AT 5,2;L$
8517 IF INKEY$="N" THEN GOTO 270
8518 IF L$="Y" THEN PRINT AT 7,2;"SAVE TO TAPE ? Y/N"
8519 GOSUB 8800
8521 IF INKEY$="" THEN GOTO 8521
8522 LET X$=INKEY$
8523 IF X$="Y" THEN GOTO 9000
8525 PRINT AT 18,2;"OK-QUIT IS DONE"
8528 STOP
8530 GOTO 550
8800 FOR N=1 TO 20
8810 NEXT N
8899 RETURN
9000 REM
9010 GOSUB 1000
9020 PRINT AT 2,2;"SET RECORDER FOR SAVE MODE"
9030 PRINT AT 4,2;"WHEN READY HIT ENTER KEY"
9040 INPUT L$
9050 SAVE "HOMEACC%T"
9060 GOTO 270
9100 REM
9105 LET Z=0
9107 LET G=0
9110 CLS
9112 PRINT AT 0,0;"%A%C%C%O%U%N%T";AT 0,14;"%B%U%D%G%E%T";AT 0,23;"%A%C%T%U%A%L"
9120 FOR N=1 TO 18
9122 PRINT AT N,1;C$(N);" ";AT N,14;T(N,1);" ";AT N,23;T(N,2)
9125 LET Z=T(N,1)+Z
9130 LET G=T(N,2)+G
9132 LET F=Z-G
9133 NEXT N
9134 PRINT "TOTALS ";AT 19,14;Z;AT 19,23;G
9138 PRINT "REMAINING BALANCE= ";F
9220 PRINT AT 21,2;"%P%R%E%S%S% %E%N%T%E%R% %W%H%E%N% %R%E%A%D%Y"
9230 INPUT Y$
9235 CLS
9240 GOTO 550
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

