Invaders

Developer(s): Michael Bews
Date: 1983
Type: Program
Platform(s): TS 2068
Tags: Arcade

Invaders is a Space Invaders-style game in which the player moves a rocket launcher across the bottom of the screen and fires upward to eliminate a grid of alien invaders before they descend too far. The program defines eleven UDG (User Defined Graphics) characters via DATA statements to render the invaders, the launcher, the rocket, the laser shields, the command ship, and an explosion sprite. Game state is tracked through a 50-element DIM array I() that marks which invaders have been destroyed, and a scan of this array drives both the win condition and the per-row animation shuffle. Scoring awards 20 points per invader (inferred from the instructions text), 200 points for a command ship direct hit, and deducts 10 points per miss, with a running high score and cumulative total maintained across rounds. Invaders that advance within range of the player become indestructible, adding a time-pressure mechanic.


Program Structure

The program is organized into clearly commented blocks separated by REM statements. Execution begins at line 460 (initialization), flows through an instruction screen, then enters the main game loop at line 50 via GO TO 790GO TO 50. The main loop at line 50 calls two subroutines repeatedly: the invader shuffle routine starting at line 70 (actually implemented from line 100) and the keyboard input routine at line 220. End states branch to line 800 (planet taken over) or line 900 (all invaders destroyed), both converging at line 910 for score display and restart logic.

  • Lines 20–30: Array allocation and jump to init
  • Lines 50–200: Main loop, invader movement, display
  • Lines 220–295: Keyboard handler (move left/right, fast move)
  • Lines 330–380: Rocket fire subroutine
  • Lines 460–790: Initialization, UDG setup, instructions, game setup
  • Lines 800–940: End-of-game handling and restart
  • Lines 1000–1090: UDG DATA
  • Lines 2000–3010: Dead code (debug/test utilities, never called from main flow)

UDG Sprite System

Eleven UDG characters are defined by POKEing pixel data into USR addresses via READ/DATA loops in lines 465–497. The sprites and their roles are:

UDGRoleString variable
S, IInvader body (two halves)I$
A, BRocket launcherT$
C, D, E, F, GCommand ship (5 chars wide)G$
J, K, LLaser shield segmentsD$
HRocket projectileR$
MExplosion spriteM$

The rocket UDG H is defined with seven rows of 24 (binary 00011000, a two-pixel vertical bar) and a final row of 0, giving a clean projectile image.

Invader State Array

The 50-element array I() at line 20 tracks destroyed invaders. The grid is 7 columns × 7 rows (P=7, Q=6), addressed as I(P*row + col). When a rocket hits at row R and column derived from XP, line 370 sets the corresponding element to 1. The win-detection scan at line 100 walks the array looking for any element still equal to U (1 = alive); if none is found it jumps to line 900. A separate scan checks for any non-zero entry to detect FX status.

Movement and Display Logic

The invader shuffle subroutine uses variables L (current column offset, 0–Q), Z (direction, ±1), A (current invader row on screen), and C (step counter). Each call to the subroutine advances L by U (1); when L exceeds Q it resets to 0, reverses Z, increments C, and when C reaches V (5) it clears the top row and advances A downward. The FX flag is used to skip the first display pass so the grid is printed before movement begins.

Line 125 checks L+A>21 to detect invaders reaching the player row, branching to line 800 (game over). The SCREEN$ function at line 350 is used for collision detection on the rocket’s upward path — it scans row by row for a non-space character.

Keyboard Handling

The keyboard routine at lines 220–295 uses INKEY$ for non-blocking input. Movement keys are “5” (left one step) and “8” (right one step). Cursor key codes 8 and 9 (detected via CODE X$) provide fast movement, moving V (5) positions at a time with boundary clamping. The fire key is detected by CODE X$=11 or X$="7", both routing to the rocket subroutine at line 330. A BEEP at line 245 provides audio feedback for any keypress.

Rocket Fire Subroutine

The firing routine at line 330 computes XN=(XP-U+W)*T and checks the result modulo W to determine whether the launcher is positioned behind a shield — if so, firing is suppressed and the routine returns early. This is an unusual shield-blocking mechanic encoded as arithmetic rather than SCREEN$ lookup. The rocket reserve RR is decremented; if it reaches zero, a FLASH warning is shown and no rocket is fired.

Line 350 uses a FOR loop scanning upward from row 18 to row 0, checking SCREEN$ for impact. On a miss, the score is decremented by W (10) and a “MISS!” message flashes. On a hit, line 360 plays an explosion animation using M$ with three BEEP tones in a loop. Line 370 records the hit in the I() array.

Scoring and Persistence

Score SC is updated at lines 360 and 350. The expression 200-(180*SGN R) at line 360 awards 200 points for a hit in row 0 (the top) and 20 for any lower row, since SGN R returns 1 for R>0. High score HS and total score TS persist across restarts (lines 910–915) and are displayed at line 920. On restart, line 940 resets I(), SC, RR, and XP, then returns to line 725 for a fresh game.

Variable Reference Table

VariablePurpose
UConstant 1 (used as step increment and truth value)
VConstant 5 (fast-move step; C rollover threshold)
WConstant 10 (miss score penalty; shield modulus base)
PConstant 7 (number of invader columns)
QConstant 6 (max column offset before direction reverse)
TConstant 2 (char width scale factor)
LCurrent lateral offset of invader grid
ZHorizontal direction (±1)
AVertical row of top invader row on screen
CStep counter for downward advance
XPLauncher horizontal position
RRRemaining rocket count
SC, HS, TSCurrent score, high score, total score
FXFirst-pass flag for invader display

Bugs and Anomalies

  • Line 725 sets Q, T, U, V, and W twice each — the duplicate assignments are harmless redundancy.
  • Line 100 references GO TO 900 (win) and U as a boolean zero/nonzero test; since U=1, comparisons like I(X)=U test for value 1, which is intentional and correct.
  • The REM at line 69 says “INVADER SHUFFLE ROUTINE” but the subroutine entry from GO SUB 70 at line 50 actually begins executing at line 100 (the next line after 70), since line 70 does not exist — this is normal behavior.
  • Lines 2000–3010 are dead code left from development: line 2000 is a keycode inspector and line 3000 is an AT/SCREEN$ tester, neither reachable from the main program.
  • Line 330’s shield-blocking calculation uses XS <>T AND XS <>T AND XS <>T+T — the first two conditions are identical, so only two distinct positions are actually checked rather than three as apparently intended.
  • The instructions text at line 560 contains “ANDTO” (missing space) due to line wrapping in the PRINT statement.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

   10 REM ***INVADERS - ©1983      MICHAEL BEWS
   20 DIM I(50)
   30 GO TO 460
   49 REM ******MAIN PROGRAM LOOP
   50 GO SUB 70:GO SUB 220:GO TO 50
   69 REM INVADER SHUFFLE ROUTINE
  100 FOR X=U TO P:IF I(X+L*P)=U THEN  NEXT X:LET FX=U:FOR X=U TO 49:IF I(X)=U THEN  NEXT X:GO TO 900
  120 IF FX=U THEN  LET FX=0:GO TO 180
  125 IF L+A>21 THEN  GO TO 800
  130 PRINT AT L+A,0;:IF Z=-U THEN  PRINT " ";
  140 FOR X=U TO P:LET B$=I$:IF I(P*L+X)=U THEN  LET B$="  "
  150 PRINT B$;
  160 NEXT X
  180 LET L=L+U:IF L>Q THEN  LET L=0:LET Z=-Z:LET C=C+U:IF C=V THEN  LET C=0:PRINT AT A,0;"                                ";:LET A=A+U
  190 IF (L=0 AND A>0) THEN  PRINT AT 0,0;"                                "; AT 0, INT (27* RND); INK 2;G$
  200 RETURN 
  210 REM **KEYBOARD INPUT****
  220 LET X$= INKEY$
  230 IF X$="" THEN  RETURN 
  240 IF (CODE X$=11 OR X$="7") THEN  GO SUB 330:RETURN 
  245 BEEP .08,-40
  250 IF X$ <>"5" THEN  GO TO 265
  255 IF XP<U THEN  RETURN 
  260 LET XP=XP-U:PRINT AT 20,XP;T$;:RETURN 
  265 IF X$ <>"8" THEN  GO TO 280
  270 IF XP>27 THEN  RETURN 
  275 LET XP=XP+U:PRINT AT 20,XP;T$;:RETURN 
  280 IF CODE X$ <>8 THEN  GO TO 290
  285 PRINT AT 20,XP;"    ";:LET XP=XP-V:IF XP<0 THEN  LET XP=0
  286 PRINT AT 20,XP;T$;:RETURN 
  290 IF CODE X$ <>9 THEN  RETURN 
  292 PRINT AT 20,XP;"    ";:LET XP=XP+V:IF XP>27 THEN  LET XP=27
  295 PRINT AT 20,XP;T$;:RETURN 
  320 REM **ROCKET FIRE******
  330 LET XN=(XP-U+W)*T:LET XS=XN-W* INT (XN/W):IF (XS <>T AND XS <>T AND XS <>T+T) THEN  RETURN 
  332 IF RR<1 THEN  PRINT AT 21,15; FLASH 1;"TAKE COVER!! 0 "; FLASH 0:RETURN 
  334 BEEP .12,35
  335 LET RR=RR-1:PRINT AT 21,30;"  "; AT 21,30;RR;
  340 PRINT AT 19,XP+U;"[UDG-H]";
  350 FOR R=18 TO 0 STEP -U:IF SCREEN$ (R,XP+U)=" " THEN  NEXT R:PRINT AT 19,XP+U;" "; AT 0,XP+U;"[UDG-H]"; AT 21,W; FLASH 1;"MISS!"; FLASH 0:BEEP .2,-15:FOR X=U TO 80:NEXT X:LET SC=SC-W:PRINT AT 21,6;"   "; AT 21,6;SC; TAB W;"   "; AT 0,XP+U;" ":RETURN 
  360 FOR X=1 TO 3:PRINT AT R,XP+U;M$;:BEEP .02,30-(3*X):FOR Y=1 TO 8:NEXT Y:PRINT AT R,XP+U;" ";:FOR Y=U TO V:NEXT Y:NEXT X:PRINT AT R,XP;"  "; AT 19,XP+U;" ";:LET SC=SC+200-(180* SGN R):PRINT AT 21,6;SC;
  370 IF R >=A THEN  LET I(P*(R-A)+U+ INT ((XP+1)/4))=1
  380 RETURN 
  459 STOP 
  460 CLS :BORDER 1:PRINT  PAPER 1; INK 7;"I N V A D E R S ©1982 M.BEWS":PRINT AT 0,1; OVER 1;"______________________________"; OVER 0
  462 PRINT AT 11,5;"I N I T I A L I S I N G"
  465 FOR X=0 TO 7:READ A,B:POKE USR "[UDG-S]"+X,A:POKE USR "[UDG-I]"+X,B:NEXT X:LET I$="[UDG-S][UDG-I]"+" "
  470 FOR X=0 TO 7:READ A,B:POKE USR "[UDG-A]"+X,A:POKE USR "[UDG-B]"+X,B:NEXT X:LET T$=" "+"[UDG-A][UDG-B]"+" "
  480 FOR X=0 TO 7:READ A,B:POKE USR "[UDG-C]"+X,A:POKE USR "[UDG-G]"+X,B:NEXT X:FOR X=0 TO 7:READ A,B:POKE USR "[UDG-D]"+X,A:POKE USR "[UDG-F]"+X,B:NEXT X:FOR X=0 TO 7:READ A:POKE USR "[UDG-E]"+X,A:NEXT X:LET G$="[UDG-C][UDG-D][UDG-E][UDG-F][UDG-G]"
  490 FOR X=0 TO 7:READ A,B,C:POKE USR "[UDG-J]"+X,A:POKE USR "[UDG-K]"+X,B:POKE USR "[UDG-L]"+X,C:NEXT X:LET D$="[UDG-J][UDG-K][UDG-L]"+" "
  495 FOR X=0 TO 6:POKE USR "[UDG-H]"+X,24:NEXT X:POKE USR "[UDG-H]"+7,0:LET R$="[UDG-H]"
  497 FOR X=0 TO 7:READ A:POKE USR "[UDG-M]"+X,A:NEXT X:LET M$="[UDG-M]"
  500 LET RR=60
  510 LET HS=0:LET TS=0
  550 PRINT AT 1,0;"USE YOUR ROCKET LAUNCHER ";T$;" TO WIPE OUT THE INVADERS  ";I$;
  560 PRINT " ANDTO DAMAGE THE COMMAND SHIP WHICH ARRIVES LATER ";G$;
  570 PRINT :PRINT "YOU CAN NOT FIRE WHILE YOU ARE  CHR$ EHIND A LASER SHIELD  ";D$
  590 PRINT :PRINT "USE LEFT AND RIGHT CURSOR ARROWSTO MOVE YOUR LAUNCHER (with caps shift to move faster).  USE THE  'UP' ARROW TO FIRE ROCKETS."
  600 PRINT TAB 3;"YOU HAVE ONLY ";RR+1;" ROCKETS."
  610 PRINT "****************"
  620 PRINT "SCORE  20  FOR EACH KNOCKED OUT INVADER AND  200  FOR  DIRECT HIT ON THE COMMAND SHIP."
  700 PRINT "LOSE  10  POINTS FOR EACH MISS!!"
  710 PRINT :PRINT " IF INVADERS GET CLOSE TO YOU  THEY WILL BECOME INDESTRUCTABLE!"
  720 PRINT TAB 2; FLASH 1;"{PRESS ANY KEY TO START GAME}"; FLASH 0
  722 IF INKEY$="" THEN  GO TO 722
  725 LET A=0:LET C=0:LET FX=0:LET L=0:LET P=7:LET Q=6:LET T=2:LET U=1:LET V=5:LET W=10:LET Q=6:LET T=2:LET U=1:LET V=5:LET W=10:LET SC=0:LET XP=0:LET Z=1
  730 BORDER 5:PAPER 5:CLS :FOR X=0 TO 6:PRINT AT X,2;:FOR Y=1 TO 7:PRINT  INK 1;I$;:NEXT Y:NEXT X
  750 PRINT AT 19,0;:FOR X=1 TO 6:PRINT  INK 3;D$;:NEXT X:PRINT  INK 3;"[UDG-L][UDG-K]";
  760 PRINT AT 20,0;T$;
  770 PRINT AT 21,0;"SCORE:";SC; AT 21,15;"ROCKET RESERVE:";RR;
  790 GO TO 50
  799 REM ***END OF LOST GAME
  800 PRINT AT 5,0; PAPER 2; INK 7;"YOUR PLANET HAS BEEN TAKEN OVER!!!.                                   YOU SCORED ";SC;" USING ";60-RR;" ROCKETS";:GO TO 910
  900 PRINT AT 5,0; PAPER 1; INK 7;"YOU HAVE WIPED OUT THE INVADERS,"; AT 7,0;"SCORED ";SC;" AND USED ";60-RR;" ROCKETS"; AT 9,1; FLASH 1;"C O N G R A T U L A T I O N S"; FLASH 0
  910 IF SC>HS THEN  LET HS=SC
  915 LET TS=TS+SC
  920 PRINT AT 11,12; PAPER 6; INK 2;"LAST SCORE    ";SC; AT 12,12;"HIGHEST SCORE ";HS; AT 13,12;"TOTAL SCORE  ";TS; AT 15,2; FLASH 1;"PRESS ANY KEY FOR NEXT GAME"
  930 LET X$= INKEY$:IF X$="" THEN  GO TO 930
  940 CLS :PRINT AT 11,12; INK 2; PAPER 6; FLASH 1;"STANDBY"; FLASH 0:FOR X=5 TO 1 STEP -1:PRINT AT 17,15;X;:FOR Y=1 TO 80:NEXT y:NEXT X:FOR X=1 TO 49:LET I(X)=0:NEXT X:LET SC=0:LET RR=60:LET XP=0:GO TO 725
 1000 REM ****USR DEF GRAPHICS
 1030 DATA 60,60,140,49,115,206,51,204,63,252,111,246,193,131,192,3
 1040 DATA 27,224,31,240,26,88,62,124,255,255,255,255,85,170,42,84
 1050 DATA 0,0,0,0,7,224,31,248,248,31,127,254,0,0,0,0
 1060 DATA 0,0,63,252,240,15,191,253,0,0,255,255,127,254,0,0
 1070 DATA 126,255,0,195,255,129,255,60
 1080 DATA 255,255,255,126,0,126,255,255,255,126,0,126,255,255,255,126,0,126,255,255,255,0,0,0
 1090 DATA 0,82,24,124,62,24,74,0
 2000 LET X$= INKEY$:IF X$="" THEN  GO TO 2000
 2010 PRINT CODE X$;" ";:GO TO 2000
 3000 INPUT A,B,C$:PRINT AT 18,30;" "; AT A,B;C$;:IF SCREEN$ (4,4)="0" THEN  PRINT AT 18,30;"*";
 3010 GO TO 3000

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

Scroll to Top