This program generates and displays a graphical calendar for any month between 1583 and 9999 AD, using the Zeller’s Congruence algorithm to calculate the day of the week for any given month and year, including correct leap year handling. The calendar grid is drawn using PLOT and DRAW commands to produce ruled lines, with day numbers positioned via PRINT AT within the cells. An introductory menu subroutine at line 410 allows the user to select custom LPRINT headings—such as “Month of Birth” or “Year of Birth”—or enter up to 32 characters of freeform text, optionally centered, before printing. The “Y” mode loops through all 12 months automatically, sending each to the printer via COPY and LPRINT, while “M” mode prints a single month. POKE 23609 adjusts the printer’s line feed delay and POKE 23658 enables CAPS LOCK at startup.
Program Structure
The program is organized into two main sections: the main calendar logic (lines 10–400) and a menu/heading subroutine (lines 410–790). Execution begins with hardware POKEs and a call to the subroutine at line 410, which presents the heading-selection menu. On return, the main loop at line 80 accepts a mode character (T$: "Y", "M", or "D"), then collects month and year inputs before computing and rendering the calendar grid.
Day-of-Week Calculation (Zeller’s Congruence)
Lines 140–170 implement a variant of Zeller’s Congruence. January and February are treated as months 13 and 14 of the previous year by adjusting E and C. The formula at line 160 computes a raw day index using century, year, and month terms, and line 170 reduces it modulo 7, adding 1 to produce a 1-based Sunday-first column offset stored in D.
E = B - (A < 3)— adjusts year for Jan/FebC = A + 12*(A < 3) + 1— adjusts month indexD = INT(E/400) - INT(E/100) + INT(1.25*E) + INT(2.6*C)— raw Zeller valueD = D - 7*INT(D/7) + 1— maps to 1–7 (Sun–Sat)
Leap Year Handling
Line 210 handles February’s day count. It starts with 28, then tests the compound leap-year rule: divisible by 4 AND NOT by 100, OR divisible by 400. The Boolean arithmetic uses multiplication for AND and addition for OR, a standard Sinclair BASIC idiom.
Calendar Grid Rendering
Rather than using character graphics, the grid is drawn with PLOT/DRAW commands (lines 240–280). A horizontal header separator is drawn at line 250, six horizontal row dividers follow in the loop at line 270, and six vertical column dividers are drawn in the loop at line 280. Day numbers are sourced from the pre-built string Z$ at line 70, which contains all 31 two-character day-number substrings concatenated. Line 320 builds C$ by padding with spaces proportional to the starting day offset (2*D-2 spaces), then appending Z$. The print loop at lines 330–350 walks through C$ two characters at a time, advancing column by 4 and row by 2 at each week boundary.
Operating Modes
| T$ value | Behavior |
|---|---|
"Y" | Loops A from 1 to 12, prints each month to screen via COPY and adds LPRINT year banner |
"M" | Displays a single month and sends it to the printer via COPY |
"D" | Display-only; no printing |
Heading Menu Subroutine (Lines 410–790)
The subroutine presents labeled options and polls INKEY$ in a tight loop (lines 520–580). Each key triggers an LPRINT heading or a sub-flow. The "P" option (lines 620–760) prompts for up to 32 characters of custom text, optionally centering it by computing L = (32 - LEN Z$) / 2 and using LPRINT TAB INT L. The "S" option at line 790 saves the program to tape.
Notable Techniques and Idioms
POKE 23609,20sets the printer LPRINT delay;POKE 23658,8enables CAPS LOCK — both are system variable manipulations common in Sinclair BASIC utilities.- Boolean expressions like
(A<3)evaluate to 1 or 0 and are used directly in arithmetic, avoiding IF statements in the Zeller calculation. VAL "number"is not used here; GO TO targets are literal line numbers.- The month-name array is a
DIM M$(12,9)fixed-width string matrix loaded from DATA at line 60, padded automatically to 9 characters. - The program title contains the misspelling “CALENDER” (should be “CALENDAR”), which appears consistently in both the display strings and the SAVE filename
"Calende". - There is a case inconsistency:
T$is set by INPUT at line 80 but compared as lowercaset$at lines 130 and 220 — on Spectrum-family systems these refer to the same variable, as variable names are case-insensitive in the tokenizer.
Potential Anomalies
- Line 50 prints a heading but is executed before month/year inputs; it is immediately overwritten by
CLSat line 120, so it only flashes briefly. - Line 590 is unreachable from the normal INKEY$ polling loop (lines 520–580 skip from 580 back to 520), making the extra LPRINT calls at line 590 dead code.
- The
LPRINTyear banner at line 290 only fires whenT$="Y"andA=1, so yearly print runs get a year header only on the January page. - After a full run, line 380 asks to repeat and branches to
RUN, which re-initializes all variables and re-enters the heading menu, rather than branching to line 80 to skip the menu.
Source Code
10 POKE 23609,20:POKE 23658,8
20 REM \*1984 BY I. AUERSBACHER: MODIFIED BY ALGIS E. GEDRIS
30 BORDER 5:BRIGHT 1:CLS :DIM M$(12,9)
40 BEEP .1,30:GO SUB 410:CLS
50 PRINT ' PAPER 6;" *** CALENDER(1583-9999 AD) *** "
60 RESTORE :FOR Z=1 TO 12:READ M$(Z):NEXT Z
70 LET Z$=" 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031":BEEP .05,20
80 INPUT "Y-YEAR, M-PRINT MO, D-DISPLAY:"; LINE T$
90 IF T$="Y" THEN GO TO 110
100 INPUT "MONTH (1-12):";A:IF A<1 OR A>12 THEN BEEP .5,-20:GO TO 100
110 INPUT "YEAR (YYYY):";B:IF B<1582 OR B>9999 THEN BEEP .5,-20:GO TO 110
120 CLS
130 IF t$="Y" THEN FOR X=1 TO 12:LET A=X
140 LET B= INT B:LET E=B-(A<3)
150 LET C=A+12*(A<3)+1
160 LET D= INT (E/400)- INT (E/100)+ INT (1.25*E)+ INT (2.6*C)
170 LET D=D-(7* INT (D/7))+1
180 IF (A=4)+(A=6)+(A=9)+(A=11) THEN LET C=30
190 IF (A=1)+(A=3)+(A=5)+(A=7)+(A=8)+(A=10)+(A=12) THEN LET C=31
200 IF A <>2 THEN GO TO 220
210 LET C=28:IF (B/4= INT (B/4))*(B/100 <> INT (B/100))+(B/400= INT (B/400)) THEN LET C=29
220 IF t$="Y" THEN CLS
230 PRINT " *****************************"
240 PLOT 10,132:DRAW 225,0:DRAW 0,-112:DRAW -225,0:DRAW 0,112
250 PLOT 10,118:DRAW 225,0
260 PRINT AT 6,2;"SU MO TU WE TH FR SA":PRINT AT 3,0;
270 FOR Z=1 TO 6:PLOT 10,116-(Z-1)*16:DRAW 225,0:NEXT Z
280 FOR Z=1 TO 6:PLOT 40+(Z-1)*32,132:DRAW 0,-112:NEXT Z
290 IF T$="Y" AND A=1 THEN LPRINT " \{20}\{1}>>>>>>>>>>>\{20}\{0} ";B;" \{20}\{1}<<<<<<<<<<<<\{20}\{0}":LPRINT :LPRINT
300 IF T$="Y" THEN PRINT TAB 12;M$(A):GO TO 320
310 PRINT TAB 8;M$(A); TAB 18;B
320 DIM C$(74):LET J=8:LET K=2:LET C$=C$( TO 2*D-2)+Z$
330 FOR Z=1 TO C+D-1:PRINT AT J,K;C$(2*Z-1 TO 2*Z):LET K=K+4
340 IF (Z/7= INT (Z/7)) THEN LET J=J+2:LET K=2
350 NEXT Z
360 IF T$="Y" THEN COPY :NEXT X
370 IF T$="M" THEN COPY
380 INPUT "ANOTHER YEAR OR MONTH? (Y/N):";Y$:IF Y$ <>"N" THEN RUN
390 DATA "JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"
400 GO TO 10
410 CLS
420 PRINT AT 5,0; PAPER 6;" *** CALENDER(1583-9999 AD) *** "
430 PRINT ''
440 PRINT " SPECIAL CALENDER HEADINGS"; OVER 1; AT 8,0;" _________________________"
450 PRINT '
460 PRINT " SPECIAL \{20}\{1}P\{20}\{0}RINTING ...... P"
470 PRINT " THIS \{20}\{1}C\{20}\{0}ALENDAR IS FOR:...C"
480 PRINT " \{20}\{1}M\{20}\{0}ONTH OF BIRTH .........M"
490 PRINT " \{20}\{1}Y\{20}\{0}EAR OF BIRTH...........Y"
500 PRINT " \{20}\{1}N\{20}\{0}O HEADING..............N"
510 PRINT " \{20}\{1}S\{20}\{0}AVE....................S"
520 IF INKEY$="S" THEN GO TO 790
530 IF INKEY$="M" THEN GO TO 600
540 IF INKEY$="Y" THEN GO TO 610
550 IF INKEY$="P" THEN GO TO 620
560 IF INKEY$="N" THEN RETURN
570 IF INKEY$="C" THEN GO TO 770
580 GO TO 520
590 LPRINT :LPRINT :GO TO 520
600 LPRINT :LPRINT " MONTH of BIRTH":LPRINT :GO TO 520
610 LPRINT :LPRINT " YEAR of BIRTH":LPRINT :GO TO 520
620 CLS :PRINT :PRINT :PRINT "DO YOU WANT THE INPUT CENTERED?"
630 IF INKEY$="Y" THEN GO TO 660
640 IF INKEY$="N" THEN GO TO 720
650 GO TO 630
660 PRINT :PRINT " ENTER UP TO 32 CHARACTERS."
670 INPUT Z$:IF LEN Z$>32 THEN GO TO 670
680 LET L=(32- LEN Z$)/2
690 LPRINT TAB INT L;Z$
700 LPRINT ''
710 GO TO 410
720 PRINT :PRINT "Enter up to 32 characters."
730 INPUT Z$:IF LEN Z$>32 THEN GO TO 730
740 LPRINT Z$
750 LPRINT ''
760 GO TO 410
770 LPRINT :LPRINT " THIS CALENDAR IS FOR:"
780 LPRINT :GO TO 410
790 SAVE "Calende" LINE 1:BEEP .5,20:REM PRINT INK 2;AT 20,4;"\{20}\{1} REWIND THE TAPE \{20}\{0}";AT 21,2;"\{20}\{1}*** AND PLAY TO VERIFY ***\{20}\{0}":VERIFY "":GO TO 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

