This program implements a personal checking account management system, storing up to 500 transaction records in a dimensioned string array where each 46-character record encodes the check number, date, payee name, amount, running balance, category, and void status using a compact binary packing scheme. Amounts and balances are stored as 5-byte binary-encoded values using a custom FN n$/FN n pair of functions that pack a floating-point dollar amount into three bytes of integer magnitude, one sign byte, and one byte of cents. The system supports writing checks, recording deposits, and searching transactions by month, date, payee name, or spending category across 16 predefined categories. Data persistence is handled by saving and loading the entire array to tape, with metadata such as the current year, check-charge fee, and transaction count stored in the last record slot. An ON ERR handler at line 9990 inspects the system variable at address 23736–23737 to recover gracefully from input errors at specific line numbers rather than crashing.
Program Analysis
Program Structure
The program is organized into functional modules accessed through a menu at line 500. Navigation uses computed GO SUB targets stored in variables (bord, getdate, senter, nospace, list), and the menu dispatcher at line 710 uses a lookup string to jump to the correct routine:
| Menu Key | Label | Line |
|---|---|---|
| A | Write a check | 1000 |
| B | Make a deposit | 1500 |
| C | Review all transactions | 2000 |
| D | List by month | 5000 |
| E | List by date | 2500 |
| F | List by name | 3000 |
| G | List by category | 5500 |
| H | Display category list | 6000 |
| I | Void a transaction | 3500 |
| J | Save data | 4000 |
| K | Load data | 6500 |
| L | Quit | 4500 |
The dispatch string at line 708 is "100015002000500025003000550060003500400065004500"; the correct 4-character substring is selected using the computed index x derived from the key pressed.
Record Layout
Each transaction is stored as a fixed-length 46-character string in a$(next, rlen) where rlen = 46 (30+2+2+5+5+1+1). The fields are:
| Bytes | Content |
|---|---|
| 1–2 | Check/deposit number (little-endian 16-bit) |
| 3 | Month (1–12) |
| 4 | Day |
| 5–34 | Payee name (30 chars, space-padded) |
| 35–39 | Amount (5-byte packed binary float) |
| 40–44 | Running balance (5-byte packed binary float) |
| 45 | Void flag (CHR$ 255 = voided) |
| 46 | Category index (1–16) |
Deposits are distinguished from checks by having their transaction number ≥ 10000 (starting at 10001). Deposit amounts are stored as negated values so that a uniform running-balance formula (old - amnt) works for both checks and deposits.
Binary Amount Encoding
The most technically interesting aspect is the custom 5-byte binary floating-point encoding for dollar amounts, implemented entirely in BASIC using DEF FN. The packing function FN n$(q) at line 40 encodes an amount as:
- Byte 1: high byte of integer part (
INT ABS q / 65536) - Byte 2: middle byte of integer part
- Byte 3: low byte of integer part
- Byte 4: sign byte (0 = positive, 1 = negative via
q<0) - Byte 5: cents (fractional part × 100)
The complementary unpacking function FN n(y$) at line 12 reconstructs the value using FN z(y$) (line 10) which reassembles the 3-byte integer, then subtracts twice its value if the sign byte is nonzero. This avoids storing negative numbers using two’s-complement, instead using a sign-magnitude scheme.
Search and Result Packing
All search routines (by date, name, month, category) build a result list p$ as a packed string of 2-byte little-endian record indices. After searching, the generic list display routine at line 2700 iterates over p$ in steps of 2, reconstructing each record index with CODE p$(t)+CODE p$(t+1)*256. This is an efficient indirect-reference scheme that avoids duplicating record data. The cycling BORDER color (BORDER u-INT(u/8)*8) during searches provides a visual progress indicator.
Error Handling
The program installs a global ON ERR GO TO 9990 handler. The handler at line 9990 reads the error line number from system variables at addresses 23736–23737 and dispatches to appropriate recovery points for known problematic lines (9050, 205, 9030). Line 9995 uses IF erl=205 to recover from a VAL "" error when the user enters an empty check-charge string. If the error occurs within the handler itself (line ≥ 9990), execution stops with ON ERR RESET.
Date Handling
The getdate subroutine at line 9010 accepts dates in MM/DD format, scanning the input string for a / delimiter. If no delimiter is found, it falls back to using the previously entered month and interpreting the entire input as a day number. Leap year detection for February is handled in the DATA statement at line 135: 28+(1 AND year/4=INT(year/4)).
Display Utilities
The senter subroutine at line 9100 center-prints a name by trimming trailing spaces then using TAB 15-LEN e$/2. The FN p$(s) function at line 50 formats dollar amounts to two decimal places by appending "." when the value is a whole number, then appending "00" and slicing to ensure the correct length.
Notable Anomalies
- Line 9370 uses
LET r$=r$+CHR$ (...)which appends to any existing content ofr$rather than assigning fresh. Sincer$is initialized to""at line 5510, this is harmless in normal flow, but could accumulate stale data on repeated category lookups without that initialization path. - The word “catagory” (a misspelling of “category”) is used consistently throughout all menus and messages.
- Several lines of code are commented out inline with
REMprefixes mid-line (e.g., lines 2027, 2050), suggesting iterative development and debugging. - The quit routine at line 4510 uses
ON ERR RESET : STOP, a defensive measure to clean up the error handler state before halting. - The metadata record stored in
a$(max+1)at line 4025 encodes only a single byte foryear(the 2-digit form), limiting the system to years expressible as values 0–255 offset from 1900.
Content
Source Code
1 ON ERR GO TO 9990
4 INVERSE 0
5 INK 0: PAPER 1: BORDER 1: CLS : PRINT INK 7;AT 8,2;"THE PERSONAL CHECKING SYSTEM"
7 PAUSE 100: CLS
10 DEF FN z(x$)=CODE x$(1)*65536+CODE x$(2)*256+CODE x$(3)
12 DEF FN n(y$)=VAL (STR$ (FN z(y$)-(2*FN z(y$) AND CODE y$(4)))+"."+STR$ CODE y$(5))
20 DEF FN a(u)=INT (u/65536)
30 DEF FN b(w)=w-FN a(w)*65536
40 DEF FN n$(q)=CHR$ FN a(INT ABS q)+CHR$ (INT (FN b(INT ABS q)/256))+CHR$ (FN b(INT ABS q)-256*INT (FN b(INT ABS q)/256))+CHR$ (q<0)+CHR$ (100*(ABS q-INT ABS q))
50 DEF FN p$(s)=(STR$ s+("." AND s=INT (s))+("00"))( TO LEN STR$ INT s+3)
60 LET dep=1
70 DIM m$(12,3): RESTORE 80: FOR t=1 TO 12: READ m$(t): NEXT t
80 DATA "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
100 REM
120 INPUT "Year: ";year: IF year>1900 THEN LET year=year-1900
130 DIM m(12): FOR t=1 TO 12: READ m(t): NEXT t
135 DATA 31,28+(1 AND year/4=INT (year/4)),31,30,31,30,31,31,30,31,30,31
140 LET oldm=1: LET oldd=1
150 REM
155 LET list=2700
160 GO SUB 9010
200 INPUT "Check charge: ";z$
205 LET cc=VAL (z$+("0" AND z$=""))
210 LET bord=9900
212 LET ctri=6
214 LET getdate=9010
216 LET senter=9100
218 LET nospace=9200
300 REM
305 LET max=500
310 LET rlen=VAL "30+2+2+5+5+1+1"
320 DIM a$(max+1,rlen)
330 LET next=1
340 DIM c$(16,20)
350 RESTORE 360: FOR r=1 TO 16: READ c$(r): NEXT r
360 DATA "Automotive","Vacation","Food","Heating","Clothing","Home Improvements","Mortgage/Loans","Insurance","Paycheck","Medical Care","Cash- personal","Rent","Telephone","Special 1","Special 2","Special 3"
500 REM
501 INVERSE 0
502 LET k=4: LET h=4
510 INK 0: PAPER 5: BORDER 6: CLS
520 GO SUB bord
525 PRINT INK 0;AT 1,2;"The Personal Checking System"
526 PLOT 0,156: DRAW 255,0
527 FOR e=h-1 TO h+15: PRINT INVERSE 1;AT e,1;" ": NEXT e
528 ON ERR GO TO 9990
530 PRINT INVERSE 1;AT h,k;"[A] Write a check";AT h+1,k;"[B] Make a deposit";AT h+2,k;"[C] Review all transactions";AT h+3,k;"[D] List by month"
532 PRINT INVERSE 1;AT h+4,k;"[E] List by date";AT h+5,k;"[F] List by name";AT h+6,k;"[G] List by catagory";AT h+7,k;"[H] Display catagory list";AT h+8,k;"[I] Void a transaction"
534 PRINT INVERSE 1;AT h+9,k;"[J] Save data";AT h+10,k;"[K] Load data";AT h+11,k;"[L] Quit"
550 PRINT AT 18,25; INVERSE 1;" "; INVERSE 0;year+1900; INVERSE 1;" ";AT 17,25;" ";AT 19,25;" "
560 PRINT AT 17,k; INK 1;" Please select... "
600 REM
622 FOR y=-1 TO 8: PLOT OVER 1;k*8-1,176-(17)*8-y-1: DRAW OVER 1;145,0
630 LET k$=CHR$ PEEK 23556
640 IF k$>="A" AND k$<="L" THEN GO TO 700
645 REM IF k$<>CHR$ 255 THEN BEEP .125,-24
650 NEXT y
660 GO TO 600
700 LET x=CODE k$-CODE "A"+1
701 LET x=4*(x-1)+1
702 INVERSE 0: PAPER 5
705 BEEP .125,32
708 LET x$="100015002000500025003000550060003500400065004500"
710 GO SUB VAL x$(x TO x+3)
720 GO TO 500
999 STOP
1000 REM check
1002 LET qq=5
1005 IF next=max+1 THEN GO TO nospace
1010 CLS : GO SUB bord
1020 INPUT "Check number: ";cn
1030 IF cn<>INT cn OR cn<1 OR cn>9999 THEN GO TO 1020
1035 PRINT PAPER qq;AT 2,1;"Check number: ";cn
1040 GO SUB getdate
1045 PRINT PAPER qq;AT 3,1;"Date: ";m$(m);" ";STR$ d;",";year+1900
1050 INPUT "Check issued to: ";n$
1051 IF n$="" THEN LET n$="CASH"
1052 GO SUB bord
1054 IF LEN n$>30 THEN LET n$=n$( TO 30)
1055 PRINT PAPER qq;AT 6,1;: GO SUB senter
1060 INPUT "Amount: ";amnt
1070 IF amnt>=256^3 OR amnt<0 THEN GO TO 1060
1071 IF n$="" THEN LET n$="PAYCHECK"
1075 PRINT PAPER qq;AT 9,1;"Amount: $";FN p$(amnt)
1080 LET cn1=INT (cn/256): LET cn2=cn-cn1*256: LET r$=CHR$ cn2+CHR$ cn1
1090 LET r$=r$+CHR$ m+CHR$ d
1095 IF LEN n$>30 THEN LET n$=n$( TO 30)
1100 LET n$=n$+" "( TO 30-LEN n$)
1110 LET r$=r$+n$
1120 LET r$=r$+FN n$(amnt)
1125 LET old=0: IF next=1 THEN GO TO 1140
1130 LET old=FN n(a$(next-1)(40 TO 44))
1140 LET new=old-amnt-(cc AND cn<10000)
1150 LET r$=r$+FN n$(new)+CHR$ 0
1155 GO SUB 9300
1160 LET a$(next)=r$
1170 LET next=next+1
1180 RETURN
1500 REM deposit
1510 IF next=max THEN GO TO nospace
1520 CLS : GO SUB bord
1530 LET cn=10000+dep: LET dep=dep+1
1540 PRINT AT 2,1;"Deposit Number: ";cn-10000
1550 GO SUB getdate
1560 PRINT AT 3,1;"Date: ";m$(m);" ";STR$ d;",";year+1900
1570 INPUT "Source: ";n$
1572 GO SUB bord
1575 IF LEN n$>30 THEN LET n$=n$( TO 30)
1580 PRINT AT 6,1;: GO SUB senter
1590 INPUT "Amount: ";amnt
1600 IF amnt>=256^3 OR amnt<0 THEN GO TO 1590
1610 PRINT AT 7,1;"Amount $";FN p$(amnt)
1615 LET amnt=0-amnt
1620 GO TO 1080
2000 REM latest
2020 LET cur=next-1
2025 IF cur<1 THEN RETURN
2027 REM CLS : REM GO SUB bord
2030 GO SUB 2300
2040 PRINT AT 17,1;("Press {M} to see the next one." AND cur>1)+("Done, Press {X} for menu. " AND cur=1);AT 18,1;("Press any other key for menu." AND cur>1)+(" " AND cur=1)
2050 LET l$=INKEY$: REM BEEP .0025,40: BEEP .0025,60: REM BEEP .0025,50
2055 IF l$="" THEN GO TO 2050
2060 IF l$="m" OR l$="M" THEN LET cur=cur-1: GO TO 2025
2070 IF l$="x" OR l$="X" THEN RETURN
2080 IF l$="" THEN GO TO 2050
2090 RETURN
2300 REM
2310 CLS : GO SUB bord
2315 LET crec=cur
2317 LET w$=a$(crec)
2320 INK 0
2325 LET tn=CODE w$+CODE w$(2)*256
2327 LET o$="Deposit": IF tn<10000 THEN LET o$="Check"
2328 PRINT AT 3,15-LEN o$/2;o$
2329 INK 0
2330 PLOT 5,60: DRAW 245,0: DRAW 0,80: DRAW -245,0: DRAW 0,-80
2335 INK 1
2340 PRINT INVERSE 1;AT 5,31-LEN STR$ (tn-(10000 AND tn>10000)); INVERSE 1;tn-(10000 AND tn>10000)
2350 LET m=CODE w$(3): LET d=CODE w$(4)
2360 PRINT AT 5,1;m$(m);" ";STR$ d;",";STR$ (year+1900);" "
2365 PLOT 8,126: DRAW 8*(9+LEN STR$ d),0
2367 DRAW INK 5;20,0
2370 PRINT AT 8,1;: LET n$=w$(5 TO 5+29): GO SUB senter
2375 PLOT 8,111-8: DRAW 255-(16),0
2377 PRINT AT 10,1;c$(CODE w$(46))
2380 PRINT AT 13,1;"Amount:";
2390 PRINT AT 13,31-LEN FN p$(ABS FN n(w$(35 TO 39)));FN p$(ABS FN n(w$(35 TO 39)))
2392 IF CODE w$(45)=255 THEN PRINT AT 11,12;"**VOID**"
2395 INK 0
2400 PRINT AT 15,1;"Balance Forward: ";
2410 PRINT AT 15,31-LEN FN p$(FN n(w$(40 TO 44)));FN p$(FN n(w$(40 TO 44)))
2415 PRINT AT 18,1;"Press any other key to go on."
2420 PRINT AT 17,1;"For a printer copy, press {"; FLASH 1;"C"; FLASH 0;"}"
2422 LET l$=INKEY$: IF l$="c" OR l$="C" THEN COPY
2424 REM BEEP .0125,28
2425 IF l$="" THEN GO TO 2422
2430 IF INKEY$<>"" THEN GO TO 2430
2490 RETURN
2500 REM by date
2505 CLS : GO SUB bord
2510 LET p$=""
2520 GO SUB getdate
2530 LET i$=CHR$ m+CHR$ d
2535 PRINT AT 15,10; FLASH 1;" SEARCHING "
2540 FOR u=1 TO next-1
2545 BORDER u-INT (u/8)*8
2550 IF i$=a$(u)(3 TO 4) THEN LET a1=INT (u/256): LET a2=u-a1*256: LET p$=p$+CHR$ a2+CHR$ a1
2560 NEXT u
2570 BORDER 6
2580 PRINT AT 15,10;" "
2585 IF LEN p$ THEN GO TO 2620
2590 PRINT AT 1,1;"You made no transactions on ";AT 2,1;"the date of ";m$(m);" ";STR$ d;",";year+1900;"."
2600 PRINT AT 4,1;"Press any key to return to the";AT 5,1;"menu."
2610 PAUSE 0: RETURN
2620 GO SUB list
2630 RETURN
2700 REM list
2710 FOR t=1 TO LEN p$ STEP 2
2720 LET cur=CODE p$(t)+CODE p$(t+1)*256: GO SUB 2300
2730 PRINT AT 17,1;("Press {M} to go on " AND t<LEN p$-1)+("Done, " AND t=LEN p$-1);AT 18,1;"Press any key to go to menu. "
2740 LET k$=INKEY$
2745 REM BEEP .0025,40: BEEP .0025,60
2760 IF k$="m" OR k$="M" THEN NEXT t: RETURN
2770 IF k$="" THEN GO TO 2740
2780 RETURN
3000 REM by name
3010 CLS : GO SUB bord
3020 INPUT "Name: ";s$
3030 IF s$="" THEN GO TO 2000
3040 IF LEN s$>30 THEN GO TO 3020
3050 LET p$=""
3060 PRINT AT 10,10; FLASH 1;" SEARCHING "
3070 FOR j=1 TO next-1
3075 BORDER j-INT (j/8)*8
3080 IF a$(j,5 TO 5+LEN s$-1)=s$ THEN LET p1=INT (j/256): LET p2=j-p1*256: LET p$=p$+CHR$ p2+CHR$ p1
3090 NEXT j
3100 PRINT AT 10,10;" "
3110 IF LEN p$ THEN GO TO list
3115 BORDER 6: CLS : GO SUB bord
3120 PRINT AT 1,1;"No such name exists."
3130 PRINT AT 3,1;"Press any key to return to the";AT 4,1;"menu."
3140 PAUSE 0
3150 RETURN
3500 REM void
3510 CLS : GO SUB bord
3520 INPUT "Transaction: ";tn
3530 LET t1=INT (tn/256): LET t2=tn-t1*256
3540 LET t$=CHR$ t2+CHR$ t1
3550 PRINT AT 10,10; FLASH 1;" SEARCHING "
3560 FOR y=1 TO next-1
3570 BORDER y-INT (y/8)*8
3590 IF a$(y)( TO 2)=t$ THEN GO TO 3650
3600 NEXT y
3605 BORDER 6
3610 PRINT AT 10,10;" "
3620 PRINT AT 1,1;"No such transaction exists.";AT 2,1;"Press any key to return to the";AT 3,1;"menu."
3630 PAUSE 0
3640 RETURN
3650 LET a$(y,45)=CHR$ 255
3660 LET adj=FN n(a$(y,35 TO 39))
3661 LET adj=adj+cc
3665 PRINT AT 10,10; FLASH 1;" ADJUSTING "
3670 FOR j=y TO next-1
3675 BORDER j-INT (j/8)*8
3680 LET a$(j,40 TO 44)=FN n$(FN n(a$(j,40 TO 44))+adj)
3690 NEXT j
3700 RETURN
4000 REM save
4010 CLS : GO SUB bord
4020 PRINT AT 1,1;"Please connect the MIC jack on";AT 2,1;"your computer to the MIC jack";AT 3,1;"on your tape recorder."
4022 LET n1=INT (next/256): LET n2=next-n1*256
4025 LET a$(max+1)=CHR$ oldm+CHR$ oldd+CHR$ year+CHR$ cc+CHR$ n2+CHR$ n1
4030 SAVE "CHECKS"+STR$ year DATA a$()
4150 RETURN
4500 REM quit
4505 BORDER 7: PAPER 7: INK 0: CLS
4510 ON ERR RESET : STOP
4520 GO TO 500
4999 RETURN
5000 REM list by month
5010 CLS : GO SUB bord
5020 INPUT "Month: ";m
5030 LET p$=""
5040 PRINT AT 10,10; FLASH 1;" SEARCHING "
5050 FOR u=1 TO next-1
5055 BORDER u-INT (u/8)*8
5060 IF CODE a$(u,3)=m THEN LET p1=INT (u/256): LET p2=u-p1*256: LET p$=p$+CHR$ p2+CHR$ p1
5070 NEXT u
5075 BORDER 6
5080 IF p$>"" THEN GO TO list
5085 CLS : GO SUB bord
5090 PRINT AT 1,1;"No transactions were made in";AT 2,1;m$(m);", ";year+1900;"."
5100 PRINT AT 4,1;"Press any key to return to the";AT 5,1;"menu."
5120 PAUSE 0
5130 RETURN
5500 REM list by catagory
5510 LET r$=""
5520 GO SUB 9300
5530 LET p$=""
5540 FOR t=1 TO next-1
5545 BORDER t-INT (t/8)*8
5550 IF a$(t,46)=r$ THEN LET p1=INT (t/256): LET p2=t-p1*256: LET p$=p$+CHR$ p2+CHR$ p1
5560 NEXT t
5565 BORDER 6
5570 IF p$="" THEN CLS : GO SUB bord: PRINT AT 1,1;"No transactions have been made";AT 2,1;"in that catagory.": GO TO 5100
5580 GO TO list
6000 REM review catagory list
6010 GO SUB 9250
6020 IF INKEY$="" THEN GO TO 6020
6030 RETURN
6499 STOP
6500 REM load
6510 CLS : GO SUB bord
6520 PRINT AT 1,1;"Rewind your tape.";AT 3,1;"Connect the EAR jack on your";AT 4,1;"computer to the EAR jack on";AT 5,1;"your tape recorder."
6530 PRINT AT 7,1;"Then press PLAY on your tape";AT 8,1;"recorder."
6540 LOAD "" DATA a$()
6550 LET z$=a$(max+1)
6560 LET oldm=CODE z$
6570 LET oldd=CODE z$(2)
6580 LET year=CODE z$(3)
6590 LET cc=CODE z$(4)
6600 LET next=CODE z$(5)+CODE z$(6)*256
6610 RETURN
9000 REM
9010 REM getdate
9015 ON ERR GO TO 9990
9020 INPUT "Date (ie 11/20): ";d$: IF d$="" THEN LET d=oldd: LET m=oldm: GO TO 9060
9025 LET m=0: LET d=0
9030 FOR y=1 TO LEN d$: IF d$(y)="/" THEN LET m=VAL d$( TO y-1): LET d=VAL d$(y+1 TO ): GO TO 9060
9040 NEXT y
9050 LET m=oldm: LET d=VAL d$
9060 LET oldm=m: LET oldd=d
9064 IF m<1 OR m>12 THEN GO SUB 9940: GO TO 9010
9065 IF d<0 THEN GO SUB 9940: GO TO 9010
9067 IF d>m(m) THEN GO SUB 9940: GO TO 9010
9070 RETURN
9100 REM senter
9110 LET e$=n$
9120 IF e$="" THEN RETURN
9130 IF e$(LEN e$)=" " THEN LET e$=e$( TO LEN e$-1): GO TO 9120
9140 PRINT TAB 15-LEN e$/2;e$
9150 RETURN
9200 REM nospace
9210 CLS : GO SUB bord: PRINT AT 1,1;"There is no more space to hold";AT 2,1;"more transactions."
9220 PRINT AT 4,1;"You should SAVE this set of";AT 5,1;"data for ";year+1900;"."
9230 PRINT AT 7,1;"Press any key to return to the";AT 8,1;"menu.": PAUSE 0: RETURN
9250 REM
9260 LET spec=1
9270 GO TO 9310
9300 REM get catagory
9305 LET spec=0
9310 CLS : GO SUB bord
9320 PRINT AT 1,1;("Choose your catagory: " AND NOT spec)+("Catagories: [Any key for menu]" AND spec)
9330 FOR y=0 TO 15: PRINT AT y+3,3;"{"+CHR$ (CODE "A"+y)+"} ";c$(y+1)
9340 NEXT y
9345 IF spec THEN RETURN
9350 LET k$=CHR$ PEEK 23556
9360 IF k$<"A" OR k$>"P" THEN GO TO 9350
9370 LET r$=r$+CHR$ (CODE k$-CODE "A"+1)
9380 RETURN
9900 REM
9910 FOR z=0 TO 1: PLOT 0+z,0+z: DRAW 255-(2*z),0: DRAW 0,175-(2*z): DRAW -(255-(2*z)),0: DRAW 0,-(175-(2*z)): NEXT z
9920 PRINT AT 20,1; PAPER 3; BRIGHT 1;" \* 1982, by Purple Sunrise "
9939 RETURN
9940 BEEP .05,-12
9949 RETURN
9990 ON ERR RESET
9991 LET erl=PEEK 23736+256*PEEK 23737
9992 IF erl>=9990 THEN ON ERR RESET : STOP
9993 ON ERR GO TO 9990
9994 IF erl=9050 THEN GO TO 9010
9995 IF erl=205 THEN GO TO 200
9996 IF erl=9030 THEN GO TO 9020
9998 ON ERR CONTINUE
9999 INK 0: PAPER 7: BORDER 7: CLS
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


