This program displays a monthly calendar for any month and year from 1752 onwards, using Zeller’s congruence to calculate the starting day of the week. The month name is matched against a packed three-character lookup string (lines 30–80), and the day-of-week calculation at lines 250–310 implements the classic Zeller algorithm with century correction. Line 220 uses PEEK 23688 (the current print column register) to detect when a row is full and break to a new line, while line 230 also PEEKs 23688 to handle automatic line wrapping. The calendar layout is printed using AT coordinates and column-position arithmetic derived from the calculated starting day Z.
Program Analysis
Program Structure
The program is divided into three logical phases:
- Initialisation (lines 20–30): Sets display attributes and loads the packed lookup string
A$containing month abbreviations followed by day abbreviations. - Input and validation (lines 40–100): Prompts for a month name (matched against the first 36 characters of
A$), then a year (must be ≥ 1752, the adoption of the Gregorian calendar in Britain). - Calendar display (lines 110–310): Prints the heading, day labels, calculates the starting column, and iterates through days 1–31, with a subroutine at lines 250–310 implementing Zeller’s congruence.
Packed Lookup String
A$ at line 30 encodes all month and day abbreviations as a single concatenated string. Characters 1–36 hold the 12 three-character month codes (e.g. JAN, FEB, …), with an unusual leading CD sentinel that shifts the useful month data to positions 3–38. Characters 39 onwards hold the seven day abbreviations (SUN MON TUE WED THU FRI SAT) which are printed directly at line 130 using the slice A$(39 TO ).
The month match loop (lines 50–70) compares the first three characters of the user’s input against A$(3*M TO 3*M+2). Because A$ starts with CD, when M=1 the slice is A$(3 TO 5) = JAN, correctly offsetting past the sentinel.
Zeller’s Congruence (lines 250–310)
The subroutine calculates the day of the week for the first of the month using a standard form of Zeller’s congruence. January and February are treated as months 13 and 14 of the previous year, achieved by the variable X which adds 12 to M and decrements L (the adjusted year) when the month is 1 or 2. The century correction uses INT(P/4)-P where P=INT(L/100).
| Variable | Role |
|---|---|
X | Month adjustment (adds 12 for Jan/Feb) |
L | Adjusted year (decremented for Jan/Feb) |
P | Century value INT(L/100) |
Z | Day of week result (0=Sunday) |
The subroutine is called twice: once at line 140 to find the starting day for positioning the cursor, and again at line 170 after incrementing M at line 160 to find the starting day of the next month — used by the end-of-month detection at line 220.
Column-Position Tracking with PEEK 23688
System variable 23688 (S_POSN column, the current print column) is PEEKed twice for layout control. At line 220, the condition 30-PEEK 23688=Z*4 compares the expected column for the next month’s first day against the current position; when they match after day 27 or later, the loop branches to line 500 (not shown in the listing) to terminate the day sequence cleanly. At line 230, PEEK 23688=2 detects when the cursor has wrapped to near the left edge, causing a newline and indent to start a new calendar row.
Key BASIC Idioms
- Boolean arithmetic:
LET Z=Z+7*(Z=0)at line 180 adds 7 whenZis zero, replacing a conditional branch. Similarly,LET X=(M=1)+(M=2)andLET L=Y-Xuse boolean expressions (evaluating to 1 or 0) for compact Zeller adjustments. - Conditional string printing:
PRINT " " AND I<10at line 210 prints a leading space for single-digit day numbers, aligning columns without an IF statement. - String slicing for lookup: Month matching uses computed string slices rather than a DATA/READ array, saving memory.
Anomalies and Notes
- Line numbers 190 and 320 onwards are absent from the listing; this may indicate renumbering was applied, or that lines were deleted. The jump to line 500 at line 220 targets code not shown in the listing.
- The 31-day loop (lines 200–240) does not account for months with fewer than 28, 29, 30, or 31 days by itself — the early-exit condition at line 220 (comparing print column to next month’s start day) handles this implicitly for months of varying length, though correctness depends on the behaviour of code at line 500.
- The
BORDER 5: PAPER 6at line 20 sets cyan paper and magenta border, withPOKE 23658,8enabling caps lock.
Content
Source Code
20 POKE 23658,8: BORDER 5: PAPER 6
30 LET A$="CDJANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDECSUN MON TUE WED THU FRI SAT"
40 INPUT "ENTER MONTH ";B$
50 FOR M=1 TO 12
60 IF B$(1 TO 3)=A$(3*M TO 3*M+2) THEN GO TO 90
70 NEXT M
80 GO TO 40
90 INPUT "ENTER YEAR ";Y
100 IF Y<1752 THEN PRINT "YEAR CANNOT BE LESS THAN 1752": GO TO 90
110 CLS
120 PRINT AT 3,10;B$;" ";Y
130 PRINT AT 6,3;A$(39 TO )
140 GO SUB 250
150 PRINT AT 8,Z*4+3;
160 LET M=M+1
170 GO SUB 250
180 LET Z=Z+7*(Z=0)
200 FOR I=1 TO 31
210 PRINT " " AND I<10;I;" ";
220 IF I>27 AND 30-PEEK 23688=Z*4 THEN GO TO 500
230 IF PEEK 23688=2 THEN PRINT ''" ";
240 NEXT I
250 LET X=(M=1)+(M=2)
260 LET L=Y-X
270 LET X=M+X*12
280 LET P=INT (L/100)
290 LET Z=INT (13*(X+1)/5)+INT (5*L/4)+INT (P/4)-P
300 LET Z=Z-7*INT (Z/7)
310 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
