Moon Lander

This file is part of CATS Library Tape 1, and ISTUG Public Domain Library 5. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Arcade, Game

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:

AddressPurpose
26727Fast keypress wait / halt loop (ROM utility)
40000Erase old sprite at previous position
40145Draw sprite at new position / collision detect (returns 0 if collision)
40300Explosion animation frame
40400Terrain painter / fill routine (called with POKEd parameters)
40451Landing pad drawing routine (yellow pad)
40550Crash/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 RUNs 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.

Content

Appears On

From Blackjack to Star Trek, Moon Lander to a first-person dungeon crawler — this tape packs 21 games and puzzles into one of CATS' earliest library volumes. Card sharks, arcade fans, and puzzle solvers alike will find something here.
Explore a first-person 3D maze, watch an audio oscilloscope in real time, draw pixel art with ten drawing modes, or generate random surreal poetry — ISTUG Library 5 is a sprawling collection that covers every corner of the TS 2068 experience.

Related Products

Related Articles

Related Content

Image Gallery

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

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

People

No people associated with this content.

Scroll to Top