CAL

Date: 198x
Type: Cassette
Platform(s): TS 1000
Tags: Home

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:

  1. 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.
  2. 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.
  3. 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 C tracks days elapsed in the month; when C = A(M+1)+1 the month is exhausted and the subroutine exits early via GOTO 580.
  • A tens-digit suppression trick at line 546 resets D to 0 when it reaches 10, so PRINT D outputs 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–406W1=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).
546The 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.
232Leap 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.
6The 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

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

People

No people associated with this content.

Scroll to Top