This program prints a complete 12-month calendar for any user-specified year on a single ZX81/TS1000 screen, laid out in a 4×3 grid of months. It calculates the day-of-week for January 1st of the requested year using a cumulative day-count formula anchored to 1900, then advances the starting day through each month using a packed string (“312831303130313130313031”) to store the days-per-month. Leap year detection adjusts February to 29 days and corrects the day-count accumulation. The introductory subroutine at line 1030 draws a decorative box using PLOT commands and displays a title screen with a 200-frame pause before clearing to the calendar.
Program Analysis
Program Structure
The program is divided into three logical phases:
- Title screen (lines 5, 1030–1210): A decorative PLOT-drawn border with a “CAL” title and UAS attribution, displayed for about 3 seconds before clearing.
- Setup and layout (lines 6–200): Warns about the 6th-week limitation, prompts for a year, draws the static calendar grid (day headers, month labels, year), and stores days-per-month in a packed string.
- Date filling (lines 200–600): Decodes days-per-month into array
A(), calculates the starting day-of-week for January 1st, and iterates through all 12 months printing dates into the correct screen positions.
Days-per-Month Encoding
Rather than using a DATA/READ approach, the program stores month lengths as the string literal "312831303130313130313031" in A$. Lines 210–230 parse this two characters at a time using VAL (A$(N))+VAL (A$(N+1))*10 to populate the numeric array A(1..12). Note that the formula at line 220 reverses the expected digit order: it computes 10*VAL(A$(N)) + VAL(A$(N+1)), so the leading digit is the tens place. The packed string encodes months as: 31,28,31,30,31,30,31,31,30,31,30,31.
Day-of-Week Calculation
The starting day-of-week for January 1st is derived in lines 240–265:
- Line 240:
W = 366 + ((YEAR-1900)*365)— a cumulative day count from a reference point. - Line 250: Adds one day per leap year elapsed since 1900:
INT((YEAR-1900)/4). - Line 255: If the target year itself is a leap year, subtracts 1 (January 1st precedes the leap day).
- Lines 260–265: Reduces modulo 7; if the result is 0 it is set to 7, mapping Sunday=7 in the SM-TW-TF-S column layout.
The guard at line 245 skips the leap-year correction for years ≤ 1904, avoiding an off-by-one around the 1900 non-leap-year boundary (1900 is divisible by 4 but not a leap year; this is a partial workaround).
Screen Layout
The 12 months are arranged in a 4-column, 3-row grid. Month labels are printed at fixed AT positions (rows 1, 8, 15) with 8-column spacing per column. The “SMTWTFS” header row is printed in inverse video. Each month occupies 8 character columns. Dates are filled starting at screen rows 3, 10, 17 (variable L steps by 7) and columns offset by K*8.
Date-Filling Logic
The subroutine at line 470 fills one month’s dates into the grid:
- The first partial row (lines 470–520) prints day 1 starting at column
W1(the day-of-week) through column 7. - Four subsequent rows (lines 530–570) fill the remaining days, wrapping columns 1–7.
- Variable
Ctracks days elapsed in the month; whenC = A(M+1)+1the month is exhausted and the subroutine exits early viaGOTO 580. - A tens-digit suppression trick at line 546 resets
Dto 0 when it reaches 10, soPRINT Doutputs only the units digit — effectively displaying 10 as “0”. This is a ZX81 width-saving technique but means the tens digit of dates 10–19+ is never printed; only the units digit appears for all dates.
Carry-Forward Between Months
After each month, the starting column for the next month is computed by accumulating total days elapsed (line 585: W = W + A(M)) and taking the fractional day-of-week from the running total (lines 591–593). The INT(W1 + 0.5) rounding at line 592 guards against floating-point remainder errors.
Notable Bugs and Anomalies
| Line(s) | Issue |
|---|---|
| 405–406 | W1=W is set at line 405, then immediately overwritten by M=0 at line 406 (a separate statement). However, W1 is correctly initialised here for January; M being reset to 0 is intentional so the first M+1 reference inside the subroutine indexes A(1). |
| 546 | The tens-digit suppression (IF D=10 THEN LET D=0) causes dates 10–19, 20–29, 30–31 to display only their units digit (e.g., “10” prints as “0”). This is a deliberate space-saving measure but makes the calendar hard to read. |
| 232 | Leap year detection uses only the divide-by-4 rule; century years (e.g., 1900, 2100) that are not leap years are not handled beyond the partial guard at line 245. |
| 6 | The note about “6th week in month will not print” is correct — the grid only allocates 5 date rows per month (one partial + four full), so months starting late in the week can have dates beyond row 5 silently dropped. |
Subroutine at Lines 1030–1210
This title-screen subroutine uses PLOT in two loops to draw a rectangular border: horizontal lines at Y=2 and Y=43 (lines 1030–1060), and vertical lines at X=0 and X=63 (lines 1070–1100). It then uses PRINT AT to place asterisk-bordered text and the program title inside the box. A PAUSE 200 (approximately 3.3 seconds at 60 Hz) gives the user time to read the title before CLS clears to the calendar.
Content
Source Code
5 GOSUB 1030
6 PRINT "%N%O%T%E% %6%T%H% %W%E%E%K% %I%N% % %M%O%N%T%H% %W%I%L%L% %N%O%T% %P%R%I%N%T% %D%U%E% %T%O% %S%P%A%C%E% %L%I%M%I%T%A%T%I%O%N%S% "
8 PRINT AT 5,0;"IT WILL TAKE ABOUT ONE MINUTE TO PRINT CALENDARS"
10 PRINT AT 15,5;"ENTER YEAR DESIRED XXXX"
11 INPUT YEAR
12 LET Z=YEAR-1899
13 DIM A(12)
15 DIM A$(24)
16 CLS
19 FOR L=2 TO 16 STEP 7
20 FOR K=0 TO 3
30 PRINT AT L,(K*8)+1;"%S%M%T%W%T%F%S"
40 NEXT K
50 NEXT L
55 PRINT AT 0,13;YEAR
60 PRINT AT 1,0;"*JAN"
70 PRINT AT 1,8;"*FEB"
80 PRINT AT 1,16;"*MAR"
90 PRINT AT 1,24;"*APR"
100 PRINT AT 8,0;"*MAY"
110 PRINT AT 8,8;"*JUN"
120 PRINT AT 8,16;"*JUL"
130 PRINT AT 8,24;"*AUG"
140 PRINT AT 15,0;"*SEP"
150 PRINT AT 15,8;"*OCT"
160 PRINT AT 15,16;"*NOV"
170 PRINT AT 15,24;"*DEC"
200 LET A$="312831303130313130313031"
205 LET N=1
210 FOR K=1 TO 12
220 LET A(K)=10*VAL (A$(N))+VAL (A$(N+1))
225 LET N=N+2
230 NEXT K
232 IF (YEAR/4)-INT (YEAR/4)=0 THEN LET A(2)=29
240 LET W=366+((YEAR-1900)*365)
245 IF YEAR<=1904 THEN GOTO 260
250 LET W=W+INT ((YEAR-1900)/4)
255 IF (YEAR/4)-INT (YEAR/4)=0 THEN LET W=W-1
260 LET W=W-(INT (W/7)*7)
265 IF W=0 THEN LET W=7
335 LET M=1
405 LET W1=W
406 LET M=0
410 FOR L=3 TO 17 STEP 7
420 FOR K=0 TO 3
426 LET D=1
428 LET C=1
430 GOSUB 470
440 NEXT K
450 NEXT L
460 STOP
470 FOR R=W1 TO 7
500 PRINT AT L,(K*8)+R;D
510 LET D=D+1
515 LET C=C+1
520 NEXT R
530 FOR S=1 TO 4
540 FOR R=1 TO 7
545 IF C=A(M+1)+1 THEN GOTO 580
546 IF D=10 THEN LET D=0
550 PRINT AT L+S,(K*8)+R;D
555 LET D=D+1
556 LET C=C+1
560 NEXT R
570 NEXT S
580 LET M=1+M
585 LET W=W+A(M)
591 LET W1=((W/7)-INT (W/7))*7
592 LET W1=INT (W1+.5)
593 IF W1=0 THEN LET W1=7
599 LET C=1
600 RETURN
1030 FOR K=0 TO 63
1040 PLOT K,43
1050 PLOT K,2
1060 NEXT K
1070 FOR K=2 TO 43
1080 PLOT 0,K
1090 PLOT 63,K
1100 NEXT K
1110 PRINT AT 4,11;"***********"
1120 PRINT AT 8,11;"***********"
1130 FOR K=1 TO 3
1140 PRINT AT 4+K,11;"*"
1150 PRINT AT 4+K,21;"*"
1160 NEXT K
1170 PRINT AT 6,14;"C A L"
1180 PRINT AT 21,0;"UAS BOX612 HADDONFIELD,N.J.08033"
1190 PAUSE 200
1200 CLS
1210 RETURN
1220 SAVE "CA%L"
1230 GOTO 5
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


