Biorhythms and Day Of The Week

Products: Biorhythms
Developer(s): Ron Oblander
Date: 1982
Type: Cassette
Platform(s): TS 1000

This program calculates and displays biorhythm charts for a given birth date and target month, plotting Physical (23-day), Emotional (28-day), and Intellectual (33-day) cycles on a 32×24 character screen grid. The user enters birth date and desired month, and the program computes the number of elapsed days to determine each cycle’s phase using sine functions scaled to row positions. Input validation checks for impossible month numbers, day values over 31, and target dates before the birth year. The chart uses FAST mode for calculation, then switches to SLOW for display, with optional COPY output to a ZX Printer and navigation keys to step forward by month or restart entirely. A POKE to address 16418 is used to control the printer buffer/display area.


Program Analysis

Program Structure

The program is organized into a clear sequence of phases:

  1. Data entry (lines 2–67): Prompts for birth date, target month/year, printer choice, and a 4-letter name.
  2. Leap-year detection (lines 77–165): Computes the number of days remaining in the birth year after the birth date (DA1).
  3. Elapsed-year accumulation (lines 250–315): Counts days between end of birth year and start of target year (DA2), correctly inserting 366-day years for leap years.
  4. Days-into-target-month (lines 320–404): Adds the cumulative day-of-year for the target month’s start (DA3).
  5. Phase calculation (lines 405–424): Computes DAYT (total elapsed days) and derives display row offsets FUDGP, FUDGE, FUDGI for each cycle’s first day in the target month.
  6. Array filling (lines 745–946): Runs in FAST mode, filling 32-element arrays P(), E(), I() with screen row values (0–20) for each day.
  7. Display (lines 690–727, 952–967): Renders the chart frame, axis labels, month name, and plots P, E, I characters at computed positions.
  8. Navigation (lines 990–1066): Allows advancing to the next month, restarting, or stopping.

Day-Count Arithmetic

The total elapsed days from birth to the start of the target month is built from three components:

  • DA1: days remaining in the birth year after the birth date (lines 80–135 for non-leap, 155–160 for leap January/February).
  • DA2: sum of days in all complete years between birth year+1 and the target year (lines 260–315), with leap years yielding 366.
  • DA3: cumulative day offset to the start of the target month within the target year (lines 325–380), with a +1 correction at line 395 if the target year is a leap year and the month is March or later.

The combined total DAYT = DA1 + DA2 + DA3 - 1 (line 405) gives the number of days from the birth date to the first day of the target month.

Biorhythm Phase and Screen Mapping

Lines 422–424 compute the display column offset for day 1 of the target month within each cycle:

  • FUDGP (Physical, period 23): INT(.5 + 22 - (DAYT/23 - INT(DAYT/23))*23)
  • FUDGE (Emotional, period 28): INT(.5 + 27 - (DAYT/28 - INT(DAYT/28))*28)
  • FUDGI (Intellectual, period 33): INT(.5 + 32 - (DAYT/33 - INT(DAYT/33))*33)

The fractional part of DAYT/period gives the phase offset, which is subtracted from the period length and rounded to find how many days remain in the current cycle at the month boundary. The sine functions in the array-fill loops then use this offset to place the plotted character at the correct screen row.

Array Filling and Sine Scaling

The array-fill loops (e.g., lines 755–805 for Physical) use two nested strategies:

  • Forward fill: for day index P, compute column X = FUDGP + P; if X ≤ 31, set P(X+1) = 11 - 5*SIN(P/11.5*PI) (descending half of cycle).
  • Backward fill: if X > 31, iterate backwards from the fudge offset to fill the ascending half using P(XX+1) = 11 + 5*SIN(Q/11.5*PI).

The centre row is 11 (midpoint of the 0–20 usable display range). Amplitude scaling is ±5 rows for Physical, ±7 for Emotional, ±9 for Intellectual, matching the relative importance traditionally assigned to each rhythm.

Notable Techniques

  • FAST/SLOW switching: FAST (line 76, 745) is used during calculation to suppress screen updates and speed up the sine-heavy array fills; SLOW (line 690) is restored for display.
  • POKE 16418: Used at line 406 (set to 0) and line 968 (set to 12). Address 16418 is the system variable DF_SZ, the number of lines in the lower display area. Setting it to 0 suppresses the lower screen during calculation; restoring it to 12 after plotting is unusual — the normal value is 2 — and may be an attempt to extend the display or manage COPY output.
  • COPY for printer output: Line 970 conditionally issues COPY based on the user’s initial A$="Y" choice, sending the screen to a ZX Printer.
  • Looping navigation: Lines 1061–1066 increment BMN, handle year rollover, and jump back to line 76 (FAST mode re-entry) to recalculate for the next month without re-entering the birth date.
  • Array variable name collision: The program uses P both as a scalar loop variable (lines 755, 952) and as an array P() (line 750). BASIC resolves these as distinct entities, but the practice is potentially confusing.

Bugs and Anomalies

  • DA validation is incomplete: The check at line 57 only rejects DA > 31; days 29–31 are accepted for February, and day 31 is accepted for 30-day months. This can produce slightly incorrect day counts.
  • BYR=YR check (line 255): The program stops if the birth year equals the target year (line 257), but allows target years only one year ahead of birth without issue. However, the user could enter a target month/year in the birth year but a later month — this would produce incorrect results rather than being caught.
  • 32-element vs 31-day arrays: Arrays are dimensioned to 32 (line 750–752) and the fill loop runs to 32, but only 31 elements are ever plotted. This is harmless but wastes a small amount of memory.
  • Leap-year detection uses two-digit year: LY = (YR/4) - INT(YR/4) works correctly for years divisible by 4 within the two-digit range, but would misidentify year 00 (2000) as a leap year coincidentally correctly, and year 00 (1900) — also correctly, since 1900 was not a leap year but the program has no century handling.
  • Month name PRINT statements (lines 701–712): Several month names are missing a comma before the closing quote (e.g., "FEBRUARY""" vs "JANUARY,"""), leading to inconsistent formatting in the display header.

Variable Summary

VariablePurpose
A$Printer flag (“Y”/”N”)
MN, DA, YRBirth month, day, two-digit year
BMN, BYRTarget month and year for display
N$User’s name (4 letters)
DA1Days remaining in birth year after birth date
DA2Days in complete years between birth and target
DA3Day-of-year offset for start of target month
DAYTTotal elapsed days from birth to target month
FUDGP/FUDGE/FUDGIPhase column offsets for P/E/I cycles
P(), E(), I()Screen row arrays for each cycle’s 31 days
LY, LYYLeap-year fraction test and next leap year

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Biorhythms and Day Of The Week

Source Code

   1 REM BIORHYTHM 
   2 PRINT "PROGRAM TO CALCULATE BIORHYTHMS FOR GIVEN MONTHS WHEN ZX81 IS   PROVIDED WITH BIRTHDATE.        COPYRIGHT-RONALD G. OBLANDER,   ANN ARBOR,MICHIGAN 1982"              
   3 PRINT "IF PRINTER OUTPUT IS DESIRED,    ENTER ""Y"",IF NOT ""N""";"--";
   4 INPUT A$
   5 PRINT A$
   6 PRINT "ENTER MONTH OF BIRTH IN          NUMERICAL FORM";"--";
  10 INPUT MN
  11 PRINT MN
  15 PRINT "ENTER DAY OF MONTH OF BIRTH--";
  20 INPUT DA
  21 PRINT DA
  25 PRINT "ENTER LAST TWO DIGITS OF YEAR OF BIRTH";"--";
  30 INPUT YR
  31 PRINT YR
  35 PRINT "ENTER MONTH OF BIORHYTHMS        DESIRED";"--";
  36 INPUT BMN
  37 PRINT BMN
  42 PRINT "ENTER LAST TWO DIGITS OF YEAR OF BIORHYTHMS";"--";
  43 INPUT BYR
  44 PRINT BYR
  52 IF BYR<YR THEN GOTO 59
  53 IF MN<1 THEN GOTO 61
  54 IF MN>12 THEN GOTO 61
  55 IF BMN<1 THEN GOTO 61
  56 IF BMN>12 THEN GOTO 61
  57 IF DA>31 THEN GOTO 63
  58 GOTO 65
  59 PRINT "BIORHYTHMS BEFORE BIRTH ARE     INVALID DUMMY-RERUN"
  60 STOP 
  61 PRINT "INVALID MONTH NUMBER DUMB-BELL -RERUN"
  62 STOP 
  63 PRINT "INVALID DAY NUMBER, IDIOT -RERUN"
  64 STOP 
  65 PRINT "ENTER NAME(4 LETTERS ONLY)--";
  66 INPUT N$
  67 PRINT N$
  76 FAST 
  77 LET LY=(YR/4)-INT (YR/4)
  78 LET LYY=(INT (YR/4)+1)*4
  79 IF LY=0 THEN GOTO 150
  80 IF MN=1 THEN LET DA1=366-DA
  85 IF MN=2 THEN LET DA1=335-DA
  90 IF MN=3 THEN LET DA1=307-DA
  95 IF MN=4 THEN LET DA1=277-DA
 100 IF MN=5 THEN LET DA1=246-DA
 105 IF MN=6 THEN LET DA1=216-DA
 110 IF MN=7 THEN LET DA1=186-DA
 115 IF MN=8 THEN LET DA1=154-DA
 120 IF MN=9 THEN LET DA1=123-DA
 125 IF MN=10 THEN LET DA1=93-DA
 130 IF MN=11 THEN LET DA1=62-DA
 135 IF MN=12 THEN LET DA1=32-DA
 140 GOTO 250
 150 IF MN>=3 THEN GOTO 90
 155 IF MN=1 THEN LET DA1=367-DA
 160 IF MN=2 THEN LET DA1=336-DA
 165 GOTO 250
 250 LET DA2=0
 255 IF YR=BYR THEN GOTO 257
 256 GOTO 260
 257 PRINT "BIORHYTHMS IN YEAR OF BIRTH     CANNOT BE CALCULATED-SORRY"
 258 STOP 
 260 LET YR1=YR+1
 265 IF YR1=BYR THEN GOTO 320
 270 IF YR1=LYY THEN GOTO 305
 280 LET DA2=DA2+365
 285 LET YR1=YR1+1
 290 IF YR1=BYR THEN GOTO 320
 295 IF YR1=LYY THEN GOTO 305
 300 GOTO 280
 305 LET DA2=DA2+366
 310 LET LYY=LYY+4
 315 GOTO 285
 320 IF YR1=LYY THEN GOTO 385
 325 IF BMN=1 THEN LET DA3=0
 330 IF BMN=2 THEN LET DA3=31
 335 IF BMN=3 THEN LET DA3=59
 340 IF BMN=4 THEN LET DA3=90
 345 IF BMN=5 THEN LET DA3=120
 350 IF BMN=6 THEN LET DA3=151
 355 IF BMN=7 THEN LET DA3=181
 360 IF BMN=8 THEN LET DA3=212
 365 IF BMN=9 THEN LET DA3=243
 370 IF BMN=10 THEN LET DA3=273
 375 IF BMN=11 THEN LET DA3=304
 380 IF BMN=12 THEN LET DA3=334
 381 GOTO 405
 385 IF BMN>2 THEN GOTO 395
 390 GOTO 325
 395 LET DA2=DA2+1
 400 GOTO 325
 405 LET DAYT=-1+DA1+DA2+DA3
 406 POKE 16418,0
 422 LET FUDGP=INT (.5+22-(DAYT/23-INT (DAYT/23))*23)
 423 LET FUDGE=INT (.5+27-(DAYT/28-INT (DAYT/28))*28)
 424 LET FUDGI=INT (.5+32-(DAYT/33-INT (DAYT/33))*33)
 425 GOTO 745
 690 SLOW 
 691 PRINT AT 11,0;"% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
 692 IF BMN=12 OR BMN=1 OR BMN=3 OR BMN=5 OR BMN=7 OR BMN=8 OR BMN=10 THEN PRINT AT 10,0;"          1111111111222222222233"
 693 IF BMN=12 OR BMN=1 OR BMN=3 OR BMN=5 OR BMN=7 OR BMN=8 OR BMN=10 THEN PRINT AT 12,0;" 1234567890123456789012345678901"
 694 IF BMN=4 OR BMN=6 OR BMN=9 OR BMN=11 THEN PRINT AT 10,0;"          111111111122222222223"
 695 IF BMN=4 OR BMN=6 OR BMN=9 OR BMN=11 THEN PRINT AT 12,0;" 123456789012345678901234567890"
 696 IF BMN=2 THEN PRINT AT 10,0;"          1111111111222222222"
 697 IF BMN=2 THEN PRINT AT 12,0;" 1234567890123456789012345678"
 699 PRINT AT 0,0;"*******BIORHYTHMS FOR ";N$;"******"
 700 PRINT AT 1,4;"FOR MONTH OF ";
 701 IF BMN=1 THEN PRINT "JANUARY,""";BYR;
 702 IF BMN=2 THEN PRINT "FEBRUARY""";BYR;
 703 IF BMN=3 THEN PRINT "MARCH,""";BYR;
 704 IF BMN=4 THEN PRINT "APRIL,""";BYR;
 705 IF BMN=5 THEN PRINT "MAY,""";BYR;
 706 IF BMN=6 THEN PRINT "JUNE,""";BYR;
 707 IF BMN=7 THEN PRINT "JULY,""";BYR;
 708 IF BMN=8 THEN PRINT "AUGUST,""";BYR;
 709 IF BMN=9 THEN PRINT "SEPTEMBER,""";BYR;
 710 IF BMN=10 THEN PRINT "OCTOBER,""";BYR;
 711 IF BMN=11 THEN PRINT "NOVEMBER,""";BYR;
 712 IF BMN=12 THEN PRINT "DECEMBER,""";BYR;
 713 PRINT AT 23,0;"******BIRTHDATE:";MN;"/";DA;"/";YR;"******"
 717 PRINT AT 21,2;"P";"=PHYS    ";"E";"=EMOT    ";"I";"=INTEL"
 718 FOR S=2 TO 9
 719 PRINT AT S,0;"%+"
 720 PRINT AT S+11,0;"%-"
 721 NEXT S
 722 PRINT AT 22,6;"% % % % % =CRITICAL DAY"
 723 PRINT AT 9,0;"%D"
 724 PRINT AT 10,0;"%A"
 725 PRINT AT 11,0;"%T"
 726 PRINT AT 12,0;"%E"
 727 GOTO 952
 745 FAST 
 750 DIM P(32)
 751 DIM E(32)
 752 DIM I(32)
 755 FOR P=0 TO 32
 760 LET X=FUDGP+P
 765 IF X>31 THEN GOTO 780
 770 LET P(X+1)=11-5*SIN (P/11.5*PI)
 775 GOTO 805
 780 FOR Q=1 TO 31
 785 LET XX=FUDGP-Q
 790 IF XX<0 THEN GOTO 815
 795 LET P(XX+1)=11+5*SIN (Q/11.5*PI)
 800 NEXT Q
 805 NEXT P
 815 FOR E=0 TO 32
 820 LET X1=FUDGE+E
 825 IF X1>31 THEN GOTO 840
 830 LET E(X1+1)=11-7*SIN (E/14*PI)
 835 GOTO 865
 840 FOR F=1 TO 31
 845 LET XXX=FUDGE-F
 850 IF XXX<0 THEN GOTO 900
 855 LET E(XXX+1)=11+7*SIN (F/14*PI)
 860 NEXT F
 865 NEXT E
 900 FOR I=0 TO 31
 905 LET X2=FUDGI+I
 910 IF X2>31 THEN GOTO 925
 915 LET I(X2+1)=11-9*SIN (I/16.5*PI)
 920 GOTO 946
 925 FOR G=1 TO 36
 930 LET X3=FUDGI-G
 935 IF X3<0 THEN GOTO 947
 940 LET I(X3+1)=11+9*SIN (G/16.5*PI)
 945 NEXT G
 946 NEXT I
 947 CLS 
 948 GOTO 690
 952 FOR P=1 TO 31
 953 PRINT AT P(P),P;"P"
 954 NEXT P
 962 FOR P=1 TO 31
 963 PRINT AT E(P),P;"E"
 964 NEXT P
 965 FOR P=1 TO 31
 966 PRINT AT I(P),P;"I"
 967 NEXT P
 968 POKE 16418,12
 970 IF A$="Y" THEN COPY 
 990 PRINT AT 0,0;"PRESS N-FOR NEXT MONTH, R-FOR   RESTART (DIFFERENT BIRTHDATE) ORS-FOR STOP.                   "
 991 IF INKEY$="" THEN GOTO 991
 992 LET U$=INKEY$
 993 IF U$="N" OR U$="R" OR U$="S" THEN GOTO 998
 994 PRINT AT 0,0;"INVALID ENTRY -- REENTER.                                                                "
 995 PAUSE 30
 996 PRINT AT 0,0;"                         "
 997 GOTO 990
 998 IF U$="N" THEN GOTO 1061
 999 IF U$="R" THEN GOTO 1010
 1000 STOP 
 1010 CLS 
 1011 GOTO 2
 1061 LET BMN=BMN+1
 1062 IF BMN<=12 THEN GOTO 76
 1063 LET BMN=BMN-12
 1064 LET BYR=BYR+1
 1066 GOTO 76

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

Scroll to Top