Moon Lander

This file is part of ISTUG Public Domain Library 6, and SINCUS Exchange Tape 101 - Entertainment. Download the collection to get this file.
Type: Program
Platform(s): TS 2068
Tags: Arcade, Game

Moon Lander is a joystick-controlled lunar landing game in which the player pilots a lander across multiple scrolling moonscapes, touching down on marked landing pads without crashing or running out of fuel. The game uses 21 user-defined graphics (UDGs “a” through “u”) to render the lander in four orientations plus detailed explosion debris, all loaded from DATA statements via POKE USR. Five distinct moonscape sections are stored as PLOT/DRAW coordinate sequences in separate DATA blocks (lines 9530–9580), with RESTORE used to select the active terrain and scroll the world horizontally when the lander crosses screen boundaries. The TS2068 STICK function (| operator) is polled directly for joystick input, and SOUND commands provide engine thrust and crash effects throughout gameplay.


Program Analysis

Program Structure

The program is organized into clearly delineated sections, loosely following a game-loop architecture:

  1. Lines 1–2: Title screen and key-press wait.
  2. Lines 10–75: Initialization — variables, starfield, intro animation, pad setup, terrain draw, HUD render.
  3. Lines 100–410: Main game loop — input polling, physics update, display refresh, boundary checks.
  4. Lines 500–580: Scenery change (horizontal scroll) when lander descends to ground level.
  5. Lines 600–696: Successful landing logic.
  6. Lines 700–795: Screen-edge boundary crossing (left/right wrap between terrain sections).
  7. Lines 800–860: Crash sequence with animated explosion.
  8. Lines 870–895: Game over screen.
  9. Lines 900–960: Level complete / out-of-fuel handling.
  10. Lines 9000–9270: UDG loader subroutine and DATA.
  11. Lines 9500–9580: Terrain drawing subroutine and five moonscape DATA sets.

User-Defined Graphics

Subroutine 9000 loads 21 UDGs (“a” through “u”) by iterating over character strings read from DATA, using POKE USR p$+n,a for each of the 8 bytes per character. UDGs “a”–”d” form the four rotational orientations of the lander sprite. UDGs “e” onward define explosion fragments and debris pieces, referenced in the crash animation at lines 820–849.

Terrain System

Five moonscape sections are encoded as PLOT/DRAW coordinate sequences in DATA lines 9530–9580. The variable ls (landscape select) holds the line number of the active DATA block; RESTORE ls at lines 520, 740, and 790 resets the DATA pointer to the correct set. The subroutine at line 9500 reads a starting PLOT coordinate then executes 26 relative DRAW commands to render the terrain. Each DATA line beyond the terrain definition is padded with zeros to keep the READ count consistent.

Horizontal Scrolling

When the lander reaches column 30 (right edge), lines 700–750 increment ls by 10 and wrap it from 9590 back to 9540. When the lander goes below column 0 (left edge), lines 760–795 decrement ls by 10 with a corresponding wrap. In both cases, the entry column for the lander on the new screen is computed with a Boolean-arithmetic chain, e.g.:

LET column=(ls=9540)+8*(ls=9550)+13*(ls=9560)+18*(ls=9570)+22*(ls=9580)

This evaluates each equality to 1 (true) or 0 (false) and multiplies by the desired column offset — a compact alternative to IF/THEN chains.

Physics and Input

Vertical speed is held in vs and horizontal drift in drift. The STICK function (|(2,2) for fire, |(1,2) for up/down, |(2,2)=4/8 etc.) is polled each frame at lines 150–195. Key assignments:

  • Fire button (|(2,2)=1): main thruster — decelerates downward velocity via down, deducts fuel, triggers BEEP.
  • Left/right stick (|(2,2)=4/8 read via |(1,2) at line 160): increments/decrements gs (graphic select), cycling through UDGs 144–147 for the four lander orientations.
  • Lines 150/170/180 bound gs to the range 144–147 with wraparound.

Line 145 advances the lander vertically only if the cell ahead is not terrain color 132 (cyan/green), providing collision detection against the moonscape:

LET line=line+lv*SGN vs*(ATTR (line+SGN vs,column)<>132)

Landing Pad Tracking

The array c(30) is used as a visited-pad registry. On a successful landing at line 690, c(line+column) is set to 1. If the lander attempts to land on the same pad a second time, line 630 detects the flag and branches to the “RESTRICTED LANDING PAD” message at line 640. The variable pad counts successful landings; reaching 5 triggers the level-complete sequence at line 900.

Crash Animation

The crash sequence (lines 810–848) loops three times (FOR x=0 TO 2), printing and then erasing groups of explosion UDGs at offsets relative to the impact point. Flash attribute and Paper 8 (transparent/contrast) are used during the burst. A SOUND statement at line 831 plays a percussive crash effect on the TS2068 AY chip.

Intro Animation

Line 37 runs a 15-step loop that combines SOUND, INK, BORDER, FLASH, CIRCLE, and PRINT to produce a pulsing launch animation before gameplay begins. The SOUND calls use multiple channel parameters (channels 7–13) per statement, exploiting the TS2068’s AY-3-8912 envelope registers directly.

Notable Bugs and Anomalies

  • Line 36 initializes DIM c(30), but the index used is line+column, which can potentially exceed 30 if both are large (e.g., line=19, column=15 → index 34), causing an error. In practice, the ranges constrain this, but it is fragile.
  • Line 540 computes h (the landing pad row) and line 550 computes d (the landing pad column) using the same Boolean-arithmetic style; however, the boundary comparisons in lines 510 and 540/550 use slightly inconsistent column ranges, so the pad marker position could drift one column off in edge cases.
  • Line 9040 contains BIN 1010000 (only 7 bits written without leading zero) — this evaluates correctly as 80 decimal in BASIC but is unconventional and could cause confusion when editing.
  • Lines 892–895 form a polling loop waiting for the joystick-up release before restarting, which prevents accidental double-starts.

Variable Summary

VariableRole
lvLevel multiplier for vertical speed (starts 0.5, increments each level)
scScore accumulator
zShips remaining (lives)
vsVertical speed display value
hsHorizontal speed display value
fFuel remaining
lsActive landscape DATA line pointer
gsCurrent lander UDG graphic index (144–147)
driftHorizontal drift accumulator
downThrust state flag
padCount of successfully landed pads
c(30)Array flagging already-used landing pads
h, dLanding pad row and column on current screen
sSub-screen scroll state flag

Content

Appears On

Library tape of the Indiana Sinclair Timex User’s Group.

Related Products

Related Articles

Related Content

Image Gallery

Moon Lander

Source Code

    1 INK 3: PRINT AT 4,0;"    MOON LANDER"''''"Fire button is jet propulsion."'"Joystick left & right to rotate."''"Land on pads. too fast you crash"''"Watch out for green meteors"''"Land on all for next level."
    2 PRINT "Press a key to start": PAUSE 0
   10 REM SetupVariables
   20 PAPER 0: BORDER 0: CLS : INK 6
   30 LET lv=.5: LET sc=0: LET z=3: LET l=0: LET vs=30: LET hs=21
   35 FOR j=10 TO 85: PLOT j*RND*3,j*RND*2: NEXT j: FOR x=1 TO 6: INK x+2: CIRCLE (x*RND)*40+6,(x*RND)*25+10,x: NEXT x: GO SUB 9000
   36 LET pad=0: LET f=3000: DIM c(30)
   37 FOR x=0 TO 14: SOUND 7,14;8,2;9,x;10,x: INK x/2: BORDER 0:: SOUND 0,100+x;3,x;5,15: PRINT AT 21-x,3+x; INK 6; FLASH 1;"\m"; FLASH 0; INK 7;"\c": CIRCLE 200,140,x*2: PRINT AT 21-x,3+x;"  ": NEXT x: CLS 
   38 IF |(2,2)=1 THEN SOUND 6,6;7,0;8,15;9,16;10,16;12,56;13,8: PAUSE 2: SOUND 8,0;9,0;10,0
   40 LET ls=9530: LET h=0: LET d=0: LET line=2: LET column=10: LET drift=3: LET gs=147: LET down=4: LET s=0
   60 GO SUB 9500
   65: FOR j=40 TO 85: INK 8: PLOT j*RND*3,j*RND*1+80: INK 6: NEXT j: CIRCLE 200,140,10:
   70 PRINT AT 14,4; FLASH 1;"x";AT 20,9;"x";AT 17,15;"x";AT 15,21;"x";AT 19,24;"x"
   75 FOR x=0 TO 21: FOR Y=0 TO 21 STEP 21: PRINT AT Y,0; PAPER 3; INK 0; FLASH 1;"\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''\..\''";AT X,0;"\''";AT X,31;"\.."; FLASH 0: NEXT Y: NEXT x: PRINT #0;AT 1,0;; INK 2;"v/speed:"; FLASH 1; PAPER 6;"\.'"; FLASH 0; INK 2;vs; FLASH 1; INK 5*RND; PAPER 6;"\.'\'.\.'\. \''\ :\'.\n\'.";AT 1,20; FLASH 0; INK 2; PAPER 5;"h/speed: ";AT 1,28;hs;" ";AT 0,21; INK 8;"\d\d\::Ships:";AT 0,31;z;#0; INVERSE 1; INK 2;AT 0,0;"fuel: ";f; FLASH 1;"\'.\'.\n\'.\''\'.\j\'.\k\'.\'."
  100 REM  run program
  110 FLASH 0: BORDER 0: INK 7
  115: PRINT #0;AT 1,0;; INK 7;"v/speed:"; FLASH 1; PAPER 1;"\.'"; FLASH 0;vs; FLASH 1; INK 5*RND;"\.'\'.\.'\. \''\ :\'.\n\'.";AT 1,20; FLASH 0; INK 1; PAPER 7;"h/speed: ";AT 1,28;hs;" ";AT 0,21;"\d\d\::Ships:";AT 0,31;z;#0;AT 0,0; PAPER 1; INK 7;"fuel: ";f; FLASH 1;"\'.\'.\n\'.\''\'.\j\'.\k\'.\'."
  116 IF INT line=h-2 AND (INT column=d OR INT (column+.5)=d) THEN GO TO 600
  119 IF |(2,2)=1 AND gs=147 THEN PRINT AT line+1,column;"\p": PAUSE 2: PRINT AT line+1,column;" "
  120 PRINT AT line,column;CHR$ gs
  121 IF |(2,2)=1 AND gs=145 THEN PRINT AT line-1,column;"\p": PAUSE 2: PRINT AT line-1,column;" "
  122 IF |(2,2)=1 AND gs=146 THEN PRINT AT line,column-1;"\p": PAUSE 2: PRINT AT line,column-1;" "
  123 IF |(2,2)=1 AND gs=144 THEN PRINT AT line,column+1;"\p": PAUSE 2: PRINT AT line,column+1;" "
  124 IF |(2,2)=1 AND gs=147 THEN PRINT AT line+1,column;"\p": PAUSE 2: PRINT AT line+1,column;" "
  125 IF ATTR (line+SGN vs,column+SGN drift)=4 OR ATTR (line,column+SGN drift)=4 THEN GO TO 800
  126 FOR x=1 TO RND*2
  127 PRINT AT x,(30*RND)-column; INK 7*RND;"  \k      \h     \g\h \r\g  "
  128 NEXT x
  130 LET pc=column: LET pl=line
  140 LET column=column+SGN drift
  141 IF |(2,2)=1 THEN SOUND 6,6;7,0;8,15;9,16;10,16;12,200;13,8:
  142 SOUND 0,100;2,150;5,5;7,28;8,column;9,column;10,10
  145 LET line=line+lv*SGN vs*(ATTR (line+SGN vs,column)<>132)
  150 LET drift=drift+(|(2,2)=1 AND gs=146)-(|(2,2)=1 AND gs=144)
  155 LET down=SGN (down+(|(2,2)=1 AND gs=145)-(|(2,2)=1 AND gs=147)-down*(INKEY$=""))
  160 LET gs=gs+(|(1,2)=4)-(|(1,2)=8)
  170 IF gs=148 THEN LET gs=144
  180 IF gs=143 THEN LET gs=147
  190 IF |(2,2)=1 THEN LET f=f-10
  195 IF |(2,2)=1 THEN BEEP .08,20
  200 IF column>=30 THEN GO TO 700
  210 IF column>=0 AND column<1 THEN GO TO 760
  220 IF line=1 THEN LET line=2
  230 LET vs=vs+5+15*down: LET hs=drift*7
  235 IF s<>0 THEN GO TO 250
  240 IF line>=12 AND s=0 THEN GO TO 505
  250 IF s=1 AND line<=2 THEN RESTORE 9530: LET column=(column/3.1)+22*(ls=9580)+19*(ls=9570)+11*(ls=9560)+6*(ls=9550): LET ls=9530: CLS : INK 4: LET line=11: LET s=0: GO TO 60
  270 IF l=4 THEN BEEP .08,40
  271 IF l=6 THEN BEEP .12,42: LET l=0
  275 LET l=l+1
  280 IF line<=2 THEN LET line=2
  290 IF f=0 THEN GO TO 950
  400 PRINT AT pl,pc;" "
  410 GO TO 100
  500 REM change scenery
  505 LET column=INT column
  510 LET ls=9530+10*(column>0 AND column<7)+20*(column>6 AND column<13)+30*(column>12 AND column<18)+40*(column>17 AND column<22)+50*(column>21 AND column<31)
  520 CLS : INK 4: LET line=2: RESTORE ls
  530 GO SUB 9500
  540 LET h=6*(column>0 AND column<7)+21*(column>6 AND column<13)+16*(column>12 AND column<18)+9*(column>17 AND column<22)+21*(column>21 AND column<31)
  550 LET d=12*(column>0 AND column<7)+9*(column>6 AND column<13)+12*(column>12 AND column<18)+16*(column>17 AND column<22)+6*(column>21 AND column<31)
  560 PRINT AT h,d; FLASH 1;"x"
  565 LET column=column-6*(column>21)-5*(column>17)-5*(column>12)-6*(column>6)
  570 LET column=column*3.1: LET s=1
  580 GO TO 100
  600 REM  landing
  620 IF gs<>147 OR vs>10 THEN GO TO 800
  630 IF c(line+column)<>1 THEN GO TO 680
  640 PRINT AT 21,0;"RESTRICTED LANDING PAD. GO AWAY."
  650 FOR i=1 TO 500
  660 IF INKEY$="9" THEN LET h=0: GO TO 100
  670 NEXT i: GO TO 800
  680 PRINT AT 10,7;"GREAT LANDING";AT 11,6;"CONGRATULATIONS!"
  690 LET c(line+column)=1: FOR i=1 TO 4: FOR j=0 TO 10: BEEP .08,j: NEXT j: NEXT i: RESTORE 9530: CLS : INK 4
  693 LET sc=sc+100
  695 LET pad=pad+1: IF pad=5 THEN GO TO 900
  696 GO TO 40
  700 REM crossing boundries
  710 IF ls=9530 THEN LET column=1: GO TO 400
  720 LET ls=ls+10
  730 IF ls=9590 THEN LET ls=9540
  740 CLS : RESTORE ls: INK 4
  750 LET column=(ls=9540)+8*(ls=9550)+13*(ls=9560)+18*(ls=9570)+22*(ls=9580): GO TO 530
  760 IF ls=9530 THEN LET column=29: GO TO 400
  770 LET ls=ls-10
  780 IF ls=9530 THEN LET ls=9580
  790 CLS : RESTORE ls: INK 4
  795 LET column=6*(ls=9540)+12*(ls=9550)+17*(ls=9560)+21*(ls=9570)+29*(ls=9580): GO TO 530
  800 REM crash landing
  803 LET z=z-1: BEEP .08,0: BEEP .08,15: BEEP .08,3: PAUSE 80
  810 FOR x=0 TO 2
  815 FLASH 1: PAPER 8: INK 6
  820 PRINT AT line-x,column-x;"\g\h\i";AT line,column-1;"\j\k\m";AT line+x,column-x;"\o\p\q"
  821 PRINT AT line-x,column-x;"\m\o\p";AT line-(7+x),column-x;"\k\l\r";AT line-(5+x),column-x;"\g\u\i\l"
  828 PRINT AT line-(9+x),column-(4+x);"\g\k  \l";AT line-(8+x),column-(2+x);"\k \l \r";AT line-(6+x),column+x;"\g \u\i  \l"
  831: SOUND 6,6;7,7;8,16;9,16;10,16;12,250;13,8:
  840 PRINT AT line-x,column-x;"   ";AT line,column-1;"   ";AT line+x,column-x;"   "
  841 PRINT AT line-x,column-x;"   ";AT line-(7+x),column-x;"   ";AT line-(5+x),column-x;"    "
  848 PRINT AT line-(9+x),column-(4+x);"     ";AT line-(8+x),column-(2+x);"     ";AT line-(6+x),column+x;"       "
  849 PRINT AT line-(9+x),column-(4+x);"\g\k  \l";AT line-(8+x),column-(2+x);"\k \l \r";AT line-(6+x),column+x;"\g \u\i  \l"
  850 NEXT x: PAUSE 60: INK 7: PAPER 0: BORDER 0: CLS : IF z=0 THEN GO TO 870
  860 FLASH 0: RESTORE 9530: CLS : INK 4: GO TO 40
  870 PRINT AT 2,10;"Your Score: ";sc
  875 PRINT AT 10,0;"            GAME OVER           ";AT 11,3;"Push Joystick up to start"
  892 IF |(1,2)=1 THEN CLEAR : CLS : FLASH 0: GO TO 10
  895 IF |(1,2)=0 THEN GO TO 892
  900 REM Finish Game
  905 LET sc=sc+f
  910 CLS : INK 7: PRINT AT 5,1;"Well done , you have landed on all the pads with a score of ";AT 8,12; FLASH 1;sc
  920 IF z=0 THEN PRINT AT 12,3;"Push stickup to start": IF |(1,2)=1 THEN RUN 
  930 LET z=z+1: LET lv=lv+.5
  940 PAUSE 200: CLS : INK 4: GO TO 36
  950 PRINT AT 10,7;"OUT OF FUEL": LET z=0: PAUSE 200
  960 GO TO 870
 9000 REM lander graphics
 9009 FOR m=1 TO 21: READ p$
 9010 FOR n=0 TO 7: READ a: POKE USR p$+n,a
 9011 NEXT n: NEXT m: RETURN 
 9020 DATA "a",BIN 00000101,BIN 00001111,BIN 01101101,BIN 11111100,BIN 11111100,BIN 01101101,BIN 00001111,BIN 00000101
 9030 DATA "b",BIN 11100111,BIN 01000010,255,BIN 01111110,BIN 00011000,BIN 00111100,BIN 00111100,BIN 00011000
 9040 DATA "c",BIN 1010000,BIN 11110000,BIN 10110110,BIN 00111111,BIN 00111111,BIN 10110110,BIN 11110000,BIN 10100000
 9050 DATA "d",BIN 00011000,BIN 00111100,BIN 00111100,BIN 00011000,BIN 01111110,255,BIN 01000010,BIN 11100111
 9110 DATA "e",0,24,40,40,16,0,0,0
 9120 DATA "f",112,136,136,68,40,48,0,0
 9130 DATA "g",0,0,0,56,44,16,0,0
 9140 DATA "h",0,0,30,18,34,84,232,0
 9150 DATA "i",94,177,130,228,34,65,66,60
 9160 DATA "j",0,62,65,66,34,17,14,0
 9170 DATA "k",96,144,144,116,10,49,65,126
 9180 DATA "l",62,65,242,9,247,136,144,96
 9190 DATA "m",145,82,16,7,244,8,74,137
 9200 DATA "n",100,24,198,0,222,0,24,102
 9210 DATA "o",0,4,34,18,1,68,50,9
 9220 DATA "p",9,50,68,1,18,34,4,0
 9230 DATA "q",0,8,170,42,73,65,137,137
 9240 DATA "r",144,76,34,128,72,68,32,0
 9250 DATA "s",0,3,15,15,120,200,255,56
 9260 DATA "t",0,192,240,240,30,19,255,28
 9270 DATA "u",0,32,68,72,128,36,76,144
 9500 REM Moonscapes
 9510 READ x,y: PLOT x,y
 9520 FOR n=0 TO 25: READ h,v: DRAW h,v: NEXT n
 9530 DATA 0,31,16,0,16,32,8,0,8,8,16,-48,8,-8,8,0,8,16,16,8,8,16,8,-16,8,0,2,8,2,0,4,8,16,16,8,-24,8,8,8,0,16,-32,8,0,16,40,8,-8,16,-8,8,16,6,-8
 9540 DATA 0,31,48,0,48,96,24,0,24,24,48,-128,24,-16,24,0,8,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 9550 DATA 0,153,48,-128,22,-18,24,0,24,48,48,24,24,48,24,-48,24,0,6,12,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 9560 DATA 0,31,48,24,24,48,22,-56,24,0,6,12,6,0,6,24,48,48,24,-72,24,24,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 9570 DATA 0,55,6,24,6,0,12,24,48,48,24,-72,24,24,24,0,48,-96,24,0,32,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 9580 DATA 0,103,40,-96,24,0,56,120,24,-24,48,-24,24,48,32,-24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 9999 RETURN 

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

People

No people associated with this content.

Scroll to Top