--- title: "Invaders" id: 70798 type: "computer_media" slug: "invaders-3" url: "http://localhost/computer_media/invaders-3/" markdown_url: "http://localhost/computer_media/invaders-3.md" published_at: "2026-08-23T15:08:11+00:00" modified_at: "2026-08-23T17:54:45+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/invaders-1.png" alt: "Invaders screen" excerpt: "A full Space Invaders clone with UDG sprites, laser shields, a command ship, limited rockets, and persistent high-score tracking across multiple rounds." 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/" indiv: - name: "Michael Bews" slug: "michael-bews" taxonomy: "indiv" url: "http://localhost/indiv/michael-bews/" genre: - name: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" media_type: "Program" programmers: - name: "Michael Bews" slug: "michael-bews" taxonomy: "indiv" url: "http://localhost/indiv/michael-bews/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Invaders%20%281983%29%28Bews%2C%20Michael%29%28TS2068%29%28UK%29%28Program%29.zip" mediadate: "1983" images: - url: "http://localhost/wp-content/uploads/2026/08/invaders-1.png" alt: "Invaders screen" - url: "http://localhost/wp-content/uploads/2026/08/Invaders-2.png" alt: "Invaders screen" media_type_tags: "Arcade" --- # Invaders 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 790` → `GO 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: | UDG | Role | String variable | | --- | --- | --- | | S, I | Invader body (two halves) | `I$` | | A, B | Rocket launcher | `T$` | | C, D, E, F, G | Command ship (5 chars wide) | `G$` | | J, K, L | Laser shield segments | `D$` | | H | Rocket projectile | `R$` | | M | Explosion sprite | `M$` | 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 | Variable | Purpose | | --- | --- | | `U` | Constant 1 (used as step increment and truth value) | | `V` | Constant 5 (fast-move step; C rollover threshold) | | `W` | Constant 10 (miss score penalty; shield modulus base) | | `P` | Constant 7 (number of invader columns) | | `Q` | Constant 6 (max column offset before direction reverse) | | `T` | Constant 2 (char width scale factor) | | `L` | Current lateral offset of invader grid | | `Z` | Horizontal direction (±1) | | `A` | Vertical row of top invader row on screen | | `C` | Step counter for downward advance | | `XP` | Launcher horizontal position | | `RR` | Remaining rocket count | | `SC`, `HS`, `TS` | Current score, high score, total score | | `FX` | First-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. ## 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"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 ```