--- title: "Monthly Planner" id: 56701 type: "computer_media" slug: "monthly-planner" url: "http://localhost/computer_media/monthly-planner/" markdown_url: "http://localhost/computer_media/monthly-planner.md" published_at: "2024-09-22T23:31:48+00:00" modified_at: "2026-04-03T07:58:56+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/MP.jpeg" excerpt: "A full monthly calendar manager with appointment entry, first-letter event search, day-view, item editing, and a hardware-cursor menu system — all in BASIC." 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: "Tardis Software" slug: "tardis-software" taxonomy: "post_tag" url: "http://localhost/tag/tardis-software/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" indiv: - name: "Michael Felerski" slug: "michael-felerski" taxonomy: "indiv" url: "http://localhost/indiv/michael-felerski/" genre: - name: "Calendar" slug: "calendar" taxonomy: "genre" url: "http://localhost/type/calendar/" media_type: "Program" programmers: - name: "Michael Felerski" slug: "michael-felerski" taxonomy: "indiv" url: "http://localhost/indiv/michael-felerski/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Monthly%20Planner%20%28198x%29%28Tardis%20Software%29%28TS1000%29%28US%29%28Program%29.zip" mediadate: "1986" producer_company: - id: 57915 title: "Tardis Software" type: "company" url: "http://localhost/company/tardis-software/" images: - url: "http://localhost/wp-content/uploads/2024/09/MP.jpeg" - url: "http://localhost/wp-content/uploads/2024/09/MP-scaled.jpg" - url: "http://localhost/wp-content/uploads/2024/09/MP1.jpg" - url: "http://localhost/wp-content/uploads/2024/09/Mp2.jpg" - url: "http://localhost/wp-content/uploads/2024/09/MP3-scaled.jpg" - url: "http://localhost/wp-content/uploads/2024/09/MP4-scaled.jpg" related_products: - id: 57916 title: "Monthly Planner" type: "product" url: "http://localhost/product/monthly-planner/" media_type_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 | 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 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. ## 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 ```