--- title: "Calendar Maker" id: 57130 type: "computer_media" slug: "calendar-maker" url: "http://localhost/computer_media/calendar-maker/" markdown_url: "http://localhost/computer_media/calendar-maker.md" published_at: "2024-10-03T23:59:33+00:00" modified_at: "2026-03-30T21:19:41+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/79_Cal.png" excerpt: "A Gregorian calendar program that uses a clever packed month-name lookup string and a Zeller-style formula to calculate and display any month from 1752 onward." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Home" slug: "home" taxonomy: "genre" url: "http://localhost/type/home/" media_contents: - id: 56733 title: "Timex Sinclair Public Domain Library Tape 1002" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1002/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/79_Cal.png" media_type_tags: "Home" --- # Calendar Maker This program displays a monthly calendar for any month and year from 1752 onward, the year Britain adopted the Gregorian calendar. The month is entered as a full name (matched by comparing the first three characters against a packed lookup string), and the year of adoption is enforced with a lower-bound check at line 110. Day-of-week calculation uses the Zeller-style formula at lines 300–320, adjusting for January and February falling in the previous year’s cycle. Display positioning is handled entirely with PRINT AT and column-counting via PEEK 16441 (the ZX81’s system variable holding the current print column), which is also used at line 230 to detect when a date would overflow the last row and STOP the output. *** ## Program Analysis ### Program Structure The program is divided into three logical phases: 1. **Input and validation (lines 15–110):** prompts for month name and year, validates both. 2. **Display setup (lines 120–190):** clears screen, prints heading and day-name row, and determines the starting column for day 1. 3. **Date printing loop (lines 200–250):** iterates from 1 to 31, printing each date with spacing, wrapping to new lines as needed. The subroutine at lines 260–330 implements the day-of-week calculation and is called twice: once for the current month (to find the starting weekday) and once after incrementing `M` (line 170), though the second call’s result is only used for the starting-column print at line 160 — a subtle ordering quirk discussed below. ### Month Name Lookup Line 20 defines `A$` as a single packed string containing all 12 three-character month abbreviations followed by seven three-character day abbreviations: ``` "CDJANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDECSUN MON TUE WED THU FRI SAT" ``` The first two characters `CD` are padding so that the 1-based month index `M` maps correctly: month `M` occupies characters `A$(3*M TO 3*M+2)`. The user’s input is matched by comparing `B$(TO 3)` (first three characters) against each month slice in the FOR loop at lines 50–70. If no match is found, control returns to line 40 to re-prompt. ### Day-of-Week Subroutine (Lines 260–330) The subroutine implements a variant of Zeller’s congruence. January and February are treated as months 13 and 14 of the previous year, achieved by setting `X=1` at line 270 when `M=1 OR M=2`. The adjusted year is stored in `L` and the adjusted month in `X`. The formula at line 310 is: `Z = INT(13*(X+1)/5) + INT(5*L/4) + INT(P/4) - P` where `P = INT(L/100)` is the century term. The result is then reduced modulo 7 at line 320. The return value in `Z` represents the day of week (0=Sunday through 6=Saturday, matching the day-name order in `A$`). ### Display Mechanics and PEEK 16441 The program uses `PEEK 16441` throughout to read the ZX81 system variable `S_POSN_X` (the current print column). This is used in two ways: - At line 240: `IF PEEK 16441=5 THEN PRINT ,,,` — when the column counter reaches 5 (i.e., after the last day of the week column), three commas advance to a new line, implementing the weekly row wrap. - At line 230: `IF I>27 AND 33-PEEK 16441=Z*4 THEN STOP` — for dates beyond the 27th, this checks whether the column position matches the expected position for day 1 of the next month (i.e., the calendar has wrapped back to the starting column), halting output to avoid printing beyond the actual month length. The starting column for day 1 is set by `PRINT AT 8,Z*4;` at line 160, positioning the cursor 4 characters per weekday column (each date occupies 4 characters as printed by lines 210–220). ### Control Flow Anomaly: Subroutine Call Order There is a subtle ordering issue in the display setup. The subroutine is first called at line 150 (`GOSUB 260`) to compute `Z` for the current month, but then `M` is incremented at line 170 before the result is used at line 160’s `PRINT AT 8,Z*4`. The second `GOSUB 260` at line 180 then overwrites `Z` with the next month’s starting day — which is not actually used. In practice the `PRINT AT` at line 160 executes *after* the `GOSUB` at 150 and before the increment at 170, so `Z` correctly holds the current month’s starting weekday at that point. The second subroutine call at line 180 is therefore redundant and its result in `Z` is immediately corrected at line 190 (`IF Z=0 THEN LET Z=7`), which adjusts a Sunday result — but since the second call’s `Z` is never used for display, lines 180–190 appear to be vestigial or left from an earlier version. ### Year Validation Line 110 rejects years before 1752, the year Britain and its colonies adopted the Gregorian calendar, making the Zeller formula valid. This is a historically informed guard that prevents meaningless results for earlier dates. ### Key Variables | Variable | Purpose | | --- | --- | | `A$` | Packed month and day-name lookup string | | `B$` | User-entered month name | | `M` | Month number (1–12) | | `Y` | Year (≥1752) | | `Z` | Day-of-week result from subroutine (0–6) | | `L` | Adjusted year for Zeller formula | | `X` | Adjusted month / century scratch variable | | `P` | Century term INT(L/100) | ## Source Code ``` 10 SAVE "1007%9" 15 PRINT "**** CALENDAR ****" 20 LET A$="CDJANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDECSUN MON TUE WED THU FRI SAT" 30 PRINT "ENTER MONTH IE JANUARY" 40 INPUT B$ 50 FOR M=1 TO 12 60 IF B$( TO 3)=A$(3*M TO 3*M+2) THEN GOTO 90 70 NEXT M 80 GOTO 40 90 PRINT "ENTER YEAR IE 1984" 100 INPUT Y 110 IF Y<1752 THEN GOTO 100 120 CLS 130 PRINT AT 3,8;B$;" ";Y 140 PRINT AT 6,0;A$(39 TO ) 150 GOSUB 260 160 PRINT AT 8,Z*4; 170 LET M=M+1 180 GOSUB 260 190 IF Z=0 THEN LET Z=7 200 FOR I=1 TO 31 210 PRINT " ";I;" "; 220 IF I<10 THEN PRINT " "; 230 IF I>27 AND 33-PEEK 16441=Z*4 THEN STOP 240 IF PEEK 16441=5 THEN PRINT ,,, 250 NEXT I 260 LET X=0 270 IF M=1 OR M=2 THEN LET X=1 280 LET L=Y-X 290 LET X=M+X*12 300 LET P=INT (L/100) 310 LET Z=INT (13*(X+1)/5)+INT (5*L/4)+INT (P/4)-P 320 LET Z=Z-7*INT (Z/7) 330 RETURN ```