This program provides three utility functions accessible from a menu: determining the day of the week for a given date, calculating the number of days between two dates, and converting a dollar/cents amount (up to $99.99) into written English text. The day-of-week calculation at line 1150 implements a variant of Zeller’s congruence, adjusting for century terms. The dollars-to-text converter at line 2000 builds a 27-row string array D$() holding number words and uses a subroutine at line 10 to extract variable-length entries, compensating for the fixed-width string array by scanning for trailing spaces. An animated logo routine beginning at line 9550 reads character ROM data via PEEK from address 7680 and renders three letters pixel-by-pixel on screen before displaying a copyright notice.
Program Analysis
Program Structure
The program is organised into four distinct regions: a shared subroutine block, a main menu, three utility modules, and a startup/logo routine.
| Lines | Purpose |
|---|---|
| 10–70 | Shared subroutines: string-array word extractor (10) and DIM G$(9) initialiser (15) |
| 500–580 | Main menu — displays options D, N, T and polls INKEY$ |
| 1000–1180 | Module D: day-of-week via Zeller’s congruence |
| 1500–1690 | Module N: days between two dates |
| 2000–3250 | Module T: dollars/cents to English text |
| 9550–9680 | Startup: logo renderer, copyright notice, then falls through to menu |
Execution begins at line 9550 (the startup block), renders the animated logo, then jumps to the menu at line 500. All three utility modules return to 505 (the menu re-entry point with a CLS) after completing their output.
Subroutines at Lines 10 and 15
Because ZX81 BASIC does not support variable-length string arrays, D$() is dimensioned as DIM D$(27,9) — every row is exactly nine characters, padded with spaces. The subroutine starting at line 10 accepts an index in A and a modifier X1 (used to offset into the tens-words section), computes X as the row index, then scans columns 1–9 to find where trailing spaces begin, storing the result in H. Callers then use G$(1 TO H) to print only the significant characters.
Line 15 (DIM G$(9)) is a subroutine entry point — a clever reuse of a DIM statement mid-program. Calling GOSUB 15 re-dimensions G$ and then falls through into the word-extraction loop at line 20, meaning the same loop body serves both the “fresh dimension” path and the normal extraction path entered via GOSUB 10.
Day-of-Week Module (Lines 1000–1180)
The formula at line 1150 is a variant of Zeller’s congruence:
Q = Y - (M<3)— adjusts year for January/February being treated as months 13/14 of the prior year.K = Q/100— century term (notINTed here; used inINT(K)andINT(K/4)below).T = M + 12*(M<3)— adjusted month number.R = INT(13*(T+1)/5) + INT(5*Q/4) - INT(K) + INT(K/4) + D + 5— core Zeller sum.R = R - 7*INT(R/7) + 1— reduces to range 1–7.
The result indexes into B$ (" MONTUEWEDTHUFRISATSUN") using B$((R*3) TO (R*3)+2), producing a three-character day abbreviation. The two leading spaces in B$ act as a placeholder for index 0 (which should never occur with valid input).
Days-Between Module (Lines 1500–1690)
This module approximates the day count using a decomposed arithmetic approach rather than Julian Day Numbers:
Y = 365*(Y2-Y1)— year contribution in days.M = 31*(M2-M1)— month contribution, using 31 days per month as an approximation.D = D2-D1— day-of-month difference.Xat line 1660 — corrects for months with fewer than 31 days usingINT(.4*M+2.3), a known approximation for cumulative short-month offsets.Zat line 1670 — leap-year correction by counting years divisible by 4, adjusted for whether the date is before or after February.
ABS is applied to the final result so that dates entered in either order yield a positive count. The formula does not account for century years not divisible by 400, so it will be slightly inaccurate for dates spanning e.g. 1900 or 2100.
Dollars-to-Text Module (Lines 2000–3250)
The amount is validated to be in the range [0.01, 9999.99] before processing. It is then multiplied by 100 and truncated to an integer (with a small epsilon +.001 to avoid floating-point under-count), and converted to a string with STR$. The string length L drives a computed GOTO at line 2710:
2710 GOTO 2620+(7-L)*100
This jumps to different entry points in the printing chain depending on how many digits the number has (1–6 digits for amounts up to 999999 cents, though the program limits input to <10000, giving at most 6 digits). The chain handles thousands, hundreds, tens, and units progressively, incrementing pointer A through the digit string.
Teen numbers (11–19) are handled specially at line 3200: when the tens digit is 1, the two-digit value is looked up directly in D$() rows 11–19 via GOSUB 15, bypassing the separate tens/units path.
The offset formula in the extractor subroutine (X = VAL N$(A) + 18*(X1=1)) maps units digits 1–9 to rows 1–9 and tens digits 2–9 to rows 20–27 when X1=1. The value 18 = 20 − 2, accounting for the fact that the tens array starts at row 20 and represents the digit 2 (TWENTY) as the first entry.
Logo Renderer (Lines 9550–9650)
The startup routine spells out “UTY” (from A$="UTY") using pixel-level character ROM reads. For each of the three characters, it PEEKs the 8 bytes of ROM font data at 7680 + CODE(char)*8 + row, then shifts through each bit using a halving-division method (V=128, V=V/2) to decide whether to print an inverse space or a normal space at each pixel position. The three characters are rendered at columns 5, 12, and 19 (step 7 in the FOR L loop), giving evenly spaced large letters.
Notable Bugs and Anomalies
- Line 2035 — “FIFTHTEEN”: The word “FIFTEEN” is misspelled as
FIFTHTEEN(extra “TH”), so amounts containing 15 cents or the number 15 in dollar values will be spelled incorrectly. - Line 2550 — negative number check: The condition
IF N>=.01 THEN GOTO 2580correctly skips the error message for valid inputs, but the error message reads “NO NEGATIVE NUMBERS” even though the real issue is any value below 0.01 (including zero). Negative numbers will also trigger this path, so the message is partially correct but misleading for zero input. - Line 2656 —
GOSUB 15for teens: CallingGOSUB 15re-dimensionsG$each time, which is safe but redundant when called repeatedly; it costs extra time compared toGOSUB 10. - Lines 2850–2925 — skipped hundred handling: When
VAL N$(A)=0at line 2850 (no hundreds digit), execution jumps to 2840 incrementingA, but line 2840 is inside the hundreds block. The flow relies on fall-through from variousGOTOtargets being correctly aligned; a careful trace shows the logic is functional but the spaghetti branching makes it fragile. - Cents always printed as digits: Lines 3060–3100 print the cents portion as raw digit characters with a
/100 DOLLARS***suffix rather than spelling them out, inconsistent with the dollars portion which is fully spelled. This appears to be intentional (cheque-writing style notation for cents).
Content
Source Code
10 LET X=VAL N$(A)+18*(X1=1)
15 DIM G$(9)
20 FOR J=1 TO 9
30 IF D$(X,J)=" " THEN GOTO 60
40 LET G$(J)=D$(X,J)
50 NEXT J
60 LET H=J-1
70 RETURN
500 CLS
505 SLOW
510 PRINT AT 18,0;"INPUT:"
520 PRINT AT 19,0;"D=DAY OF WEEK"
530 PRINT AT 20,0;"N=NUMBER OF DAYS BETWEEN"
540 PRINT AT 21,0;"T=DOLLARS/CENTS TO TEXT"
550 IF INKEY$="D" THEN GOTO 1000
560 IF INKEY$="N" THEN GOTO 1500
570 IF INKEY$="T" THEN GOTO 2000
580 GOTO 510
1000 DIM B$(24)
1010 LET B$=" MONTUEWEDTHUFRISATSUN"
1020 CLS
1022 PRINT "INPUT MONTH NUMBER=";
1024 INPUT M
1026 PRINT M
1030 PRINT "INPUT DAY NUMBER=";
1040 INPUT D
1050 PRINT D
1090 PRINT "INPUT YEAR (XXXX)=";
1100 INPUT Y
1110 PRINT Y
1120 LET Q=Y-(M<3)
1130 LET K=Q/100
1140 LET T=M+12*(M<3)
1150 LET R=INT (13*(T+1)/5)+INT (5*Q/4)-INT (K)+INT (K/4)+D+5
1160 LET R=R-(7*INT (R/7))+1
1165 PRINT
1170 PRINT M;"/";D;"/";Y;"=";B$((R*3) TO (R*3)+2)
1180 GOTO 505
1500 CLS
1510 PRINT "INPUT MONTH 1 NUMBER=";
1520 INPUT M1
1525 PRINT M1
1530 PRINT "INPUT DAY 1 NUMBER=";
1540 INPUT D1
1550 PRINT D1
1560 PRINT "INPUT YEAR 1 (XXXX)=";
1570 INPUT Y1
1575 PRINT Y1
1577 PRINT "INPUT MONTH 2 NUMBER=";
1578 INPUT M2
1580 PRINT M2
1590 PRINT "INPUT DAY 2 NUMBER=";
1600 INPUT D2
1605 PRINT D2
1610 PRINT "INPUT YEAR 2 (XXXX)=";
1620 INPUT Y2
1625 PRINT Y2
1630 LET Y=365*(Y2-Y1)
1640 LET M=31*(M2-M1)
1650 LET D=D2-D1
1660 LET X=INT ((.4*M2+2.3)*(M2>2))-INT ((.4*M1+2.3)*(M1>2))
1670 LET Z=INT ((Y2-(M2<=2))/4)-INT ((Y1-(M1<=2))/4)
1675 PRINT
1680 PRINT "DAYS BETWEEN=";ABS (Y+M+D-X+Z)
1690 GOTO 505
2000 CLS
2010 DIM D$(27,9)
2021 LET D$(1)="ONE"
2022 LET D$(2)="TWO"
2023 LET D$(3)="THREE"
2024 LET D$(4)="FOUR"
2025 LET D$(5)="FIVE"
2026 LET D$(6)="SIX"
2027 LET D$(7)="SEVEN"
2028 LET D$(8)="EIGHT"
2029 LET D$(9)="NINE"
2030 LET D$(10)="TEN"
2031 LET D$(11)="ELEVEN"
2032 LET D$(12)="TWELVE"
2033 LET D$(13)="THIRTEEN"
2034 LET D$(14)="FOURTEEN"
2035 LET D$(15)="FIFTHTEEN"
2036 LET D$(16)="SIXTEEN"
2037 LET D$(17)="SEVENTEEN"
2038 LET D$(18)="EIGHTEEN"
2039 LET D$(19)="NINETEEN"
2040 LET D$(20)="TWENTY"
2042 LET D$(21)="THIRTY"
2044 LET D$(22)="FORTY"
2050 LET D$(23)="FIFTY"
2060 LET D$(24)="SIXTY"
2070 LET D$(25)="SEVENTY"
2080 LET D$(26)="EIGHTY"
2090 LET D$(27)="NINETY"
2500 PRINT "ENTER AMOUNT=";
2510 INPUT N
2515 PRINT N
2520 IF N<10000 THEN GOTO 2550
2530 PRINT "NUMBER TOO LARGE"
2540 GOTO 2500
2550 IF N>=.01 THEN GOTO 2580
2560 PRINT "NO NEGATIVE NUMBERS"
2570 GOTO 2500
2580 LET N1=N
2590 LET N=INT (N*100+.001)
2600 LET N$=STR$ (N)
2605 LET L=LEN N$
2610 PRINT
2620 PRINT "***";
2630 IF N>=100 THEN GOTO 2700
2631 LET A=1
2632 IF L=1 THEN GOTO 2648
2634 IF VAL N$(1)=0 THEN GOTO 2646
2636 IF VAL N$(1)<2 THEN GOTO 2654
2638 LET X1=1
2640 GOSUB 10
2642 PRINT G$(1 TO H);
2644 LET X1=0
2646 LET A=A+1
2648 GOSUB 10
2650 PRINT G$(1 TO H);
2652 GOTO 2680
2654 LET X=VAL N$(1 TO 2)
2656 GOSUB 15
2658 GOTO 2650
2680 PRINT " CENTS AND NO DOLLARS***"
2690 GOTO 505
2700 LET A=1
2705 LET X1=0
2710 GOTO 2620+(7-L)*100
2720 GOSUB 10
2730 PRINT G$(1 TO H);" THOUSAND ";
2740 LET A=A+1
2750 IF VAL N$(A)=0 THEN GOTO 2840
2820 GOSUB 10
2830 PRINT G$(1 TO H);" HUNDRED ";
2840 LET A=A+1
2850 IF VAL N$(A)=0 THEN GOTO 2950
2920 LET X1=1
2925 IF VAL N$(A)=1 THEN GOTO 3200
2930 GOSUB 10
2940 PRINT G$(1 TO H);" AND ";
2950 LET A=A+1
2960 LET X1=0
2990 IF VAL N$(A)=0 THEN GOTO 3040
3020 GOSUB 10
3030 PRINT G$(1 TO H);" AND ";
3040 LET A=A+1
3060 PRINT VAL N$(A);
3070 LET A=A+1
3075 IF VAL N$(A)=0 THEN GOTO 3100
3080 PRINT VAL N$(A);
3090 GOTO 3110
3100 PRINT "0";
3110 PRINT "/100 DOLLARS***"
3120 GOTO 505
3200 LET X=VAL N$(A TO A+1)
3210 GOSUB 15
3220 PRINT G$(1 TO H);" AND ";
3230 LET X1=0
3240 LET A=A+1
3250 GOTO 3040
9550 SAVE "UT%Y"
9555 LET A$="UTY"
9560 FAST
9565 LET N=0
9570 FOR L=5 TO 19 STEP 7
9575 LET N=N+1
9580 LET C=CODE A$(N)
9585 FOR H=0 TO 7
9590 LET P=PEEK (7680+C*8+H)
9595 LET V=128
9600 FOR G=0 TO 7
9605 IF P<V THEN GOTO 9625
9610 PRINT AT H+3,G+L;"% "
9615 LET P=P-V
9620 GOTO 9630
9625 PRINT AT H+3,G+L;"@@"
9630 LET V=V/2
9635 NEXT G
9640 NEXT H
9645 NEXT L
9650 PRINT " UTILITY PROGRAMS COPYRIGHT UAS"
9655 PRINT "BOX 612 HADDONFIELD, N.J. 08033"
9660 PAUSE 200
9670 CLS
9680 GOTO 500
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


