This program implements a monthly calendar manager that allows users to set up a calendar for any named month, view it on screen, enter and search appointments, and save data to cassette or Micro Wafer. The calendar grid is stored in a 42-element array C() mapping week positions to day numbers, with up to 155 appointments held in parallel arrays P() (day numbers) and E$() (25-character event strings). Navigation throughout the program uses a cursor-highlight idiom driven by INKEY$ polling rather than INPUT, with inverse-video characters marking the selected menu option. Special holidays are hard-coded: July 4th and December 25th are displayed in inverse video on the calendar grid. A machine-code routine is invoked via RAND USR 13082 to handle Micro Wafer saves.
Program Analysis
Program Structure
The program is organised into clearly labelled subroutines, each beginning at a round line number. The flow starts at line 100 (main menu), which dispatches to one of four major modules via the computed GOSUB (L*100)+100 at line 490. The modules are:
- Lines 500–595 — Month setup: collects month name, first day of week, and number of days.
- Lines 600–650 — Calendar array builder.
- Lines 700–770 — Calendar display.
- Lines 900–970 — Appointment entry.
- Lines 1100–1160 — Save to cassette or Micro Wafer.
- Lines 2000–2880 — Calendar view/navigation hub with sub-menus.
- Lines 3000–3510 — First-letter event search.
- Lines 4000–5070 — Day item display and item editing.
- Lines 6000–6810 — On-screen cursor navigation to select a calendar day.
- Lines 7000–7020 — Save-and-restart routine.
Lines 25–50 contain array dimension statements that are jumped over by GOTO 100 at line 20; they are executed only when called as a subroutine (via GOSUB 25 at line 501), re-dimensioning arrays before a new month is set up. The RETURN at line 50 closes that subroutine.
Data Structures
| Variable | Type | Purpose |
|---|---|---|
C(42) | Numeric array | Calendar grid: 42 cells (6 weeks × 7 days), each holds the day-of-month number or 0 for empty cells |
P(155) | Numeric array | Day number for each appointment |
E$(155,25) | String array | Event text, up to 25 characters per appointment |
M$(3) | String array | 3-character month abbreviation (e.g. “JAN”) |
S$(1) | String | Single-character search key for first-letter event lookup |
ANUM | Scalar | Next free appointment slot index (1-based) |
F | Scalar | First day of month (1=Sun … 7=Sat) |
END | Scalar | Computed last cell index in C() for the month |
Calendar Array Construction
Lines 600–650 fill the C() array. Cells 1 to F-1 are set to 0 (days before the 1st of the month), cells F to END receive ascending day numbers starting at 1, and any remaining cells up to 42 are zeroed. The variable END is adjusted at line 555: LET END=END+F-1 converts the raw number-of-days into a final array index that accounts for the starting offset.
INKEY$-Based Menu Navigation
All menus use a polling loop rather than INPUT, reading INKEY$ repeatedly. Keys “5” (up) and “8” (down) move a highlight cursor; CHR$ 118 (ENTER/NEWLINE) confirms the selection. The current selection position is tracked in variables L, LL, or LX depending on the menu. The main menu (lines 455–475) moves a two-character inverse-video marker (%=%>) up and down in steps of 2 screen rows, clamped between rows 4 and 10, and dispatches via the computed GOSUB (L*100)+100.
On-Screen Day Selector (Lines 6000–6810)
A block-graphic cursor (\''\'`, a 2×2 block) is moved over the printed calendar grid using the four cursor keys (5/6/7/8). Columns are spaced 4 apart (matching the STEP 4 in the calendar printer at line 720) and rows 2 apart (STEP 2 at line 715). On ENTER, the subroutine at line 6050 performs a reverse-mapping: it iterates through the same nested loop structure as the calendar printer, counting positions until the cursor coordinates match, yielding an index into C() from which the actual day number is recovered. If the cursor is on an empty cell the subroutine returns DAY=0.
Special Holiday Highlighting
Lines 726–727 hard-code two holidays with inverse-video display: July 4th (M$="JUL" and C(X)=4) and December 25th (M$="DEC" and C(X)=25). These checks occur inside the calendar printing loop immediately after the normal day number is printed, overwriting the cell with an inverse-video version.
Machine Code for Micro Wafer
Line 1150 invokes RAND USR 13082 to save data to a Timex Micro Wafer unit. Address 13082 is a known entry point in the Micro Wafer Interface ROM. The standard SAVE M$ at line 1155 handles cassette storage, using the month abbreviation as the tape filename.
Event Search
The search routine (lines 3000–3260) accepts a single character via S$ and iterates through all ANUM appointments comparing E$(X,1) against it. Matching entries are printed with their day number and event text. When the display reaches row 21, subroutine 3200 pauses and clears the screen to handle overflow, then continues from the same loop position — though note the FAST/SLOW calls and the CLS inside the overflow handler at line 3235 mean the outer loop at line 3055 continues printing from the top of the new page without resetting X, which is correct behaviour.
Item Editing (Lines 5000–5070)
The change-item routine prompts for an index number (INDEX) and directly overwrites P(INDEX) and E$(INDEX) with new INPUT values. There is no bounds check on INDEX against ANUM, so entering an out-of-range value would silently corrupt unrelated array cells or produce an error.
Notable Anomalies
- Line 2050 (
GOTO 700) is unreachable — execution can never fall through from line 2040’s unconditionalGOTO 2020to line 2050. It appears to be a leftover stub. - The variable name
ENDis used as a plain numeric variable (lines 545–555). While not a reserved word in this dialect, it is an unusual choice that could cause confusion. - Lines 2030 and 2035 check
INKEY$=CHR$ 118and dispatch toLL*1000. WithLLranging 3–5, this resolves to lines 3000, 4000, or 5000 — but line 5000 is the change-item subroutine, seemingly not intended as a direct destination from this menu path. - The cursor erase in the main menu prints two spaces at
AT L-2,0andAT L+2,0before drawing atAT L,0, meaning the previous cursor position is only erased if it is exactly two rows away; rapid key presses could leave ghost characters. GOSUB 25at line 501 re-dimensions all arrays, which resets appointment data. This is intentional for setting up a new month, but any previously entered appointments are silently discarded.
Content
Source Code
1 REM CALENDAR PROGRAMME
2 REM COPYRIGHT 30-OCT-86
3 REM VERS 1.6, £ A101
4 REM BY: M. FELERSKI
5 LET ANUM=1
7 LET MENU=100
10 DIM M$(3)
20 GOTO 100
25 DIM W(7,5)
30 DIM C(42)
35 DIM P(155)
40 DIM E$(155,25)
45 DIM S$(1)
50 RETURN
100 LET L=4
400 REM MAIN MENU
405 CLS
407 SLOW
410 PRINT "%C%A%L%E%N%D%A%R% %F%O%R% %M%O%N%T%H%:"
415 PRINT AT 0,21;M$
420 REM MAIN MENU CHOICES
430 PRINT
435 PRINT AT 4,2;" SET UP NEW MONTH"
440 PRINT AT 6,2;" VIEW MONTH/ITEMS"
445 PRINT AT 8,2;" ENTER NEW ITEMS"
450 PRINT AT 10,2;" SAVE MONTH TO TAPE/WAFER"
455 PRINT AT L-2,0;" "
457 PRINT AT L+2,0;" "
460 PRINT AT L,0;"%=%>"
465 IF INKEY$="6" THEN LET L=L+2
467 IF INKEY$="7" THEN LET L=L-2
469 IF INKEY$=CHR$ 118 THEN GOTO 490
470 IF L>10 THEN LET L=10
473 IF L<4 THEN LET L=4
475 GOTO 455
490 GOSUB (L*100)+100
495 GOTO 405
500 REM SETUP QUESTIONS
501 GOSUB 25
503 CLS
504 PRINT AT 0,1;"%C%L%E%A%R% %A%N%D% %S%E%T% %U%P% %A% %N%E%W% %M%O%N%T%H"
505 PRINT AT 6,0;"ENTER 3 CHAR. MONTH NAME:"
507 LET ANUM=1
510 INPUT M$
515 PRINT AT 6,27;M$
520 PRINT AT 8,2;"ENTER FIRST DAY OF MONTH:"
525 PRINT AT 10,0;"%I%.%E%. SUN=1, MON=2, TUE=3, ETC,"
530 INPUT F
535 PRINT AT 8,28;F
540 PRINT AT 12,0;"ENTER NUMBER OF DAYS IN MONTH:"
545 INPUT END
555 LET END=END+F-1
560 GOSUB 600
595 RETURN
600 REM SET UP CALENDAR, F=DAY1
603 FAST
605 FOR I=1 TO F-1
610 LET C(I)=0
615 NEXT I
617 LET J=1
620 FOR I=F TO END
625 LET C(I)=J
630 LET J=J+1
635 NEXT I
640 FOR I=(END+1) TO 42
645 LET C(I)=0
647 NEXT I
650 RETURN
700 REM PRINT CALENDER
703 CLS
705 FAST
707 PRINT AT 0,12;"% % ";M$;"% % "
710 PRINT AT 2,2;"SUN MON TUE WED THR FRI SAT"
712 LET X=1
715 FOR I=4 TO 14 STEP 2
720 FOR J=3 TO 27 STEP 4
725 IF C(X)<>0 THEN PRINT AT I,J;C(X)
726 IF (M$="JUL") AND (C(X)=4) THEN PRINT AT I,J;"%4"
727 IF (M$="DEC") AND (C(X)=25) THEN PRINT AT I,J;"%2%5"
730 IF C(X)=0 THEN PRINT AT I,J;" "
735 LET X=X+1
740 NEXT J
745 NEXT I
747 SLOW
750 GOTO 2000
760 REM RETURN TO MAIN MENU
770 RETURN
900 REM ENTER NEW ITEMS
905 CLS
910 PRINT "%E%N%T%E%R% %N%E%W% %A%P%P%O%I%N%T%M%E%N%T%S%:";M$
911 PRINT AT 19,0;"%T%Y%P%E 0 %F%O%R% %D%A%Y% %T%O% %E%X%I%T"
912 LET D=2
915 IF ANUM>155 THEN GOTO 960
920 PRINT AT 21,0;"%E%N%T%E%R% %D%A%Y% %O%F% %T%H%E% %M%O%N%T%H"
925 INPUT P(ANUM)
927 IF P(ANUM)=0 THEN RETURN
930 PRINT AT 21,0;"ENTER EVENT (25 CHAR) "
935 INPUT E$(ANUM)
940 LET D=D+1
945 PRINT AT D,0;P(ANUM);AT D,3;E$(ANUM,1 TO 25)
947 LET ANUM=ANUM+1
948 IF ANUM>155 THEN GOTO 960
950 IF D=18 THEN GOTO 905
955 GOTO 915
960 PRINT AT 21,0;"PROGRAM IS FULL--SORRY "
965 PAUSE 220
970 RETURN
\n1100 REM SAVE ROUTINE
\n1105 CLS
\n1107 LET L=6
\n1110 PRINT AT 0,0;"%D%A%T%A% %S%T%O%R%A%G%E% %M%E%N%U"
\n1115 PRINT AT 6,2;" SAVE TO CASSETTE"
\n1120 PRINT AT 8,2;" SAVE TO MICRO WAFER"
\n1123 PRINT AT L,0;"%=%>"
\n1125 IF INKEY$="6" THEN LET L=8
\n1130 IF INKEY$="7" THEN LET L=6
\n1135 IF INKEY$=CHR$ 118 THEN GOTO 1150
\n1137 IF L=8 THEN PRINT AT 6,0;" "
\n1139 IF L=6 THEN PRINT AT 8,0;" "
\n1140 GOTO 1123
\n1150 IF L=8 THEN RAND USR 13082
\n1155 IF L=6 THEN SAVE M$
\n1160 RETURN
\n2000 REM CHOOSE YOUR VIEWING
\n2003 LET LL=4
\n2005 GOSUB 2600
\n2020 IF INKEY$="5" THEN GOSUB 2500
\n2025 IF INKEY$="8" THEN GOSUB 2800
\n2030 IF (INKEY$=CHR$ 118) AND (LL<>5) THEN GOTO LL*1000
\n2035 IF (INKEY$=CHR$ 118) AND (LL=5) THEN GOTO 760
\n2040 GOTO 2020
\n2050 GOTO 700
\n2500 IF LL>3 THEN LET LL=LL-1
\n2505 IF LL=3 THEN GOSUB 2550
\n2510 IF LL=4 THEN GOSUB 2600
\n2520 RETURN
\n2550 PRINT AT 21,0;"%F%I%N%D% %E%V%E%N%T"
\n2560 PRINT AT 21,12;"VIEW DAY"
\n2570 PRINT AT 21,22;"MAIN MENU"
\n2580 RETURN
\n2600 PRINT AT 21,0;"FIND EVENT"
\n2610 PRINT AT 21,12;"%V%I%E%W% %D%A%Y"
\n2620 PRINT AT 21,22;"MAIN MENU"
\n2630 RETURN
\n2800 IF LL<5 THEN LET LL=LL+1
\n2805 IF LL=4 THEN GOSUB 2600
\n2810 IF LL=5 THEN GOSUB 2850
\n2820 RETURN
\n2850 PRINT AT 21,0;"FIND EVENT"
\n2860 PRINT AT 21,12;"VIEW DAY"
\n2870 PRINT AT 21,22;"%M%A%I%N% %M%E%N%U"
\n2880 RETURN
\n3000 REM FIND ITEM
\n3005 CLS
\n3010 PRINT AT 0,0;"%F%I%N%D% %E%V%E%N%T% %F%O%R%: ";M$
\n3015 PRINT AT 2,0;"ENTER THE FIRST LETTER"
\n3020 PRINT "OF THE EVENT YOU ARE"
\n3025 PRINT "SEARCHING FOR. THERE"
\n3030 PRINT "WILL BE A SHORT PAUSE"
\n3035 PRINT "DURING THE SEARCH....."
\n3040 INPUT S$
\n3045 FAST
\n3047 CLS
\n3050 PRINT AT 0,0;"%D%A%Y%/%E%V%E%N%T%S% %F%O%U%N%D% % % %I%N%: ";M$
\n3052 LET Y=2
\n3055 FOR X=1 TO ANUM
\n3060 IF E$(X,1)=S$ THEN PRINT AT Y,0;P(X);AT Y,5;E$(X)
\n3065 IF E$(X,1)=S$ THEN LET Y=Y+1
\n3070 IF Y>21 THEN GOSUB 3200
\n3075 NEXT X
\n3080 SLOW
\n3095 GOTO 3300
\n3200 PRINT AT 21,0;"%T%Y%P%E% %E%N%T%E%R% %T%O% %C%O%N%T%I%N%U%E"
\n3205 SLOW
\n3210 IF INKEY$=CHR$ 118 THEN GOTO 3230
\n3220 GOTO 3210
\n3230 FAST
\n3235 CLS
\n3240 LET Y=2
\n3255 PRINT AT 0,0;"%F%I%N%D% %E%V%E%N%T% %F%O%R%: ";M$
\n3260 RETURN
\n3300 REM SEARCH CHOICES
\n3310 LET LX=1
\n3315 GOSUB 3400
\n3320 IF INKEY$="5" THEN GOSUB 3500
\n3325 IF INKEY$="8" THEN GOSUB 3400
\n3330 IF INKEY$=CHR$ 118 THEN GOTO 3350
\n3340 GOTO 3320
\n3350 IF LX=2 THEN GOTO 2050
\n3355 IF LX=1 THEN GOTO 3000
\n3400 PRINT AT 21,3;"REPEAT";AT 21,17;"%C%A%L%E%N%D%A%R"
\n3405 LET LX=2
\n3410 RETURN
\n3500 PRINT AT 21,3;"%R%E%P%E%A%T";AT 21,17;"CALENDAR"
\n3505 LET LX=1
\n3510 RETURN
\n4000 REM ITEM DISPLAY
\n4002 GOSUB 6000
\n4003 IF DAY=0 THEN GOTO 2050
\n4004 CLS
\n4005 FAST
\n4007 PRINT AT 0,0;"%I%N%D%E%X%/%E%V%E%N%T%S% %F%O%R% %D%A%Y ";DAY;" %O%F ";M$
\n4010 LET TOT=1
\n4020 FOR X=1 TO ANUM
\n4030 IF P(X)=DAY THEN GOSUB 4200
\n4040 IF TOT=6 THEN GOTO 4060
\n4050 NEXT X
\n4060 SLOW
\n4070 LET LX=3
\n4080 GOSUB 4500
\n4100 IF INKEY$="5" THEN GOSUB 4600
\n4110 IF INKEY$="8" THEN GOSUB 4700
\n4120 IF (INKEY$=CHR$ 118) AND (LX=1) THEN GOSUB 5000
\n4125 IF (INKEY$=CHR$ 118) AND (LX=2) THEN COPY
\n4130 IF (INKEY$=CHR$ 118) AND (LX=3) THEN GOTO 2050
\n4140 GOTO 4100
\n4200 REM DISPLAY
\n4210 PRINT AT TOT*2,0;X;")"
\n4215 PRINT AT TOT*2,5;E$(X,1 TO 25)
\n4220 LET TOT=TOT+1
\n4230 RETURN
\n4300 PRINT AT 21,0;"%C%H%A%N%G%E% %I%T%E%M";AT 21,14;"COPY";AT 21,21;"CALENDAR"
\n4320 RETURN
\n4400 PRINT AT 21,0;"CHANGE ITEM";AT 21,14;"%C%O%P%Y";AT 21,21;"CALENDAR"
\n4420 RETURN
\n4500 PRINT AT 21,0;"CHANGE ITEM";AT 21,14;"COPY";AT 21,21;"%C%A%L%E%N%D%A%R"
\n4520 RETURN
\n4600 IF LX>1 THEN LET LX=LX-1
\n4610 IF LX=1 THEN GOSUB 4300
\n4620 IF LX=2 THEN GOSUB 4400
\n4630 RETURN
\n4700 IF LX<3 THEN LET LX=LX+1
\n4710 IF LX=2 THEN GOSUB 4400
\n4720 IF LX=3 THEN GOSUB 4500
\n4730 RETURN
\n5000 REM CHANGE ITEM
\n5005 PRINT AT 21,0;"ENTER EVENT %I%N%D%E%X NUMBER: "
\n5010 INPUT INDEX
\n5015 PRINT AT 21,0;"< CHANGE ON EVENT ABOVE >"
\n5020 PRINT AT 19,3;E$(INDEX,1 TO 25)
\n5025 PAUSE 160
\n5030 PRINT AT 21,0;"< ENTER NEW EVENT DATA: >"
\n5035 PAUSE 160
\n5040 PRINT AT 21,0;"< ENTER DAY > "
\n5045 INPUT P(INDEX)
\n5050 PRINT AT 21,0;"< ENTER EVENT (25 CHAR) >"
\n5052 INPUT E$(INDEX)
\n5055 PRINT AT 14,0;INDEX;")";"DAY: ";P(INDEX)
\n5060 PRINT AT 15,3;E$(INDEX)
\n5061 PRINT AT 19,3;" "
\n5062 PRINT AT 21,0;" "
\n5065 GOSUB 4300
\n5070 RETURN
\n6000 REM CHOOSE DAY
\n6005 LET A=5
\n6007 LET B=3
\n6010 PRINT AT A,B;"\''\''"
\n6015 IF INKEY$="5" THEN GOSUB 6500
\n6020 IF INKEY$="6" THEN GOSUB 6600
\n6025 IF INKEY$="7" THEN GOSUB 6700
\n6030 IF INKEY$="8" THEN GOSUB 6800
\n6035 IF INKEY$=CHR$ 118 THEN GOTO 6050
\n6040 GOTO 6010
\n6050 REM GET DAY
\n6052 PRINT AT 21,0;" DAY CHOSEN "
\n6053 PAUSE 20
\n6055 LET X=1
\n6060 FOR I=4 TO 14 STEP 2
\n6065 FOR J=3 TO 27 STEP 4
\n6070 IF ((A-1)=I) AND (B=J) THEN GOTO 6100
\n6072 LET X=X+1
\n6075 NEXT J
\n6080 NEXT I
\n6085 PRINT AT 18,0;"BAD INPUT"
\n6087 PAUSE 30
\n6089 PRINT AT 18,0;" "
\n6090 LET DAY=0
\n6095 RETURN
\n6100 LET Y=1
\n6101 IF C(Y)<>0 THEN GOTO 6104
\n6102 LET Y=Y+1
\n6103 GOTO 6101
\n6104 LET DAY=X-Y+1
\n6105 IF DAY<1 THEN GOTO 6110
\n6106 IF (X>0) AND (X<(END+1)) THEN RETURN
\n6110 PRINT AT 18,0;"BAD INPUT, TRY AGAIN"
\n6115 PAUSE 120
\n6120 PRINT AT 18,0;" "
\n6122 PRINT AT 21,0;" "
\n6125 IF INKEY$=CHR$ 118 THEN GOTO 6135
\n6130 GOTO 6010
\n6135 LET DAY=0
\n6140 RETURN
\n6500 IF B=3 THEN GOTO 6510
\n6502 PRINT AT A,B;" "
\n6505 LET B=B-4
\n6510 RETURN
\n6600 IF A>=14 THEN GOTO 6610
\n6602 PRINT AT A,B;" "
\n6605 LET A=A+2
\n6610 RETURN
\n6700 IF A<=5 THEN GOTO 6710
\n6702 PRINT AT A,B;" "
\n6705 LET A=A-2
\n6710 RETURN
\n6800 IF B=27 THEN GOTO 6810
\n6802 PRINT AT A,B;" "
\n6805 LET B=B+4
\n6810 RETURN
\n7000 REM SAVE AND BEGIN ROUTINE
\n7010 SAVE "CALENDE%R"
\n7020 GOTO 2
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.





