Cashflow Master, An 80-column C128 Budgeting Program
Do you have trouble determining if you're making enough money to cover your recurring expenses? Do you have a Commodore 128 and an 80-column monitor sitting around? Well then this program listing is exactly what you need.
Cashflow Master is a program that takes your recurring income sources and expenses, "normalizes" them all to a common time period, and shows you how much is left over at the end of that period. It's based on a spreadsheet that I've been using (and evolving) for nearly 20 years. The spreadsheet of course does a lot of extra things specific to my own needs, and maybe someday they'll make it into Cashflow Master, but for now I thought it would be cool to start with the basics... and while I was at it, learn some new BASIC 7 tricks.
Why the 80 column display? For two reasons: One, for practical reasons as there could potentially be a lot of info on the screen at once. And two, well I guess as a kid writing C64 games and programs, the C128 and its mysterious 80 column display was very intriguing to me. A few years back, I was fortunate enough to find a C128 at a local thrift shop in perfect condition...for $3.00!. It was sitting in a pile of PC keyboards, so as you can guess, it was most likely mistaken for one and priced accordingly. It did not have a power supply with it, but I grabbed one on eBay for $18 and I was in business. However, at the time I didn't have a way to use the 80-column screen. So, fast forward to this year (see also my recent post about accessing an ANSI BBS from a C128 using DesTerm), I picked up a Commodore 1084D monitor with the correct RGB connection for the 128's VDC output.
Using It
The main screen at startup presents you with a two-column table. As the on-screen instructions indicate, you can press either E or I to add an expense or income respectively. For each entry, you specify a description, an amount, and a period -- i.e., how often it recurs. Once you've entered some expenses and at least one income source, Cashflow Master will calculate your remaining cash flow per the current global period - which is indicated at the bottom of the screen. Press the G key to cycle through global period settings. The global periods available are the same ones you can specify for each income or expense -- currently: yearly, quarterly, monthly, bi-weekly, weekly, or daily. When you're done, you can press the Q key to quit and poke around the program listing if you like.
The Listing
The program is broken up into a few main parts, starting at each of the following line numbers:
5 - 10: Jumps to initialization routines.100 - 199: The mainloop which waits for keypresses and jumps elsewhere in the program accordingly.200 - 299: The "create a new income" subroutine.300 - 399: The "create a new expense" subroutine.999: Jump here to tidy up and end the program.1000-1999: Draw the screen. (Jump to 1500, if only re-writing the tables rather than a full clear + redraw.)2000-2999: Program initialization subroutine. Sets up variables and screen colors.3000-3999: A popup-window drawing routine. This draws a frame in the center of the screen and uses the BASIC 7WINDOWcommand to set up a dialog box of sorts.4000-4040: This subroutine does all the calculation -- it adjusts each income and expense to match the global period, storing the results in two new array variables. Then the total remaining (or deficit) is calulated by summing the values in the two arrays.
Download
- Get the program here: PRG format
- If you prefer, you can download the BASIC Listing and convert to a PRG using the command
petcat -w70 -o cfmaster.prg -- cfmaster.bas. (petcatis included with the VICE emulator.) - The program listing is below, as rendered by
petcat.
;./CFMASTER ==1c01==
5 gosub 2000 : rem init
10 gosub 1000 : rem draw screen
100 getkey k$ : rem mainloop
105 if k$="q" then goto 999
110 if k$="i" then gosub 200
120 if k$="e" then gosub 300
130 if k$="g" then begin
131 gp=gp+1
132 if gp>5 then gp=0
133 gosub 1000
134 bend
199 goto 100
200 gosub3000:print"create new income"
205 ifni=mithenprint"max incomes (";mi;") already created.":print"press a key":getkeyk$:return
210 print"income description";:inputd$
220 print"income period (yqmbwd)";:inputp$
225 ok=0:fori=0to5:ifp$(i)=p$thenip(ni)=i:ok=1
226 nexti:ifok=0thenprint"invalid period selected.":goto220
230 print"income amount";:inputa
235 ifa<=0thenprint"incomes must be greater than zero.":goto230
240 id$(ni)=d$
250 ia(ni)=a:ni=ni+1
299 print"{home}{home}":gosub1000:return
300 gosub3000:print"create new expense"
305 ifne=methenprint"max expenses (";me;") already created.":print"press a key":getkeyk$:return
310 print"expense description";:inputd$
320 print"expense period (yqmbwd)";:inputp$
325 ok=0:fori=0to5:ifp$(i)=p$thenep(ne)=i:ok=1
326 nexti:ifok=0thenprint"invalid period selected.":goto320
330 print"expense amount";:inputa
335 ifa<=0thenprint"expenses must be greater than zero.":goto330
340 ed$(ne)=d$
350 ea(ne)=a:ne=ne+1
399 print"{home}{home}":gosub1000:return
999 scnclr:slow:end
1000 scnclr : rem full redraw
1010 printh$;left$(x$,40-len(tt$)/2);r$;tt$
1020 fori=0to78:char1,i,1,"C":char1,i,22,"C":nexti:char1,39,1,"{CBM-R}"
1030 fori=2to21:char1,39,i,"B":nexti:char1,39,22,"{CBM-E}"
1500 printh$;left$(y$,2);left$(x$,20-len(et$)/2);et$
1510 if ne>0 then begin
1520 fori=0tone-1
1530 printleft$(ed$(i),md);left$(x$,md-len(ed$(i)));
1540 print using cf$;ea(i);:print" ";r$;p$(ep(i))
1550 nexti
1560 bend:else:print"press {rvon}e{rvof} to add an expense"
1570 printh$;left$(y$,2);left$(x$,60-len(it$)/2);it$
1580 if ni>0 then begin
1590 fori=0toni-1
1600 printleft$(x$,40);left$(id$(i),md);left$(x$,md-len(id$(i)));
1610 print using cf$;ia(i);:print" ";r$;p$(ip(i))
1620 nexti
1630 bend:else:printleft$(x$,40);"press {rvon}i{rvof} to add an income"
1640 gosub 4000:rem calculate totals
1650 printh$;left$(y$,23);"total expenses:";left$(x$,md-15);
1655 print using cf$;et;
1660 printh$;left$(y$,23);left$(x$,40);"total incomes:";left$(x$,md-14);
1665 print using cf$;it;
1670 printh$;left$(y$,24);"{rvon}g{rvof}lobal period: ";left$(x$,md-5-len(pd$(gp)));pd$(gp);
1680 printh$;left$(y$,24);left$(x$,40);
1690 ifre>=0 then print"{grn}remaining:";left$(x$,md-10);
1700 ifre<0 then print"{red}deficit:";left$(x$,md-8);
1710 print using cf$;re;:print"{wht}";
1999 return
2000 color5,2:color6,1
2010 ifrgr(0)<>5thenprint"this program must be run in 80-column mode.":end:else fast
2020 mi=20:me=20:ma=5:ni=0:ne=0:na=0:se=na:md=27:it=0:et=0:re=0
2030 dim id$(mi),ip(mi),ia(mi),ai(mi)
2040 dim ed$(me),ep(me),ea(me),ae(me)
2050 dim ad$(ma),ap(ma),aa(ma)
2060 y=5:q=4:m=3:b=2:w=1:d=0
2061 dim p(6):p(y)=365.25:p(q)=p(y)/4:p(m)=p(y)/12:p(b)=14:p(w)=7:p(d)=1
2062 dim p$(6):p$(y)="y":p$(q)="q":p$(m)="m":p$(b)="b":p$(w)="w":p$(d)="d"
2063 dim pd$(6):pd$(y)="yearly":pd$(q)="quarterly":pd$(m)="monthly":pd$(b)="bi-weekly":pd$(w)="weekly":pd$(d)="daily"
2065 h$="{home}":r$="{rvon}":x$="{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}{rght}":y$="{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}{down}"
2070 tt$=" c a s h f l o w m a s t e r "
2080 et$="expenses":it$="incomes":at$="accounts"
2090 wt=229:wb=228:wl=230:wr=231:dt=5:db=20:dl=20:dr=60
2100 gp=m : rem default to monthly
2200 cf$="#######.##"
2999 return
3000 char1,dl-1,dt-1,"U":char1,dl-1,db+1,"J"
3010 fori=dltodr:char1,i,dt-1,"C":char1,i,db+1,"C":nexti
3020 char1,dr+1,dt-1,"I":char1,dr+1,db+1,"K"
3030 fori=dttodb:char1,dl-1,i,"B":char1,dr+1,i,"B":nexti
3040 windowdl,dt,dr,db,1:return
4000 it=0:et=0:rem calculate totals
4010 fori=0toni:ai(i)=ia(i)/p(ip(i))*p(gp):it=it+ai(i):nexti
4020 fori=0tone:ae(i)=ea(i)/p(ep(i))*p(gp):et=et+ae(i):nexti
4030 re=it-et
4040 return
Modified Friday, November 07, 2025
