--- title: "Lunar Excursion Module" id: 57351 type: "computer_media" slug: "lunar-excursion-module" url: "http://localhost/computer_media/lunar-excursion-module/" markdown_url: "http://localhost/computer_media/lunar-excursion-module.md" published_at: "2024-10-04T08:44:48+00:00" modified_at: "2026-04-03T07:58:34+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/lunar-lander.jpeg" caption: "Vintage 1980s cassette inlay art, Ocean Software style, Bob Wakelin airbrushed acrylic painting, Drew Struzan movie poster composition. Heroic astronaut in gleaming white spacesuit grips manual controls inside a battered lunar lander cockpit, sweat-drenched face lit by instrument glow, cratered Moon surface rushing up through cracked viewport. Montage vignette: retro-rockets firing orange-white exhaust plumes, oxygen gauge plummeting toward zero, stark lunar horizon under star-filled black space. Dramatic diagonal composition, warm amber cockpit lighting against deep cold blues of space, chrome metallic sheen on spacecraft panels, fiery reds and burning oranges, photorealistic painterly finish, urgent tension, vintage sci-fi pulp heroism. --ar 3:4 --raw --v 6 Job ID: 5e1d0a29-7aa2-4f3e-b640-7d02aa0a6ae0" excerpt: "Guide a crippled lunar lander to the Moon's surface by hand, managing retro-rocket burn rates against real physics — before your oxygen runs out." 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: "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: "David Ahl" slug: "ahl-david" taxonomy: "indiv" url: "http://localhost/indiv/ahl-david/" - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56735 title: "Timex Sinclair Public Domain Library Tape 1004" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1004/" media_type: "Program" programmers: - name: "Tony Willing" slug: "tony-willing" taxonomy: "indiv" url: "http://localhost/indiv/tony-willing/" - name: "David Ahl" slug: "ahl-david" taxonomy: "indiv" url: "http://localhost/indiv/ahl-david/" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/lunar-lander.jpeg" caption: "Vintage 1980s cassette inlay art, Ocean Software style, Bob Wakelin airbrushed acrylic painting, Drew Struzan movie poster composition. Heroic astronaut in gleaming white spacesuit grips manual controls inside a battered lunar lander cockpit, sweat-drenched face lit by instrument glow, cratered Moon surface rushing up through cracked viewport. Montage vignette: retro-rockets firing orange-white exhaust plumes, oxygen gauge plummeting toward zero, stark lunar horizon under star-filled black space. Dramatic diagonal composition, warm amber cockpit lighting against deep cold blues of space, chrome metallic sheen on spacecraft panels, fiery reds and burning oranges, photorealistic painterly finish, urgent tension, vintage sci-fi pulp heroism. --ar 3:4 --raw --v 6 Job ID: 5e1d0a29-7aa2-4f3e-b640-7d02aa0a6ae0" - url: "http://localhost/wp-content/uploads/2024/09/199_LEM.png" media_type_tags: "Game" --- # Lunar Excursion Module This program is a Lunar Excursion Module (LEM) landing simulation, originally by David Ahl and adapted for the ZX81/TS1000. The player manually controls retro-rocket burn rate (0–16,500 lb/sec) each 10-second interval, trying to achieve a soft touchdown on the Moon. Physics are handled by a Taylor-series approximation subroutine at line 1000, which computes updated velocity and altitude using five terms of a power-series expansion to avoid division-by-zero issues during the burn. Landing outcomes range from a perfect soft landing through craft damage, stranding, and crater-formation based on the final impact velocity in MPH. The program uses SLOW/FAST toggling loops for blinking effects at lines 310–314 and 5000–5003, and inverse-video characters for highlighted on-screen text output. *** ## Program Analysis ### Program Structure The program is organised into clearly separated phases: 1. **Title and introduction** (lines 0–75): scrolling title, narrative text, and delay loops. 2. **Initialisation** (lines 80–117): sets up simulation constants and variables. 3. **Main simulation loop** (lines 118–180): displays state, accepts burn rate input, steps physics. 4. **Fuel exhaustion handler** (lines 185–205): computes free-fall trajectory after fuel runs out. 5. **Landing outcome display** (lines 265–325): five outcome branches based on impact velocity. 6. **Physics subroutine** (lines 1000–1015): Taylor-series burn computation. 7. **Replay prompt** (lines 2000–2030): asks player to try again. 8. **Blink effect subroutine** (lines 5000–5005): FAST/SLOW toggling for screen flash. 9. **Utility tail** (lines 5020–5050): STOP, CLEAR, SAVE, RUN stubs. ### Simulation Variables | Variable | Meaning | Initial value | | --- | --- | --- | | `L` | Elapsed time (seconds) | 0 | | `A` | Altitude (miles) | 140 | | `V` | Velocity (miles/sec, downward positive) | 1 | | `M` | Total mass (lbs, including fuel) | 33000 | | `N` | Empty (dry) mass (lbs) | 16500 | | `G` | Lunar gravity (miles/sec²) | 1×10⁻³ | | `Z` | Exhaust velocity factor | 1.8 | | `K` | Burn rate (lbs/sec, player-supplied ÷10) | 0 | | `T` | Time remaining in current 10-sec interval | 10 | | `S` | Sub-interval time step | varies | ### Physics Subroutine (lines 1000–1015) The subroutine at line 1000 avoids direct integration of the rocket equation by using a Taylor-series expansion around the mass-ratio term `Q = S*K/M`. This is the classic Ahl LEM approach: rather than computing `LN(1-Q)` directly (which would require a LOG function and risks domain errors near zero), the natural logarithm is approximated as: `-Q - Q²/2 - Q³/3 - Q⁴/4 - Q⁵/5` This gives updated velocity `J` and altitude `I` without needing LOG, making it suitable for systems where accuracy of the transcendental function may vary or to keep the computation self-contained. ### Burn Rate Input Handling The player enters a burn rate at line 123 (`INPUT K`). The value is then divided by 10 at line 124 (`LET K=K/10`). This scales the user-friendly 0–16500 range down to internal units. Notably, line 117 initialises `K=0` and line 118 immediately performs `LET K=K/10` (a no-op on zero), which appears to be a remnant of the input loop structure rather than intentional logic — the division at line 118 is redundant on first entry but harmless. ### Sub-interval Clamping At line 145–150, the time sub-step `S` is clamped so that the fuel consumed (`S*K`) does not exceed the remaining fuel (`M-N`). If there is insufficient fuel for a full step, `S` is shortened to exactly exhaust the remaining fuel, ensuring `M` never undershoots `N`. ### Altitude-Below-Zero Handling Lines 370–395 handle the case where computed altitude `I` goes negative within a sub-step (i.e., the craft has crossed the surface). The code bisects the time step using a velocity/distance estimate (`D = V + SQR(V²+2*A*(G-Z*K/M))`, giving `S = 2*A/D`) and iterates until `S < 5E-3` seconds, at which point it falls through to the landing display at line 205. ### Landing Outcome Branches | Condition (W = impact MPH) | Outcome | | --- | --- | | `W <= 1.2` | Perfect landing — congratulations | | `1.2 <= W <= 10` | Good landing with screen blink (subroutine 5000) | | `10 < W <= 60` | Craft damage, stranded | | `W > 60` | No survivors; crater depth printed as `W*0.277` feet | ### SLOW/FAST Blinking Effects Two places in the program toggle between `SLOW` and `FAST` display modes inside a FOR–NEXT loop to create a visible flicker effect. The crash sequence at lines 310–313 runs 370 iterations; the good-landing blink at lines 5000–5003 runs only 10. This is a well-known ZX81/TS1000 technique for producing attention-grabbing screen animation without any graphics primitives. ### Notable Idioms and Quirks - The display header at line 85 uses inverse-video characters (rendered via `%` escapes) to produce a highlighted column label row for the simulation table. - `TAB` is used in line 120 to align columns: time, altitude (integer miles), fractional feet, MPH, and remaining fuel. - Line 122 checks `L>190` to trigger a `SCROLL`, preventing the table from overflowing the 24-line display — a simple but effective guard. - The `INPUT H$` at line 2022 reads a string and tests `H$(1)` for “Y” or “N”, a standard ZX81 idiom for single-character menu choices. - Lines 5020–5050 (`STOP`, `CLEAR`, `SAVE`, `RUN`) are unreachable utility stubs, likely used for development convenience rather than normal execution. - The power operator `**` is used at line 1005 (`Q**3`), which on the ZX81/TS1000 is equivalent to `^` — an unusual notation sometimes seen in early BASIC ports. ## Source Code ``` 0 REM L.E.M. BY DAVID AHL MODIFIED FOR %TS-1000 BY ANTHONY WILLING 5 SLOW 10 PRINT AT 10,12;"L.E.M." 15 PRINT " (LUNAR EXCURSION MODULE)" 16 FOR Z=1 TO 45 17 NEXT Z 20 FOR Z=1 TO 10 30 SCROLL 40 NEXT Z 50 PRINT AT 4,0;"YOU ARE THE PILOT OF AN APOLLO";" LUNAR LANDING CAPSULE WHOSE" 55 PRINT "ONBOARD COMPUTER HAS FAILED (IT WAS MADE BY TEXAS INSTRUMENTS)." 60 PRINT "TO SURVIVE, YOU MUST LAND THE L.E.M. MANUALLY. SET THE BURN" 65 PRINT "RATE OF THE RETRO-ROCKETS TO ANYVALUE FROM 0 (FREE FALL) TO 16500 POUNDS PER SECOND." 67 FOR Z=0 TO 300 68 NEXT Z 70 PRINT AT 17,10;"%G%O%O%D% %L%U%C%K" 71 FOR Z=1 TO 100 72 NEXT Z 75 CLS 80 LET L=0 85 PRINT "%S%E%C %M%I%+%F%T %M%P%H %L%B%.%F%U%E%L %B%U%R%N" 90 LET A=140 95 LET V=1 100 LET M=33000 105 LET N=16500 110 LET G=1E-03 115 LET Z=1.8 117 LET K=0 118 LET K=K/10 120 PRINT L;TAB 4;INT (A);TAB 8;INT (5280*(A-INT (A)));TAB 13;INT (3600*V);TAB 18;M-N 122 IF L>190 THEN SCROLL 123 INPUT K 124 LET K=K/10 125 LET T=10 130 IF M-N<1E-03 THEN GOTO 185 135 IF T<1E-03 THEN GOTO 120 140 LET S=T 145 IF M>=N+S*K THEN GOTO 155 150 LET S=(M-N)/K 155 GOSUB 1000 160 IF I<=0 THEN GOTO 370 165 IF V<=0 THEN GOTO 175 170 IF J<0 THEN GOTO 400 175 GOSUB 330 180 GOTO 130 185 PRINT "FUEL OUT AT ";L;" SECONDS" 190 LET S=(-V+SQR (V*V+2*A*G))/G 195 LET V=V+G*S 200 LET L=L+S 205 LET W=3600*V 265 CLS 266 PRINT "ON MOON AT ";L;" SECONDS" 270 PRINT " IMPACT VELOCITY ";INT W;" MPH." 271 FOR H=1 TO 150 272 NEXT H 275 IF W<=1.2 THEN PRINT "PERFECT LANDING-CONGRATULATIONS" 280 IF W<=1.2 THEN GOTO 2000 285 IF W>=1.2 AND W<=10 THEN GOSUB 5000 289 IF W>=1.2 AND W<=10 THEN PRINT "GOOD LANDING (A BIT FAST...)" 290 IF W>=1.2 AND W<=10 THEN GOTO 2000 295 IF W>60 THEN GOTO 310 300 PRINT "CRAFT DAMAGE...YOU 'RE STRANDED HERE UNTIL A RESCUE PARTY ARRIVES--HOPE YOU HAVE ENOUGH OXYGEN" 305 GOTO 2000 310 FOR H=1 TO 370 311 SLOW 312 FAST 313 NEXT H 314 SLOW 315 PRINT "SORRY--THERE WERE NO SURVIVORS. YOU BLEW IT" 316 PRINT "IN FACT, YOU BLASTED A NEW CRATER ";W*.277;" FEET DEEP." 320 PRINT "IT WILL, OF COURSE, BE NAMED IN YOUR HONOR..." 325 GOTO 2000 330 LET L=L+S 335 LET T=T-S 340 LET M=M-S*K 350 LET A=I 360 LET V=J 365 RETURN 370 IF S<5E-03 THEN GOTO 205 375 LET D=V+SQR (V*V+2*A*(G-Z*K/M)) 380 LET S=2*A/D 385 GOSUB 1000 390 GOSUB 330 395 GOTO 370 400 LET W=(1-M*G/(Z*K))/2 405 LET S=M*V/(Z*K*(W+SQR (W*W+V/Z)))+.05 410 GOSUB 1000 415 IF I<=0 THEN GOTO 370 420 GOSUB 330 425 IF J>0 THEN GOTO 130 430 IF V>0 THEN GOTO 400 435 GOTO 130 1000 LET Q=S*K/M 1005 LET J=V+G*S+Z*(-Q-Q*Q/2-Q**3/3-Q**4/4-Q**5/5) 1010 LET I=A-G*S*S/2-V*S+Z*S*(Q/2+Q**2/6+Q**3/12+Q**4/20+Q**5/30) 1015 RETURN 2000 FOR H=1 TO 150 2010 NEXT H 2015 CLS 2020 PRINT AT 10,7;"%T%R%Y% %A%G%A%I%N%?% %(%Y%/%N%)" 2022 INPUT H$ 2025 IF H$(1)="N" THEN CLS 2026 IF H$(1)="N" THEN STOP 2030 IF H$(1)="Y" THEN GOTO 50 5000 FOR H=1 TO 10 5001 FAST 5002 SLOW 5003 NEXT H 5005 RETURN 5020 STOP 5030 CLEAR 5040 SAVE "1019%9" 5050 RUN ```