This program generates a month-by-month calendar for any year from 1900 onward, printing each month in a bordered grid layout with day-of-week columns. It detects leap years by checking divisibility by 4 and adjusts February’s day count accordingly. A 28-year cycle lookup string at line 30 (`R$`) is used to determine the starting day of the week for January 1st of the requested year, avoiding complex day-of-week arithmetic. Users can pre-register up to 10 special dates, which are then displayed in inverse video within the calendar grid. Each monthly page can be sent to a printer via COPY and LPRINT, either automatically or on demand with the “z” key.
Program Structure
The program is organized into several logical blocks:
- Initialization (lines 1–42): Sets up data strings and dimensions arrays.
- User input (lines 100–187): Prompts for year and optional special dates.
- Leap year and start-day calculation (lines 200–280): Computes calendar parameters.
- Calendar printing loop (lines 300–570): Iterates over 12 months, printing each in a framed grid.
- Subroutines: Line 600 (new row), line 700 (print copy), line 800 (highlight special dates).
- Save/run (lines 900–901).
Data Encoding
Line 20 encodes all 12 month names and their day counts in a single string A$. Each month occupies 11 characters: a 9-character padded name followed by a 2-digit day count. The pointer C steps through this string in increments of 11 (line 560), and Z similarly tracks the position of each month’s day count (line 565). This avoids the need for separate arrays for month names and lengths.
Line 30 encodes the 28-year day-of-week cycle in the string R$. The expression at line 270 computes the offset of the requested year within its 28-year cycle and reads one character from R$ to obtain the starting weekday offset A3. The screen column for day 1 is then set at line 280 as X = 3 + (A3 * 4), placing it in the correct day column.
Special Dates Feature
Up to 10 special dates are accepted as input in the format DD.MM (e.g., 23.10). Line 153 extracts the month using VAL B$(4 TO) and line 154 extracts the day using VAL B$(TO 2), storing them in arrays V() and W() respectively. During calendar printing, lines 441–442 scan all 10 slots to check if the current day and month match any stored special date; a match branches to the highlight subroutine at line 800.
Inverse Video Highlighting (Subroutine 800)
Special dates are displayed in inverse video by converting each digit character to its inverse equivalent. Line 802 computes Z1 = CODE F$(1) + 128, which maps an ASCII digit to the corresponding inverse digit character code, then prints it with CHR$ Z1. Two-digit day numbers are handled at lines 810–812 in a similar fashion. However, there is a bug at line 812: CHR$ Z2 is used instead of CHR$ Z1; the variable Z2 is never assigned, so the second digit of two-digit special dates will print incorrectly or cause an error.
Leap Year Detection
Line 220 computes B1 = INT(A1 / 4) and line 230 checks whether B1 * 4 = A1. If true, D is set to 366 (line 230) and character 22 of A$ — the day count for February — is patched to "9" at line 240, making it read 29. This direct string mutation is a compact technique that avoids a separate leap-year variable for February’s length. Note that this simple divisibility-by-4 test does not account for century years (e.g., 1900), so 1900 would be incorrectly treated as a leap year.
Grid Layout and Row Wrapping
The calendar grid uses AT positioning. Days are printed at row N, column X, with X advancing by 4 for each day. When X reaches 31 (line 440), subroutine 600 is called to increment N by 2 and reset X to 3, starting a new row. The border asterisks for each month are drawn both in the header block and via a loop at lines 420–422 that places * characters in the left and right columns of rows 7 through 19.
Day-Count Running Total
Line 340 accumulates a running total of days elapsed across months in D1, using VAL A$(Z TO Z+1) to read the current month’s day count from the encoded string. This value is printed at line 351 alongside the remaining days in the year (D - D1), giving a day-of-year display within the month header.
Print and Copy Options
After each month is displayed, a 100-frame PAUSE occurs (line 503). Pressing “z” during this pause triggers subroutine 700 (COPY + two LPRINT blank lines) for a screen dump of that month. If the user selected “Y” for automatic copy at the input stage, line 545 calls the same subroutine unconditionally for every month. However, lines 182–187 read INKEY$ in a tight loop but never assign the result to Q$; Q$ is initialized to "" at line 101 and is never updated, meaning the condition at line 187 (Q$="Y" OR Q$="N") is immediately false and line 545 will never trigger the copy subroutine.
Notable Bugs and Anomalies
- Line 137: The example text reads
"EG 23.10=23 JAN."but 10 is October, not January; the example is misleading. - Line 182–187:
Q$is never assigned fromINKEY$, so the copy-on-demand feature is entirely non-functional. - Line 812:
CHR$ Z2references an uninitialized variableZ2; should beCHR$ Z1. - Leap year rule: Only divisibility by 4 is checked; century-year exceptions are not handled.
- Line 270:
VAL R$((A1-A2)+1)— ifA1 = A2, the index is 1, which is valid; but this relies onR$being exactly 28 characters, covering the full cycle correctly. - Line 580: After all 12 months, the program jumps back to line 20, re-running from the top but without resetting
D1,C, orZ, which would corrupt a second run. A fullRUNor reset of those variables would be needed for correctness.
Content
Source Code
1 REM CALENDAR PROGRAM
20 LET A$=" JANUARY 31FEBRUARY 28 MARCH 31 APRIL 30 MAY 31 JUNE 30 JULY 31 AUGUST 31SEPTEMBER30 OCTOBER 31 NOVEMBER30 DECEMBER31"
30 LET R$="0234501235601345612346012456"
40 LET Z=10
41 DIM W(10)
42 DIM V(10)
100 CLS
101 LET Q$=""
102 LET C=1
103 LET D=365
104 LET D1=0
110 PRINT TAB 10;"\{20}\{1}CALENDAR\{20}\{0}"
111 PRINT
115 PRINT "ENTER YEAR REQUIRED-1900 ONWARDS"
116 INPUT A1
117 IF A1<1900 THEN GO TO 116
118 PRINT
120 PRINT TAB 14;A1
121 PRINT TAB 14;"\{20}\{1}\{20}\{0}\{20}\{0}\{20}\{0}\..\..\..\.."
122 PRINT
130 PRINT "ENTER SPECIAL DATES(Y OR N)"
132 IF INKEY$="" THEN GO TO 132
133 IF INKEY$="N" THEN GO TO 181
134 CLS
135 PRINT "ENTER DATE THEN MONTH"
136 PRINT
137 PRINT "EG 23.10=23 JAN."
142 PRINT
143 PRINT "(MAX 10)"
144 PRINT
145 PRINT "DAY","MONTH"
148 PRINT
149 LET C1=11
150 FOR Y=1 TO 10
151 INPUT B$
152 IF B$="0" THEN GO TO 181
153 LET V(Y)= VAL B$(4 TO )
154 LET W(Y)= VAL B$( TO 2)
158 LET C1=V(Y)*11-10
160 PRINT W(Y),A$(C1 TO C1+8)
180 NEXT Y
181 PRINT
182 PRINT "\{20}\{1}COPY REQUIRED\{20}\{0}(Y/N)"
184 IF INKEY$="" THEN GO TO 184
187 IF Q$="Y" OR Q$="N" THEN GO TO 200
200 CLS
210 REM TO TEST FOR LEAP YEAR
220 LET B1= INT (A1/4)
230 IF B1*4=A1 THEN LET D=366
240 IF D=366 THEN LET A$(22)="9"
250 REM TO CALCULATE START DAY
260 LET A2=(INT ((A1-1900)/28))*28+1900
270 LET A3= VAL R$((A1-A2)+1)
280 LET X=3+(A3*4)
300 REM TO PRINT CALENDAR
310 FOR B=1 TO 12
311 LET N=8
315 PRINT "********************************"
330 PRINT "*"; TAB 9;A$(C TO C+8);A1; TAB 31;"*"
340 LET D1=D1+ VAL A$(Z TO Z+1)
351 PRINT "* ";D1; TAB 5;"+++"; TAB 9;"\..\..\..\..\..\..\..\..\..\..\..\..\.."; TAB 23;"+++"; TAB 27;D-D1; TAB 31;"*";
360 PRINT "*"; TAB 31;"*"
370 PRINT "* SUN MON TUE WED THU FRI SAT *"
400 PRINT "*"; TAB 31;"*"
410 PRINT "********************************"
420 FOR Y=7 TO 19
421 PRINT AT Y,0;"*"; AT Y,31;"*"
422 NEXT Y
430 FOR Y=1 TO VAL A$(Z TO Z+1)
440 IF X=31 THEN GO SUB 600
441 FOR R=1 TO 10
442 IF V(R)=B AND W(R)=Y THEN GO TO 800
443 NEXT R
450 PRINT AT N,X;Y
460 LET X=X+4
470 NEXT Y
500 IF D=365 THEN PRINT AT 20,0;"********************************"
501 IF D=366 THEN PRINT AT 20,0;"***********\{20}\{1}LEAP YEAR\{20}\{0}************"
502 PRINT "********************************"
503 PAUSE 100
504 IF INKEY$="z" THEN GO SUB 700
530 FOR P=1 TO 125
540 NEXT P
545 IF Q$="Y" THEN GO SUB 700
550 CLS
560 LET C=C+11
565 LET Z=Z+11
570 NEXT B
580 GO TO 20
600 LET N=N+2
620 LET X=3
630 RETURN
700 COPY
710 LPRINT
720 LPRINT
730 RETURN
800 LET F$= STR$ W(R)
802 LET Z1= CODE F$(1)+128
804 PRINT AT N,X; CHR$ Z1;
806 IF LEN F$=2 THEN GO TO 810
808 GO TO 460
810 LET Z1= CODE F$(2)+128
812 PRINT CHR$ Z2;
820 GO TO 460
900 SAVE "CALENDAR"
901 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
