--- title: "Calendar Program" id: 71175 type: "computer_media" slug: "calendar-program" url: "http://localhost/computer_media/calendar-program/" markdown_url: "http://localhost/computer_media/calendar-program.md" published_at: "2026-08-31T08:07:33+00:00" modified_at: "2026-08-31T08:08:14+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/calendar-program.png" alt: "Calendar Program screen" excerpt: "Enter a year and up to ten special dates, then watch this BASIC calendar generator print a full 12-month grid with inverse-video highlights and optional hard copy output." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Calendar" slug: "calendar" taxonomy: "genre" url: "http://localhost/type/calendar/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Calendar%20Program%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/calendar-program.png" alt: "Calendar Program screen" media_type_tags: "Calendar" --- # Calendar Program 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: 1. **Initialization (lines 1–42):** Sets up data strings and dimensions arrays. 2. **User input (lines 100–187):** Prompts for year and optional special dates. 3. **Leap year and start-day calculation (lines 200–280):** Computes calendar parameters. 4. **Calendar printing loop (lines 300–570):** Iterates over 12 months, printing each in a framed grid. 5. **Subroutines:** Line 600 (new row), line 700 (print copy), line 800 (highlight special dates). 6. **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 from `INKEY$`, so the copy-on-demand feature is entirely non-functional. - **Line 812:**`CHR$ Z2` references an uninitialized variable `Z2`; should be `CHR$ Z1`. - **Leap year rule:** Only divisibility by 4 is checked; century-year exceptions are not handled. - **Line 270:**`VAL R$((A1-A2)+1)` — if `A1 = A2`, the index is 1, which is valid; but this relies on `R$` 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`, or `Z`, which would corrupt a second run. A full `RUN` or reset of those variables would be needed for correctness. ## 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 ```