This program implements a monthly appointment calendar allowing up to eight appointments per day across 31 days. It stores appointments in eight parallel string arrays (A$ through H$), each dimensioned 31×32, using a 32-character blank string as a sentinel value to detect empty slots. The program supports entering, viewing, editing, and deleting appointments interactively, as well as saving the entire calendar to tape and printing individual days or the full month via LPRINT. Navigation uses INKEY$ polling for menu selection and INPUT for text entry, with option 9 in the day-view triggering immediate printer output.
Program Analysis
Program Structure
The program is organized into several distinct functional regions:
- Lines 10–100: Variable and array initialization — numeric array
X(31)(unused after setup), blank stringU$, and eight 31×32 string arrays. - Lines 110–380: Title/splash screen, month input, and main menu with INKEY$ dispatch.
- Lines 390–900: Day selection and appointment entry loop (slots A$–H$).
- Lines 910–1010: Tape save routine.
- Lines 1020–1500: Day viewer with inline edit/delete and optional LPRINT output.
- Lines 1510–1570: Entry prompt subroutine and blank-entry guard.
- Lines 1580–1700: Full-month print loop.
- Lines 1710–2020: Eight individual LPRINT subroutines, one per appointment slot.
- Lines 2500–2510: Auto-save and restart block.
Data Storage Design
Appointments are held in eight parallel string arrays A$() through H$(), each dimensioned (31,32), giving 32 characters per appointment slot per day. The 32-character string U$ (32 spaces) serves as a sentinel: a slot equal to U$ is considered empty. This is a straightforward but memory-hungry approach — eight arrays of 31×32 characters consume approximately 7,936 bytes of string storage alone, which is a significant portion of the 16 KB RAM available on an unexpanded machine.
The numeric array X(31) declared at line 10 is never actually used for data storage; the day number is instead held in the numeric variable X entered by the user. The array allocation is therefore wasted memory.
Menu and Navigation
The main menu at lines 220–380 uses an INKEY$ polling loop (lines 320–330) rather than INPUT, avoiding the need for the user to press ENTER. Invalid key presses fall through to GOTO 0220, re-drawing the menu. The leading zeros in GOTO 0320, GOTO 0390, etc. are cosmetic — BASIC ignores them — but they were a common stylistic convention to pad line number references to a consistent width.
Appointment Entry Flow
Lines 460–850 implement a sequential slot-filling loop. Each of the eight slots is checked in order: if the slot is not blank (i.e., already contains an appointment), it is skipped; if it is blank, the entry subroutine (line 1510) is called and user input is stored. The guard subroutine at line 1560 checks whether the entered string I$ is empty, and if so jumps back to the day-selection prompt at line 390, effectively allowing the user to stop entering appointments early by pressing ENTER with no text. If all eight slots fill up, line 870 prints a “IS FULL” message.
Day View, Edit, and Print
The day viewer at lines 1020–1500 displays all eight slots with numeric labels and waits for an INKEY$ keypress. Pressing 9 reprints the screen while simultaneously sending each non-blank slot to the printer via LPRINT. Pressing 0 returns to the menu. Pressing digits 1–8 prompts for a replacement string at lines 1390–1410; pressing ENTER with an empty input effectively deletes the appointment because an empty INPUT on a string variable returns a zero-length string, which will no longer equal U$ — meaning the slot will display as blank but will still not be treated as empty by the sentinel check. This is a subtle bug: deleted entries are replaced with an empty string "" rather than U$, so the “slot full” logic and the print-all loop will incorrectly treat them as occupied.
Print-All Subroutine Redundancy
Lines 1710–2020 consist of eight near-identical LPRINT subroutines, each printing the month name, day number, one appointment string, and a blank line. This block could have been collapsed into a single parameterized subroutine, but the ZX81 BASIC dialect makes passing parameters to subroutines awkward, so the repetition is understandable given the constraints. Together these subroutines account for over 300 lines of essentially duplicated code.
Notable Techniques and Idioms
- INKEY$ polling loop (
IF M$="" THEN GOTO 0320) used for single-keypress menu selection without requiring ENTER. - Blank string sentinel (
U$= 32 spaces) for detecting empty array slots, a common ZX81 idiom since arrays are pre-filled with spaces. - Option
M$="2"(Look at a date) shares the same day-entry input block (line 390) as option 1, with the branch to the viewer at line 450 — the code path diverges only after the day number is entered. - The variable
I$is reused for multiple purposes: appointment text during entry, a print-trigger flag ("9") in the viewer, and a dummy INPUT variable at line 890 for a “press ENTER to continue” pause. SAVE T$at line 1000 saves the entire program and variable space (including all calendar data) under the name entered by the user, providing simple persistence.
Bugs and Anomalies
| Location | Issue |
|---|---|
| Lines 460–850 (skip logic) | Slots already containing data are skipped rather than appended after, meaning re-entering a day skips to the first empty slot but also skips editing existing entries in the entry path. |
| Lines 1420–1490 (delete) | Replacing an appointment with an empty INPUT sets the slot to "", not U$. The sentinel check (<>U$) will then treat the slot as occupied, causing it to appear in print-all output as a blank line. |
| Line 10 | DIM X(31) allocates a numeric array that is never used, wasting approximately 128 bytes of RAM. |
| Line 1040 | On first entry to the viewer, I$ may hold stale content from a previous interaction, potentially triggering unexpected LPRINT output. |
Content
Source Code
10 DIM X(31)
20 LET U$=" "
30 DIM A$(31,32)
40 DIM B$(31,32)
50 DIM C$(31,32)
60 DIM D$(31,32)
70 DIM E$(31,32)
80 DIM F$(31,32)
90 DIM G$(31,32)
100 DIM H$(31,32)
110 CLS
120 PRINT AT 2,8; "ZX81/TIMEX 1000"
130 PRINT AT 3,6;"APPOINTMENT CALENDAR"
140 PRINT AT 4,14;"BY"
150 PRINT AT 5,4;"HEATH COMPUTER SERVICES"
160 PRINT AT 6,7;"950 EAST 52 SOUTH"
170 PRINT AT 7,9;"GREENTOWN IN."
180 PRINT AT 8,12;"46936"
190 PRINT AT 10,8;"COPYRIGHT 1982"
200 PRINT AT 18,8;"(ENTER MONTH)"
210 INPUT S$
220 CLS
230 PRINT "********************************"
240 PRINT AT 2,10;S$
250 PRINT AT 6,6;"DO YOU WANT TO--"
260 PRINT AT 10,3;"(1)ENTER APPOINTMENTS"
270 PRINT AT 11,3;"(2)LOOK AT A DATE"
280 PRINT AT 12,3;"(3)SAVE CALENDAR ON TAPE"
290 PRINT AT 13,3;"(4)PRINT CALENDAR"
300 PRINT AT 16,6;"PRESS NO. 1-4"
310 PRINT AT 20,0;"********************************"
320 LET M$=INKEY$
330 IF M$="" THEN GOTO 0320
340 IF M$="1" THEN GOTO 0390
350 IF M$="2" THEN GOTO 0390
360 IF M$="3" THEN GOTO 0910
370 IF M$="4" THEN GOTO 1580
380 GOTO 0220
390 CLS
400 PRINT AT 8,8;"ENTER THE DAY"
410 PRINT AT 18,6;"ENTER (0) FOR MENU"
420 INPUT X
430 IF X=0 THEN GOTO 0220
440 IF X>31 THEN GOTO 0390
450 IF M$="2" THEN GOTO 1020
460 IF A$(X)<>U$ THEN GOTO 0510
470 GOSUB 1510
480 INPUT I$
490 GOSUB 1560
500 LET A$(X)=I$
510 IF B$(X)<>U$ THEN GOTO 0560
520 GOSUB 1510
530 INPUT I$
540 GOSUB 1560
550 LET B$(X)=I$
560 IF C$(X)<>U$ THEN GOTO 0610
570 GOSUB 1510
580 INPUT I$
590 GOSUB 1560
600 LET C$(X)=I$
610 IF D$(X)<>U$ THEN GOTO 0660
620 GOSUB 1510
630 INPUT I$
640 GOSUB 1560
650 LET D$(X)=I$
660 IF E$(X)<>U$ THEN GOTO 0710
670 GOSUB 1510
680 INPUT I$
690 GOSUB 1560
700 LET E$(X)=I$
710 IF F$(X)<>U$ THEN GOTO 0760
720 GOSUB 1510
730 INPUT I$
740 GOSUB 1560
750 LET F$(X)=I$
760 IF G$(X)<>U$ THEN GOTO 0810
770 GOSUB 1510
780 INPUT I$
790 GOSUB 1560
800 LET G$(X)=I$
810 IF H$(X)<>U$ THEN GOTO 0860
820 GOSUB 1510
830 INPUT I$
840 GOSUB 1560
850 LET H$(X)=I$
860 CLS
870 PRINT AT 10,5;S$;" ";X;" IS FULL"
880 PRINT AT 20,2;" PRESS <ENTER> TO CONTINUE"
890 INPUT I$
900 GOTO 0390
910 CLS
920 PRINT "POSITION TAPE"
930 PRINT
940 PRINT "TYPE (MONTH)"
950 PRINT
960 PRINT "PRESS PLAY AND RECORD ON RECORDER"
970 PRINT
980 PRINT "PRESS <ENTER> WHEN READY"
990 INPUT T$
1000 SAVE T$
1010 GOTO 0110
1020 CLS
1030 PRINT AT 1,8;S$;" ";X;
1040 IF I$="9" THEN LPRINT S$;" ";X
1050 PRINT
1060 IF I$="9" THEN LPRINT
1070 PRINT "1"
1080 PRINT A$(X)
1090 IF I$="9" AND A$(X)<>U$ THEN LPRINT A$(X)
1100 PRINT "2"
1110 PRINT B$(X)
1120 IF I$="9" AND B$(X)<>U$ THEN LPRINT B$(X)
1130 PRINT "3"
1140 PRINT C$(X)
1150 IF I$="9" AND C$(X)<>U$ THEN LPRINT C$(X)
1160 PRINT "4"
1170 PRINT D$(X)
1180 IF I$="9" AND D$(X)<>U$ THEN LPRINT D$(X)
1190 PRINT "5"
1200 PRINT E$(X)
1210 IF I$="9" AND E$(X)<>U$ THEN LPRINT E$(X)
1220 PRINT "6"
1230 PRINT F$(X)
1240 IF I$="9" AND F$(X)<>U$ THEN LPRINT F$(X)
1250 PRINT "7"
1260 PRINT G$(X)
1270 IF I$="9" AND G$(X)<>U$ THEN LPRINT G$(X)
1280 PRINT "8"
1290 PRINT H$(X)
1300 IF I$="9" AND H$(X)<>U$ THEN LPRINT H$(X)
1310 PRINT
1320 IF I$="9" THEN LPRINT
1330 PRINT "PRESS (0)FOR MENU (9)TO PRINT"
1340 PRINT "PRESS CORRESPONDING NUMBER TO CHANGE OR DELETE"
1350 LET I$=INKEY$
1360 IF I$="" THEN GOTO 1350
1370 IF I$="9" THEN GOTO 1020
1380 IF I$="0" THEN GOTO 0220
1390 PRINT AT 19,0;"PRESS <ENTER> TO DELETE "
1400 PRINT "ENTER NEW APPOINTMENT "
1410 INPUT J$
1420 IF I$="1" THEN LET A$(X)=J$
1430 IF I$="2" THEN LET B$(X)=J$
1440 IF I$="3" THEN LET C$(X)=J$
1450 IF I$="4" THEN LET D$(X)=J$
1460 IF I$="5" THEN LET E$(X)=J$
1470 IF I$="6" THEN LET F$(X)=J$
1480 IF I$="7" THEN LET G$(X)=J$
1490 IF I$="8" THEN LET H$(X)=J$
1500 GOTO 1020
1510 CLS
1520 PRINT AT 2,6;S$;" ";X
1530 PRINT AT 4,0;"ENTER NEXT APPOINTMENT"
1540 PRINT AT 6,0;"PRESS <ENTER> WHEN FINISHED."
1550 RETURN
1560 IF I$="" THEN GOTO 0390
1570 RETURN
1580 CLS
1590 PRINT AT 9,9;"PRINTING"
1600 FOR X=1 TO 31
1610 IF A$(X)<>U$ THEN GOSUB 1710
1620 IF B$(X)<>U$ THEN GOSUB 1750
1630 IF C$(X)<>U$ THEN GOSUB 1790
1640 IF D$(X)<>U$ THEN GOSUB 1830
1650 IF E$(X)<>U$ THEN GOSUB 1870
1660 IF F$(X)<>U$ THEN GOSUB 1910
1670 IF G$(X)<>U$ THEN GOSUB 1950
1680 IF H$(X)<>U$ THEN GOSUB 1990
1690 NEXT X
1700 GOTO 0220
1710 LPRINT S$;" ";X
1720 LPRINT A$(X)
1730 LPRINT
1740 RETURN
1750 LPRINT S$;" ";X
1760 LPRINT B$(X)
1770 LPRINT
1780 RETURN
1790 LPRINT S$;" ";X
1800 LPRINT C$(X)
1810 LPRINT
1820 RETURN
1830 LPRINT S$;" ";X
1840 LPRINT D$(X)
1850 LPRINT
1860 RETURN
1870 LPRINT S$;" ";X
1880 LPRINT E$(X)
1890 LPRINT
1900 RETURN
1910 LPRINT S$;" ";X
1920 LPRINT F$(X)
1930 LPRINT
1940 RETURN
1950 LPRINT S$;" ";X
1960 LPRINT G$(X)
1970 LPRINT
1980 RETURN
1990 LPRINT S$;" ";X
2000 LPRINT H$(X)
2010 LPRINT
2020 RETURN
2500 SAVE "CALENDA%R"
2510 GOTO 0010
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.



