Monthly Planner

Products: Monthly Planner
Developer(s): Michael Felerski
Date: 1986
Type: Program
Platform(s): TS 1000
Tags: Calendar

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:

  1. Lines 500–595 — Month setup: collects month name, first day of week, and number of days.
  2. Lines 600–650 — Calendar array builder.
  3. Lines 700–770 — Calendar display.
  4. Lines 900–970 — Appointment entry.
  5. Lines 1100–1160 — Save to cassette or Micro Wafer.
  6. Lines 2000–2880 — Calendar view/navigation hub with sub-menus.
  7. Lines 3000–3510 — First-letter event search.
  8. Lines 4000–5070 — Day item display and item editing.
  9. Lines 6000–6810 — On-screen cursor navigation to select a calendar day.
  10. 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

VariableTypePurpose
C(42)Numeric arrayCalendar grid: 42 cells (6 weeks × 7 days), each holds the day-of-month number or 0 for empty cells
P(155)Numeric arrayDay number for each appointment
E$(155,25)String arrayEvent text, up to 25 characters per appointment
M$(3)String array3-character month abbreviation (e.g. “JAN”)
S$(1)StringSingle-character search key for first-letter event lookup
ANUMScalarNext free appointment slot index (1-based)
FScalarFirst day of month (1=Sun … 7=Sat)
ENDScalarComputed 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 unconditional GOTO 2020 to line 2050. It appears to be a leftover stub.
  • The variable name END is 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$ 118 and dispatch to LL*1000. With LL ranging 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,0 and AT L+2,0 before drawing at AT 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 25 at 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

Appears On

Related Products

Calendar/planner.

Related Articles

Related Content

Image Gallery

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 
 1100 REM SAVE ROUTINE
 1105 CLS 
 1107 LET L=6
 1110 PRINT AT 0,0;"%D%A%T%A% %S%T%O%R%A%G%E% %M%E%N%U"
 1115 PRINT AT 6,2;" SAVE TO CASSETTE"
 1120 PRINT AT 8,2;" SAVE TO MICRO WAFER"
 1123 PRINT AT L,0;"%=%>"
 1125 IF INKEY$="6" THEN LET L=8
 1130 IF INKEY$="7" THEN LET L=6
 1135 IF INKEY$=CHR$ 118 THEN GOTO 1150
 1137 IF L=8 THEN PRINT AT 6,0;"  "
 1139 IF L=6 THEN PRINT AT 8,0;"  "
 1140 GOTO 1123
 1150 IF L=8 THEN RAND USR 13082
 1155 IF L=6 THEN SAVE M$
 1160 RETURN 
 2000 REM CHOOSE YOUR VIEWING
 2003 LET LL=4
 2005 GOSUB 2600
 2020 IF INKEY$="5" THEN GOSUB 2500
 2025 IF INKEY$="8" THEN GOSUB 2800
 2030 IF (INKEY$=CHR$ 118) AND (LL<>5) THEN GOTO LL*1000
 2035 IF (INKEY$=CHR$ 118) AND (LL=5) THEN GOTO 760
 2040 GOTO 2020
 2050 GOTO 700
 2500 IF LL>3 THEN LET LL=LL-1
 2505 IF LL=3 THEN GOSUB 2550
 2510 IF LL=4 THEN GOSUB 2600
 2520 RETURN 
 2550 PRINT AT 21,0;"%F%I%N%D% %E%V%E%N%T"
 2560 PRINT AT 21,12;"VIEW DAY"
 2570 PRINT AT 21,22;"MAIN MENU"
 2580 RETURN 
 2600 PRINT AT 21,0;"FIND EVENT"
 2610 PRINT AT 21,12;"%V%I%E%W% %D%A%Y"
 2620 PRINT AT 21,22;"MAIN MENU"
 2630 RETURN 
 2800 IF LL<5 THEN LET LL=LL+1
 2805 IF LL=4 THEN GOSUB 2600
 2810 IF LL=5 THEN GOSUB 2850
 2820 RETURN 
 2850 PRINT AT 21,0;"FIND EVENT"
 2860 PRINT AT 21,12;"VIEW DAY"
 2870 PRINT AT 21,22;"%M%A%I%N% %M%E%N%U"
 2880 RETURN 
 3000 REM FIND ITEM
 3005 CLS 
 3010 PRINT AT 0,0;"%F%I%N%D% %E%V%E%N%T% %F%O%R%:  ";M$
 3015 PRINT AT 2,0;"ENTER THE FIRST LETTER"
 3020 PRINT "OF THE EVENT YOU ARE"
 3025 PRINT "SEARCHING FOR.  THERE"
 3030 PRINT "WILL BE A SHORT PAUSE"
 3035 PRINT "DURING THE SEARCH....."
 3040 INPUT S$
 3045 FAST 
 3047 CLS 
 3050 PRINT AT 0,0;"%D%A%Y%/%E%V%E%N%T%S% %F%O%U%N%D% % % %I%N%:  ";M$
 3052 LET Y=2
 3055 FOR X=1 TO ANUM
 3060 IF E$(X,1)=S$ THEN PRINT AT Y,0;P(X);AT Y,5;E$(X)
 3065 IF E$(X,1)=S$ THEN LET Y=Y+1
 3070 IF Y>21 THEN GOSUB 3200
 3075 NEXT X
 3080 SLOW 
 3095 GOTO 3300
 3200 PRINT AT 21,0;"%T%Y%P%E% %E%N%T%E%R% %T%O% %C%O%N%T%I%N%U%E"
 3205 SLOW 
 3210 IF INKEY$=CHR$ 118 THEN GOTO 3230
 3220 GOTO 3210
 3230 FAST 
 3235 CLS 
 3240 LET Y=2
 3255 PRINT AT 0,0;"%F%I%N%D% %E%V%E%N%T% %F%O%R%:  ";M$
 3260 RETURN 
 3300 REM SEARCH CHOICES
 3310 LET LX=1
 3315 GOSUB 3400
 3320 IF INKEY$="5" THEN GOSUB 3500
 3325 IF INKEY$="8" THEN GOSUB 3400
 3330 IF INKEY$=CHR$ 118 THEN GOTO 3350
 3340 GOTO 3320
 3350 IF LX=2 THEN GOTO 2050
 3355 IF LX=1 THEN GOTO 3000
 3400 PRINT AT 21,3;"REPEAT";AT 21,17;"%C%A%L%E%N%D%A%R"
 3405 LET LX=2
 3410 RETURN 
 3500 PRINT AT 21,3;"%R%E%P%E%A%T";AT 21,17;"CALENDAR"
 3505 LET LX=1
 3510 RETURN 
 4000 REM ITEM DISPLAY
 4002 GOSUB 6000
 4003 IF DAY=0 THEN GOTO 2050
 4004 CLS 
 4005 FAST 
 4007 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$
 4010 LET TOT=1
 4020 FOR X=1 TO ANUM
 4030 IF P(X)=DAY THEN GOSUB 4200
 4040 IF TOT=6 THEN GOTO 4060
 4050 NEXT X
 4060 SLOW 
 4070 LET LX=3
 4080 GOSUB 4500
 4100 IF INKEY$="5" THEN GOSUB 4600
 4110 IF INKEY$="8" THEN GOSUB 4700
 4120 IF (INKEY$=CHR$ 118) AND (LX=1) THEN GOSUB 5000
 4125 IF (INKEY$=CHR$ 118) AND (LX=2) THEN COPY 
 4130 IF (INKEY$=CHR$ 118) AND (LX=3) THEN GOTO 2050
 4140 GOTO 4100
 4200 REM DISPLAY
 4210 PRINT AT TOT*2,0;X;")"
 4215 PRINT AT TOT*2,5;E$(X,1 TO 25)
 4220 LET TOT=TOT+1
 4230 RETURN 
 4300 PRINT AT 21,0;"%C%H%A%N%G%E% %I%T%E%M";AT 21,14;"COPY";AT 21,21;"CALENDAR"
 4320 RETURN 
 4400 PRINT AT 21,0;"CHANGE ITEM";AT 21,14;"%C%O%P%Y";AT 21,21;"CALENDAR"
 4420 RETURN 
 4500 PRINT AT 21,0;"CHANGE ITEM";AT 21,14;"COPY";AT 21,21;"%C%A%L%E%N%D%A%R"
 4520 RETURN 
 4600 IF LX>1 THEN LET LX=LX-1
 4610 IF LX=1 THEN GOSUB 4300
 4620 IF LX=2 THEN GOSUB 4400
 4630 RETURN 
 4700 IF LX<3 THEN LET LX=LX+1
 4710 IF LX=2 THEN GOSUB 4400
 4720 IF LX=3 THEN GOSUB 4500
 4730 RETURN 
 5000 REM CHANGE ITEM
 5005 PRINT AT 21,0;"ENTER EVENT %I%N%D%E%X NUMBER:      "
 5010 INPUT INDEX
 5015 PRINT AT 21,0;"< CHANGE ON EVENT ABOVE >"
 5020 PRINT AT 19,3;E$(INDEX,1 TO 25)
 5025 PAUSE 160
 5030 PRINT AT 21,0;"< ENTER NEW EVENT DATA: >"
 5035 PAUSE 160
 5040 PRINT AT 21,0;"< ENTER DAY >            "
 5045 INPUT P(INDEX)
 5050 PRINT AT 21,0;"< ENTER EVENT (25 CHAR) >"
 5052 INPUT E$(INDEX)
 5055 PRINT AT 14,0;INDEX;")";"DAY:  ";P(INDEX)
 5060 PRINT AT 15,3;E$(INDEX)
 5061 PRINT AT 19,3;"                         "
 5062 PRINT AT 21,0;"                         "
 5065 GOSUB 4300
 5070 RETURN 
 6000 REM CHOOSE DAY
 6005 LET A=5
 6007 LET B=3
 6010 PRINT AT A,B;"''''"
 6015 IF INKEY$="5" THEN GOSUB 6500
 6020 IF INKEY$="6" THEN GOSUB 6600
 6025 IF INKEY$="7" THEN GOSUB 6700
 6030 IF INKEY$="8" THEN GOSUB 6800
 6035 IF INKEY$=CHR$ 118 THEN GOTO 6050
 6040 GOTO 6010
 6050 REM GET DAY
 6052 PRINT AT 21,0;"          DAY CHOSEN           "
 6053 PAUSE 20
 6055 LET X=1
 6060 FOR I=4 TO 14 STEP 2
 6065 FOR J=3 TO 27 STEP 4
 6070 IF ((A-1)=I) AND (B=J) THEN GOTO 6100
 6072 LET X=X+1
 6075 NEXT J
 6080 NEXT I
 6085 PRINT AT 18,0;"BAD INPUT"
 6087 PAUSE 30
 6089 PRINT AT 18,0;"         "
 6090 LET DAY=0
 6095 RETURN 
 6100 LET Y=1
 6101 IF C(Y)<>0 THEN GOTO 6104
 6102 LET Y=Y+1
 6103 GOTO 6101
 6104 LET DAY=X-Y+1
 6105 IF DAY<1 THEN GOTO 6110
 6106 IF (X>0) AND (X<(END+1)) THEN RETURN 
 6110 PRINT AT 18,0;"BAD INPUT, TRY AGAIN"
 6115 PAUSE 120
 6120 PRINT AT 18,0;"                    "  
 6122 PRINT AT 21,0;"                             "
 6125 IF INKEY$=CHR$ 118 THEN GOTO 6135
 6130 GOTO 6010
 6135 LET DAY=0
 6140 RETURN 
 6500 IF B=3 THEN GOTO 6510
 6502 PRINT AT A,B;"  "
 6505 LET B=B-4
 6510 RETURN 
 6600 IF A>=14 THEN GOTO 6610
 6602 PRINT AT A,B;"  "
 6605 LET A=A+2
 6610 RETURN 
 6700 IF A<=5 THEN GOTO 6710
 6702 PRINT AT A,B;"  "
 6705 LET A=A-2
 6710 RETURN 
 6800 IF B=27 THEN GOTO 6810
 6802 PRINT AT A,B;"  "
 6805 LET B=B+4
 6810 RETURN 
 7000 REM SAVE AND BEGIN ROUTINE
 7010 SAVE "CALENDE%R"
 7020 GOTO 2

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top