This program calculates and displays solar and lunar eclipses for any year between 1000 and 4000, using Jean Meeus’s astronomical algorithms. It computes Julian Day Numbers and applies trigonometric correction terms to determine the eclipse magnitude, type (total, annular, annular/total, or partial for solar; umbral, penumbral, or partial for lunar), and geographic visibility zone. For lunar eclipses, subroutine 600 further calculates the precise GMT times of partial and total phase start and end. Output can be directed to either the screen (stream 4 opened to “s”) or a printer (stream 4 opened to “p”), and two UDG characters are defined via POKEs and DATA at line 2000 for the title screen decoration.
Program Analysis
Program Structure
The program is divided into a title/setup section, a main eclipse-scanning loop, and a set of reusable subroutines. Execution begins at line 1000 via GO TO 1000 at line 8, which handles initialization, title display, year input, and output device selection before jumping to line 50 to start processing.
- Lines 1000–1300: Initialization — builds lookup strings, defines UDGs, draws the title screen, accepts year input and output device choice, opens stream
#4. - Lines 50–94: Main loop — iterates over lunation numbers
jfor the chosen year, checking first for solar eclipses (new moon,k=j) then lunar eclipses (full moon,k=j+0.5). - Lines 100–145: Subroutine — computes the Julian Day Number and orbital elements (
m,n,f) for lunationk. - Lines 150–160: Subroutine — applies time corrections to
jd. - Lines 200–240: Subroutine — computes eclipse magnitude parameters
g(gamma),u, ands. - Lines 300–370: Subroutine — converts Julian Day to calendar date (year
yr, monthm, daydy), handling the Gregorian calendar reform at JD 2299161. - Lines 500–540: Subroutine — prints the geographic visibility zone based on the sign of
g. - Lines 600–670: Subroutine — computes and prints GMT contact times for lunar eclipses (partial start/end, total start/end, and maximum).
- Lines 15–48: Utility subroutines — format fractional day fractions into
HH:MMtime strings using the pre-built digit lookup stringn$. - Line 9000: Year-end cleanup — issues blank lines and closes stream
#4.
Astronomical Algorithm
The core mathematics follows Jean Meeus’s eclipse prediction method. Lunation number k is derived at line 50 from the expression (y-1900)*12.3685, and both new moon (k=j) and full moon (k=j+0.5) are tested each iteration. Line 100 computes t=k/1236.85 as the centuries-scaled argument. The Julian Day formula at line 120 includes a periodic perturbation term using SIN. Lines 130–140 compute the Sun’s mean anomaly m, the Moon’s argument of latitude f, and the Moon’s mean anomaly n. Subroutine 200 calculates gamma g (the minimum distance of the Moon’s shadow axis from Earth’s center) and the penumbra/umbra size parameter u.
Eclipse Classification Logic
Solar eclipse types are determined by a cascade of IF tests at lines 58–66, based on the values of g, u, and a computed threshold w:
ABS g >= 0.9972→ Partial eclipse of the Sun (line 68)u < 0→ Total (line 60)u > 0.0047→ Annular (line 62)u < w(wherew = 0.00464*COS(ASN g)) → Annular/Total (hybrid, line 65)- Otherwise → Annular (line 66)
Lunar eclipses (lines 72–93) compute two magnitude values: m1 (umbral) and m2 (penumbral). If m1 < 0 the eclipse is penumbral only; if m1 <= 1.00 it is partial umbral; otherwise it is total.
Calendar Conversion
Subroutine 300 implements the standard Julian Day to Gregorian/Julian calendar algorithm. The branch at lines 310–320 handles the Gregorian reform: dates before JD 2299161 (October 15, 1582) use the Julian calendar path, while later dates apply the Gregorian correction via the intermediate variable a. This makes the program astronomically correct for the full year range 1000–4000.
Time Formatting with Lookup Strings
Rather than using STR$ and padding, the program pre-builds the string n$ at line 1020 containing the two-digit representations “00” through “60” concatenated. Hours and minutes are then extracted by slicing: n$(2*h+1 TO 2*h+2). This avoids formatting conditionals and is a compact BASIC idiom well-suited to memory-limited environments. Similarly, d$ holds two-character day abbreviations and m$ holds four-character month abbreviations for date display.
Output Stream Abstraction
All output is directed to stream #4, which is opened to either "s" (screen) or "p" (printer) at lines 1230 and 1300 respectively. This clean abstraction means all PRINT statements in the subroutines are identical regardless of output device. The user selects the device by pressing 1 or 2, with input validated at line 1210.
UDG Definition
Lines 1030–1040 read 8 pairs of bytes from the DATA statement at line 2000 and POKE them into the UDG area for characters "a" and "b" (using USR "a" and USR "b"). The defined UDGs are used in the title screen display at line 1220 via CHR$ 144 and CHR$ 145, providing decorative graphical markers.
Notable Techniques and Idioms
DEF FN d(x)=INT (1000*x+0.5)/1000at line 5 provides a rounding-to-3-decimal-places function used for clean magnitude display.- The expression
12*(e>=13.5)at line 350 exploits the fact that Boolean results are 1 or 0 to perform a conditional subtraction inline. PAUSE 0is not used; instead the program usesPAUSE 30(line 1195) for brief display delays.- The year boundary check at line 10 (
IF yr>y THEN GO TO 9000) and line 57 (IF yr<y THEN GO TO 72) guard against printing eclipses outside the requested year — necessary because the Julian Day to calendar conversion can yield dates in adjacent years near year boundaries. VALis not used forGO TOtargets here; all branches use literal line numbers.
Variable Reuse and Potential Confusion
Several variable names are reused across contexts, which is a common source of subtle bugs in long BASIC programs. Notably, t is used as the centuries argument in subroutine 100 (line 100), as a temporary Julian Day save register in subroutine 600 (line 630), and as the device-choice integer at line 1210. Similarly, n is used as the minute value in the time-formatting subroutines (lines 20, 42) and also as the Moon’s mean anomaly in the astronomical subroutines. The programmer manages this by carefully sequencing subroutine calls, but readers should be aware that variables are global throughout.
Data Table
| Line(s) | Purpose |
|---|---|
5 | Define rounding function FN d |
15–35 | Format and print date+time header line |
40–48 | Format and print time only (GMT) |
50–94 | Main year-scanning loop |
100–145 | Compute JD and orbital elements |
150–160 | Apply JD correction |
200–240 | Compute eclipse parameters g, u |
300–370 | Julian Day → calendar date |
500–540 | Print visibility zone |
600–670 | Print lunar eclipse contact times |
1000–1300 | Initialization and UI |
2000 | UDG bitmap data |
9000 | End-of-year output and close stream |
Content
Source Code
2 REM Solar/ Lunar Eclipses
4 REM \* 1985 I. Auersbacher
5 DEF FN d(x)=INT (1000*x+0.5)/1000
8 BORDER 5: GO TO 1000
10 IF yr>y THEN GO TO 9000
11 RETURN
12 PRINT #4;"________________________________": PRINT #4: RETURN
15 LET f=24*f: LET h=INT f
20 LET n=INT (60*(f-h))
25 LET t$=n$(2*h+1 TO 2*h+2)+":"+n$(2*n+1 TO 2*n+2): LET d=z+1.0: LET d=d-(7*INT (d/7))+1
30 PRINT #4;"Date: ";d$(2*d-1 TO 2*d);" ";m$(4*m-3 TO 4*m);n$(2*dy+1 TO 2*dy+2);",";yr;" ";t$
35 RETURN
40 LET f=24*f: LET h=INT f
42 LET n=INT (60*(f-h))
44 LET t$=n$(2*h+1 TO 2*h+2)+":"+n$(2*n+1 TO 2*n+2)
46 PRINT #4;t$;" GMT"
48 RETURN
50 BEEP 0.05,22: LET j=(y-1900)*12.3685: LET j=INT j: LET mn=1
52 PRINT #4;" Solar/Lunar Eclipses ";y: PRINT #4;"\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''":
54 LET k=j: GO SUB 100
55 IF ABS (SIN f)>0.359 THEN GO TO 72
56 GO SUB 200: IF ABS g>(1.5432+u) THEN GO TO 72
57 GO SUB 150: GO SUB 300: GO SUB 10: IF yr<y THEN GO TO 72
58 PRINT #4;" *** SOLAR ECLIPSE ***": IF ABS g>=0.9972 THEN GO TO 68
59 PRINT #4;"Central Eclipse - ";
60 IF u<0 THEN PRINT #4;" Total ":: GO SUB 15:: GO SUB 500: GO SUB 12: GO TO 72
62 IF u>0.0047 THEN PRINT #4;"Annular":: GO SUB 15: GO SUB 500: GO SUB 12: GO TO 72
64 LET w=0.00464*COS (ASN g)
65 IF u<w THEN PRINT #4;"Annular/Total":: GO SUB 15: GO SUB 500: GO SUB 12: GO TO 72
66 PRINT #4;" Annular ":: GO SUB 15: GO SUB 500: GO SUB 12: GO TO 72
68 PRINT #4;"Partial Eclipse of the SUN":: GO SUB 15
70 LET m1=(1.5432+u-ABS g)/(0.5460+2*u): LET m1=INT (1000*m1+0.5)/1000
71 PRINT #4;"Maximum Magnitude= ";m1: GO SUB 500: GO SUB 12
72 LET k=j+0.5: GO SUB 100
73 IF ABS (SIN f)>0.359 THEN GO TO 94
74 GO SUB 200: LET m1=(1.0129-u-ABS g)/0.5450: LET m2=(1.5572+u-ABS g)/0.5450: IF (m1<0)*(m2<0) THEN GO TO 94
75 LET m1=FN d(m1): LET m2=FN d(m2): GO SUB 150: GO SUB 300: GO SUB 10: IF yr<y THEN GO TO 94
76 PRINT #4;"LUNAR ECLIPSE - ";: IF m1<0 THEN GO TO 88
78 PRINT #4;" (Umbral) "
79 IF m1<=1.00 THEN LET m2=m1: GO TO 90
80 PRINT #4;"Total Eclipse of the MOON": LET w=n: GO SUB 15
82 PRINT #4;"Maximum Magnitude= ";m1: GO SUB 600
86 GO SUB 12: GO TO 94
88 PRINT #4;"(Penumbral)"
90 PRINT #4;"Partial Eclipse of the Moon": GO SUB 15
92 PRINT #4;"Maximum Magnitude= ";m2
93 GO SUB 12
94 LET j=j+1: GO TO 54
100 LET t=k/1236.85
110 LET t2=t*t: LET t3=t*t*t
120 LET jd=2415020.75933+29.53058868*k+(0.0001178*t*t-1.55e-7*t*t*t+0.00033*SIN (2.90702+2.31902*t-1.601e-4*t*t))
130 LET m=6.269645+5.079842936e-1*k-5.81195e-7*t2-6.05629e-8*t3
135 LET n=5.341149+6.733775529*k+1.872843e-4*t2+2.157227e-7*t3
140 LET f=0.3716923+6.818486627*k-2.88468e-5*t2-4.1713e-8*t3
145 RETURN
150 LET c=(.1734-.000393*t)*SIN m+0.0021*SIN (2*m)-0.4068*SIN n+0.0161*SIN (2*n)-0.0104*SIN (2*f)-0.0051*SIN (m+n)-0.0074*SIN (m-n)
160 LET jd=jd+c+0.5: RETURN
200 LET s=5.19595-0.0048*COS m+0.0020*COS (2*m)-0.3283*COS n-0.0060*COS (m+n)+0.0041*COS (m-n)
210 LET c=0.2070*SIN m+0.0024*SIN (2*m)-0.0390*SIN n+0.0115*SIN (2*n)-0.0073*SIN (m+n)-0.0067*SIN (m-n)+0.0117*SIN (2*f)
220 LET g=s*SIN f+c*COS f
230 LET u=0.0059+0.0046*COS m-0.0182*COS n+0.0004*COS (2*n)-0.0005*COS (m+n)
240 RETURN
300 LET z=INT jd: LET f=jd-z
310 IF z<2299161 THEN LET a=z
320 IF z>=2299161 THEN LET a=INT ((z-1867216.25)/36524.25): LET a=z+1+a-INT (a/4)
330 LET b=a+1524: LET c=INT ((b-122.1)/365.25): LET d=INT (365.25*c): LET e=INT ((b-d)/30.6001)
340 LET dy=b-d-INT (30.6001*e)
350 LET m=e-1-12*(e>=13.5)
360 LET yr=c-4716+1*(m<2.5)
370 RETURN
500 PRINT #4;"Visible ";
510 IF g>0.25 THEN PRINT #4;"northern hemisphere": RETURN
520 IF g<-0.25 THEN PRINT #4;"southern hemisphere": RETURN
530 PRINT #4;"equatorial regions"
540 RETURN
600 LET p=1.0129-u: LET t=0.4679-u: LET n=0.5458+0.04*COS w
605 IF t*t-g*g<=0 THEN RETURN
608 IF p*p-g*g<=0 THEN RETURN
610 LET pp=SQR (p*p-g*g)/24/n
620 LET tp=SQR (t*t-g*g)/24/n
630 PRINT #4: LET t=jd: LET jd=t-pp: GO SUB 300: PRINT #4;" Start partial: ";: GO SUB 40
640 LET jd=t-tp: GO SUB 300: PRINT #4;" Start - total: ";: GO SUB 40: LET jd=t: GO SUB 300
650 PRINT #4;" Max. eclipse : ";: GO SUB 40: LET jd=t+tp: GO SUB 300: PRINT #4;" End of total : ";: GO SUB 40: LET jd=t+pp
660 GO SUB 300: PRINT #4;" End - partial: ";: GO SUB 40
670 RETURN
1000 LET d$="SuMoTuWeThFrSa"
1010 LET m$="Jan.Feb.Mar.Apr.May Jun.Jul.Aug.Sep.Oct.Nov.Dec."
1020 LET n$="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960"
1030 BEEP 0.05,22: BEEP 0.06,20: POKE 23609,10: RESTORE 2000
1040 FOR m=0 TO 7: READ n,f: POKE USR "a"+m,n: POKE USR "b"+m,f: NEXT m
1100 BORDER 5: PAPER 3: CLS
1150 PRINT PAPER 6;AT 2,3;"\:'\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\':";AT 3,3;"\: Solar & Lunar Eclipses \ :";AT 4,3;"\: \ :";AT 5,3;"\: \* 1985 I. Auersbacher \ :";AT 6,3;"\:.\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.:"
1155 PLOT 0,0: DRAW 0,175: DRAW 255,0: DRAW 0,-175: DRAW -255,0
1160 PRINT PAPER 7;AT 8,4;"Enter Year:";
1165 INPUT y: IF y<1000 OR y>4000 THEN BEEP 1.5,-22: GO TO 1165
1170 PRINT PAPER 6;" ";y;" "
1180 PAUSE 30: PRINT PAPER 6;AT 10,4;"Output to which device? "
1190 PRINT PAPER 7;AT 12,9;"1-TV Screen ";AT 13,9;"2-TS Printer"
1195 PAUSE 30: PRINT PAPER 6;AT 15,4;"Please pick option (1-2)"
1200 LET q$=INKEY$: IF q$="" THEN GO TO 1200
1210 LET t=CODE q$-48: IF (t<1)+(t>2) THEN BEEP 0.5,-15: BEEP 0.6,-20: GO TO 1200
1220 BEEP .05,22: PRINT INK 0;AT 11+t,6;CHR$ 144; INK 6;CHR$ 145
1230 PAUSE 75: IF t=1 THEN OPEN #4,"s": PAPER 7: CLS : GO TO 50
1300 OPEN #4,"p": PRINT FLASH 1; PAPER 7;AT 18,4;" See printer for output ": GO TO 50
2000 DATA 0,24,0,48,127,96,127,255,127,255,115,248,115,248,127,240
3000 SAVE "eclipse" LINE 8:
3100 STOP : STOP
9000 LPRINT : LPRINT : LPRINT : CLOSE #4: BEEP .2,22: BEEP .2,20
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

