--- title: "Moon Lander" id: 54515 type: "computer_media" slug: "moonlander" url: "http://localhost/computer_media/moonlander/" markdown_url: "http://localhost/computer_media/moonlander.md" published_at: "2024-06-02T16:11:59+00:00" modified_at: "2026-03-30T21:44:08+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kios.png" excerpt: "Guide a lunar lander to a safe touchdown using thrust and rotation controls, with machine code sprites and collision detection driving the action." 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 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 51212 title: "CATS Library Tape 1" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-1/" - id: 54965 title: "ISTUG Public Domain Library 5" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-5/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kios.png" - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kiew.png" - url: "http://localhost/wp-content/uploads/2024/06/SCR-20240616-kikd.png" media_type_tags: "Arcade, Game" --- # Moon Lander This program implements a Lunar Excursion Module (LEM) landing simulator called “Moon Lander,” written by Peter Cooke. The player controls thrust (keys 1/2) and rotation angle (keys 0/9) through eight discrete orientations to land on a marked pad. The game relies heavily on machine code routines loaded from a separate CODE block at address 40000, called via RANDOMIZE USR to handle sprite drawing, collision detection (returning a value from USR), and explosion/landing effects. Physics are computed in BASIC using trigonometric functions: horizontal and vertical velocity components are updated each frame using COS and SIN of the angle scaled to 2*PI/8 radians per step. The program saves itself as a two-part tape file—a BASIC autostart loader at line 620 and the machine code block “moonland C” at 40000, 2768 bytes long. *** ## Program Analysis ### Program Structure The program is organized into a main game loop and several subroutines. Execution is divided broadly as follows: 1. **Lines 10–70:** Title screen — flashing “STOP THE TAPE” banner using alternating INK/PAPER colors, waiting for a keypress via `RANDOMIZE USR 26727` (a fast halt/keypress wait routine) and `INKEY$`. 2. **Lines 80–90:** Display instructions (GO SUB 370) and initialize the game screen and variables (GO SUB 520, GO SUB 400). 3. **Lines 100–290:** Main game loop — reads input, updates physics, POKEs sprite positions into machine code data areas, calls USR routines for rendering and collision detection. 4. **Lines 300–360:** Landing/crash evaluation and restart logic. 5. **Lines 370–390:** Instructions screen subroutine. 6. **Lines 400–510:** Screen setup — draws border, lunar terrain using PLOT/DRAW arcs, initializes machine code sprite tables, prints HUD. 7. **Lines 520–610:** Variable initialization subroutine. 8. **Lines 620–630:** Loader/saver block — loads the CODE file on first run; saves both BASIC and CODE parts to tape. ### Machine Code Integration The game depends entirely on a 2768-byte machine code block loaded at address 40000. BASIC calls it repeatedly via `RANDOMIZE USR` at several entry points: | Address | Purpose | | --- | --- | | `26727` | Fast keypress wait / halt loop (ROM utility) | | `40000` | Erase old sprite at previous position | | `40145` | Draw sprite at new position / collision detect (returns 0 if collision) | | `40300` | Explosion animation frame | | `40400` | Terrain painter / fill routine (called with POKEd parameters) | | `40451` | Landing pad drawing routine (yellow pad) | | `40550` | Crash/explosion sequence frame | Sprite position data is communicated to the machine code by POKEing `x` and `y` coordinates into fixed addresses. The current position is stored at `40183` (x) and `40165` (y), while the previous position (for erasure) is held at `40038` and `40020`. The rotation angle is communicated via `40006` (current) and `40151` (previous), enabling the machine code to draw the correctly rotated LEM sprite. ### Physics Model The LEM uses a simple Euler-integration physics model. The angle is quantized into 8 directions (0–7), and thrust is decomposed into X and Y velocity increments: - `sx = sx + 0.03 - 0.01 * thrust * COS(2*PI*angle/8)` — horizontal velocity with constant rightward drift (simulating gravity-free horizontal drift) - `sy = sy + 0.01 * thrust * SIN(2*PI*angle/8)` — vertical velocity (no gravitational constant is added, so “gravity” is implicitly the absence of upward thrust) The 0.03 constant in the X update provides a persistent rightward screen drift, simulating orbital motion rather than planetary gravity. The lack of a downward gravity term means the lander coasts freely unless thrust is applied, which is an unconventional but coherent model for a top-down or side-scrolling orbital context. ### Collision and Landing Detection Collision detection is performed by the machine code at 40145, which returns 0 in the USR value when a collision with terrain is detected. Line 290 checks `IF v=0 THEN GO TO 100` to continue if no collision. Line 300 then evaluates safe-landing criteria: - `(INT x) = 48` — LEM must be over the pad (X position check) - `y` between 170 and 204 — within vertical landing zone - `sx <= 0.3` — horizontal speed within safe limit - `sy <= 0.2` — vertical speed within safe limit If any condition fails, the crash sequence runs: it cycles through explosion frames (addresses 40300 and 40000) for angles 8–11, then plays a descending BEEP sequence, and prints “CRASH !!!!”. ### Notable BASIC Techniques - **Color cycling on title screen:** Lines 20–40 swap INK and PAPER values using a temporary variable `c` to alternate two colors across 22 rows, creating a striped effect without a lookup table. - **Fuel-linked audio:** Line 100 uses `BEEP 0.05, 0.05 + (12 AND fuel<100)`. The expression `12 AND fuel<100` evaluates to 12 (a high pitch) when fuel is critical and 0 otherwise, giving an audio warning. - **PAUSE NOT PI:** Used in lines 310, 340, 390 as a wait-for-keypress idiom. `NOT PI` evaluates to 0, so `PAUSE 0` waits indefinitely until a key is pressed. - **Variable initialization compaction:** In subroutine 520–610, variables are initialized by chaining assignments: `LET y=x`, `LET angle=thrust`, `LET sy=sx` — all set to the same value without redundant literals. - **OVER 1 screen clear:** Line 320 uses `OVER 1` with INK 7 and PAPER 0 to XOR-erase previously drawn graphics from rows 0–18, effectively clearing the play area without `CLS`. - **Terrain drawing with arcs:** Lines 420–430 use the Spectrum’s `DRAW x,y,angle` arc form to render curved mountain terrain, a technique requiring careful precalculation of arc parameters. ### Two-Part Tape Loading Line 620 is the autostart loader: it clears memory below 40000, prints a loading message, loads the CODE block “moonland C”, sets system variables to point to the machine code area (POKEing 23675/23676 to set the RAMTOP area), and then `RUN`s from line 10. Line 630 saves both parts: the BASIC program with `LINE 620` autostart and the 2768-byte machine code block starting at 40000. ### Potential Anomalies - Line 450 is a `REM`-commented POKE/USR block that was evidently an earlier or alternative terrain-fill call, left in as a development artifact. - The `REM` on line 1 contains what appears to be loader/development notes embedded as a REM string, a common practice for embedding metadata or copy-protection hints in BASIC programs. - The landing X-position check `(INT x)<>48` uses integer truncation rather than rounding, which could cause asymmetric pad detection depending on approach direction. ## Source Code ``` 1 REM USE LIST 2d RETURN :H\\ NEW o AND g GO SUB i{= CLEAR GO SUB a{= CLEAR LIST \ RETURN :H\\ NEW o AND g GO SUB i{= CLEAR GO SUB a{= CLEAR LIST <>oo 10 BORDER 5: PAPER 5: LET a=2: LET b=7: CLS 20 FOR i=0 TO 21 30 PRINT AT i,8; INK a; PAPER b;" STOP THE TAPE " 40 LET c=a: LET a=b: LET b=c: NEXT i 50 PRINT #1;AT 1,0; INK 9;" PRESS ANY KEY " 60 RANDOMIZE USR 26727 70 IF INKEY$="" THEN GO TO 60 80 GO SUB 370 90 GO SUB 520: GO SUB 400 100 LET z$=INKEY$: BEEP 0.05,.05+(12 AND fuel<100) 110 LET angle=angle+(z$="0")-(z$="9") 120 IF angle=8 THEN LET angle=0 130 IF angle=-1 THEN LET angle=7 140 POKE 40006,PEEK 40151: POKE 40151,angle 150 LET thrust=thrust+(z$="1")-(z$="2" AND thrust>0) 160 IF fuel<=0 THEN LET fuel=0: LET thrust=0 170 LET sx=sx+.03-.01*thrust*COS (2*PI*angle/8) 180 LET sy=sy+.01*thrust*SIN (2*PI*angle/8) 190 LET x=x+sx 200 LET y=y+sy 210 POKE 40038,PEEK 40183 220 POKE 40020,PEEK 40165 230 POKE 40183,x 240 POKE 40165,y 250 LET fuel=fuel-thrust 260 PRINT AT 20,2; PAPER 1;fuel;" ";AT 20,9;INT (sx*10);" ";AT 20,15;INT (sy*10);" ";AT 20,21;thrust;" " 270 LET v=USR 40000 280 LET v=USR 40145 290 IF v=0 THEN GO TO 100 300 IF (INT x)<>48 OR y<170 OR y>204 OR sx>.3 OR sy>.2 THEN LET v=USR 40145: FOR n=8 TO 11: POKE 40006,n: LET v=USR 40000: LET v=USR 40300: LET v=USR 40000: NEXT n: FOR n=1 TO 21: LET v=USR 40550: BEEP 0.02,21-n: NEXT n: PRINT #1;AT 0,0; INK 2; PAPER 6;" CRASH !!!! Press a key ": PAUSE NOT PI: GO TO 320 310 PRINT #1;AT 0,0; INK 2; PAPER 6;" ******* SAFE LANDING ******* ": PAUSE NOT PI 320 FOR n=0 TO 18: PRINT AT n,0; OVER 1; PAPER 0; INK 7,,: NEXT n 330 IF INKEY$<>"" THEN GO TO 330 340 PRINT #1;AT 0,0;" Q to QUIT or ANY to play again ": PAUSE NOT PI 350 IF INKEY$="Q" OR INKEY$="q" THEN STOP 360 GO SUB 520: INPUT "": GO SUB 470: GO TO 100 370 BORDER 0: PAPER 0: INK 9: CLS : PRINT AT 0,0;" *** M O O N L A N D E R *** "''" By PETER COOKE" 380 PRINT AT 5,0;"Use the keys "'"9 turn left"'"0 turn right"'"1 increase thrust"'"2 decrease thrust"''"land your L.E.M. on the pad (in yellow)." 390 PRINT AT 20,0;" press a key to start ": PAUSE NOT PI: RETURN 400 PAPER 0: INK 7: BORDER 0: CLS : FOR n=19 TO 21: PRINT AT n,0; PAPER 1,,: NEXT n 410 PLOT 0,0: DRAW 255,0: DRAW 0,175: DRAW -255,0: DRAW 0,-175 420 PLOT 30,175: DRAW 70,-110,.9: DRAW 30,70,.9: DRAW 60,30,-1: DRAW 65,-60,-1.3 430 PLOT 1,160: DRAW 100,-130,1.3: DRAW 70,90,1: DRAW 40,0: DRAW 44,-60,1 440 POKE 40452,31: POKE 40454,074: RANDOMIZE USR 40451 450 REM POKE 40401,51: POKE 40403,175: RANDOMIZE USR 40400 460 POKE 40401,1: POKE 40403,25: RANDOMIZE USR 40400 470 PRINT AT 19,2; PAPER 1;"fuel hsi vsi thrust" 480 PRINT AT 20,2; PAPER 1;fuel;" ";AT 20,9;sx;" ";AT 20,15;sy;" ";AT 20,21;thrust;" " 490 RANDOMIZE USR 40145 500 PRINT AT 7,21; INK 6; OVER 1;" " 510 RETURN 520 LET x=2: LET y=x 530 LET fuel=2000 540 LET thrust=0 550 LET angle=thrust 560 LET sx=0: LET sy=sx 570 POKE 40006,angle 580 POKE 40151,angle 590 POKE 40020,y: POKE 40165,y 600 POKE 40038,x: POKE 40183,x 610 RETURN 620 CLEAR 39999: PRINT INK 9;AT 10,2;" HANG ABOUT - STILL LOADING ": LOAD "moonland C"CODE : POKE 23676,166: POKE 23675,104: RUN 630 SAVE "Moonland" LINE 620: BEEP .2,22: SAVE "moonland C"CODE 40000,2768: BEEP .2,22 ```