Earth Attack is a side-scrolling shoot-’em-up in which the player pilots a ship across a cityscape, firing lasers at enemy craft and deploying smart bombs while protecting a set of shield units. The game relies heavily on machine code routines POKEd into RAM from DATA statements, covering functions including side-scrolling, collision/firing detection, smart-bomb blasts, and city building. Six UDG characters (A–F) are defined for the ship, enemies, refueling station, city blocks, and explosion graphics. Laser ammunition is tracked numerically and displayed on-screen, with score bonuses awarded for hits and penalties for enemy collisions; a high-score entry routine prompts the player for three initials when a new record is set. The attract-mode loop cycles through a title screen, a scoring guide, and a high-score table before waiting for the “S” keypress to start a game.
Program Structure
The program is organized into three broad phases: initialization (lines 20–50), the attract/menu loop (lines 1140–1400), and the main game (lines 80–690). Subroutines are clearly demarcated with REM labels and occupy lines 280–690 for game logic and lines 700–1130 for setup.
- Lines 20–50: Hardware setup — screen POKEs, beeps, and calls to UDG and machine-code loading subroutines.
- Lines 60–70: Variable initialization, then jump to attract loop at 1140.
- Lines 80–270: Main game loop — player movement, enemy spawning, collision, and firing.
- Lines 280–460: Firing, hit detection, life loss, and game-over logic.
- Lines 470–550: Smart-bomb subroutine with shield management.
- Lines 560–680: High-score name entry.
- Lines 700–790: UDG definition from DATA.
- Lines 800–1130: Machine code loading from DATA into RAM.
- Lines 1140–1400: Attract loop (title, scoring guide, high-score table).
Machine Code Usage
The game’s performance-critical routines are implemented as Z80 machine code, loaded from DATA statements into fixed RAM addresses. Five distinct routines are identified:
| Label | Start Address | Purpose |
|---|---|---|
| Smart Bomb | 62400 (approx) | Clears enemies from screen area |
| Side Scroll | 62426 (approx) | Scrolls the playfield one step; called via USR 62426 |
| Build City | ~62450 | Draws city graphics at game start |
| Check Firing | 62507 (approx) | Traces laser beam, returns hit count in A register |
| Misc routines | 62300, 62350 | Loaded via p1/p2 pairs at lines 1040–1100 |
Machine code is loaded using a generalized loader subroutine at line 1110–1130: p1 holds the byte count and p2 the destination address; the loop READs and POKEs each byte. The value -12, -18, -4 etc. appearing in the DATA are signed relative jump offsets for Z80 DJNZ and JR instructions. A variable a appears in several DATA lines (e.g. lines 730–780) where the literal value 0 is expected; this is likely a bug — a will have whatever numeric value it held at READ time rather than the intended constant.
UDG Definitions
Six UDGs (A–F) are defined at lines 700–790 by POKEing 8 bytes each starting at USR "[UDG-A]". The loop structure has a minor anomaly: line 710 ends with NEXT p and line 720 also has a stray NEXT p, which would cause an error in most interpreters — this appears to be a listing artifact or typo. The UDG characters represent:
- [UDG-A] — Player ship
- [UDG-B] — Enemy craft (10 points)
- [UDG-C] — Refueling station
- [UDG-D] — Shield/city block
- [UDG-E] — City pattern (inverse stripes)
- [UDG-F] — Bonus enemy (20 points)
Key BASIC Idioms
Line 240 uses a conditional string expression to select which enemy graphic to print based on rg (a random value), using the Spectrum’s string AND idiom: ("string1" AND condition)+("string2" AND NOT condition) — when a boolean condition is true it returns 1 (string kept) or 0 (empty string). This selects between UDG-C and a colored UDG-F character inline without branching.
Line 200 uses a double-PEEK to read a two-byte system variable: PEEK (PEEK 23684 + 256 * PEEK 23685) retrieves the attribute of the character under the player sprite for collision detection via the ATTR system variable pointer.
The IN keyword is used extensively (lines 190, 210, 230, 260) to read the keyboard matrix ports directly, which is faster than INKEY$ and allows multiple keys to be detected each frame. Port values 64510, 63486, 61438, 32766, 65278 correspond to specific keyboard half-rows.
Line 440 uses PAUSE PI — since PI ≈ 3.14159 and PAUSE takes an integer argument, this effectively pauses for 3/50 of a second, a compact way to insert a very brief delay.
Game Logic and Scoring
Laser count (l) starts at 20 and decreases by 1 per shot. A pickup (attribute value 60, detected at line 360) grants 20 extra lasers. The shield system uses variable sb (starting at 3), decremented by smart bomb use; shields regenerate every 1000 score points via the bo (bonus) counter. Score is modified by machine code return values multiplied by 10 in lines 330 and 500.
Attract Loop
The attract sequence at lines 1140–1400 cycles through three screens — title/controls, scoring guide, high-score table — each waiting up to a PAUSE period for the “s” keypress. The title screen animates the game title in all seven INK colors (line 1199 loop). The scoring guide at lines 1270–1300 prints item descriptions character-by-character with a BEEP per character for a teletype effect.
Notable Anomalies
- Variable
aused as a literal0in DATA statements (lines 730–1000): BASIC’sREADwill evaluateaas a numeric variable, so its value depends on prior computation — this is almost certainly a typographical error for the digit0. - The double
NEXT pat lines 710–720 in the UDG loader would cause a NEXT without FOR error on a second pass; the inner FOR loop at line 700 usesp, so the extra NEXT is spurious. - Line 690 contains a bare
STOPstatement that is never reached in normal execution; it serves as a safety barrier between subroutines. - Line 260 calls
USR 62426twice if a specific key is held, effectively double-scrolling — this appears intentional for the SHIFT “extra speed” feature mentioned in the instructions.
Content
Source Code
10 REM >> EARTH ATTACK << © ZX COMPUTING, Oct/Nov 1983by David Mold, TS 2068 versionby Alvin Albrecht
20 POKE 23730,190:POKE 23731,243:BEEP .5,12:BEEP .5,24:CLS :PRINT AT 10,8;"STOP THE TAPE!!!"
30 GO SUB 700
40 GO SUB 800
50 BORDER 0
60 LET s$="...":LET s=0:LET t=s:LET hs=s:LET bo=s
70 GO TO 1140
80 PAPER 0:INK 7:CLS :FOR n=0 TO 20:BEEP .005,n:NEXT n
90 LET x=9:LET y=5:LET b=1
100 FOR n=0 TO 7:PRINT PAPER 1; AT n,0,,:NEXT n
110 LET s=0
120 PRINT AT 20,0;"SCORE:";s
130 LET a$="[UDG-A] [UDG-A] [UDG-A] [UDG-A] ":PRINT AT 20,15;a$
140 FOR n=16 TO 19:PRINT AT n,0; PAPER 5,,:NEXT n
150 LET l=20
160 PRINT AT 0,0;"LASERS:";l;" "
170 PRINT AT 0,16;"HI-SCORE:";hs
180 PRINT AT 18,15; INK 2; PAPER 5;"[UDG-D][UDG-D][UDG-D]":LET sb=3
190 LET x=x+(IN 64510<31)-(IN 63486<31):PRINT AT x,y;"[UDG-A]";
200 LET p= PEEK (PEEK 23684+256* PEEK 23685):IF p THEN GO SUB 360
210 IF IN 61438<31 THEN IF l THEN GO SUB 280
220 IF RND>.79 THEN PRINT AT RND*5+8,31; INK 6;"[UDG-B]"
230 IF IN 32766<31 THEN GO SUB 470
240 LET rg= RND:IF rg>.96 THEN PRINT AT 15-b,31; INK 4;("[UDG-C]" AND rg>.98)+(CHR$ 16+ CHR$ 6+"[UDG-F]" AND rg <=.98):BEEP .005,10:BEEP .005,20
250 LET b= INT (RND*3)+1
260 PRINT AT x,y;" ":POKE 62477,b:LET a= USR 62426:IF IN 65278<31 THEN LET a= USR 62426
270 GO TO 190
280 REM >>Firing Subroutine<<
290 LET l=l-1
300 BEEP .005,30:LET a= USR 62507:IF a THEN BEEP .01,7:BEEP .01,0:GO TO 330
310 PRINT AT 0,7;l;" "
320 RETURN
330 LET s=s+10*a
340 PRINT AT 20,6;s;" "; AT 0,7;l;" "
350 RETURN
360 IF p=60 THEN :LET l=l+20:BEEP .01,20:GO TO 340
370 FOR f=1 TO 4:BEEP .01,f*2:NEXT f:FOR f=4 TO 1 STEP -1:BEEP .01,f*2:NEXT f
380 PRINT AT x,y; OVER 1;"#":BEEP .01,0:BEEP .01,-.5:BEEP .01,-1
390 IF a$="!" THEN GO TO 440
400 LET a$=a$(3 TO ):IF a$="" THEN LET a$="!"
410 LET s=s-10:PRINT AT 20,15;a$;" "
420 PRINT AT x,y; INK 1;"█":LET a= USR 62426
430 LET x=9:BEEP .3,0:PRINT AT 20,6;s;" ":RETURN
440 PRINT AT 4,10;"GAME OVER":FOR n=1 TO 8:LET a= USR 62300:BEEP .1,n:NEXT n:PAUSE PI:IF hs<s THEN LET hs=s:GO SUB 560
450 GO TO 1140
460 GO TO 90
470 LET sb=sb-1
480 IF INT (s/1000)>bo THEN LET sb=3:PRINT AT 18,15; INK 2; PAPER 5;"[UDG-D][UDG-D][UDG-D]"
490 IF sb<0 THEN RETURN
500 LET s=s+10* USR 62400:BEEP .5,7:BEEP .5,0:PRINT AT 20,6;s;" ":PRINT AT 18,15+sb; INK 5;"█"
510 IF INT (s/1000)>bo THEN LET sb=3:PRINT AT 18,15; INK 2; PAPER 5;"[UDG-D][UDG-D][UDG-D]"
520 LET bo= INT (s/1000)
530 FOR n=0 TO 31:POKE 62477, INT (RND*3)+1:LET a= USR 62426
540 PRINT AT x,y-1; PAPER 8;" [UDG-A]":BEEP .01,n
550 NEXT n:RETURN
560 PAPER 0:INK 6:CLS
570 PRINT " EARTH ATTACK"
580 LET a$="WELL DONE, YOU HAVE BEATEN THE HIGH SCORE: PLEASE ENTER YOUR INITIALS NOW"
590 PRINT '''
600 FOR n=1 TO LEN a$:PRINT a$(n);:BEEP .003,0:NEXT n
610 LET s$=""
620 PRINT ''" ---"; CHR$ 8; CHR$ 8; CHR$ 8;
630 FOR n=1 TO 3
640 PRINT "?";
650 PAUSE 0:LET p$= INKEY$:IF LEN p$ <>1 THEN GO TO 650
660 PRINT CHR$ 8;p$;:LET s$=s$+p$
670 BEEP .1,n:NEXT n
680 PAUSE 50:RETURN
690 STOP
700 FOR p=0 TO 47:READ a
710 POKE USR "[UDG-A]"+p,a:NEXT p
720 NEXT p
730 DATA 224,48,248,127,a,248,48,224
740 DATA 24,36,126,129,a,126,0,a
750 DATA 60,66,129,66,60,a,a,a
760 DATA 0,64,92,126,a,92,64,0
770 DATA 255,153,a,255,153,a,255,a
780 DATA 93,42,54,127,85,a,20,54
790 RETURN
800 REM >>SMART BOMB<<
810 DATA 33,255,88,14,0,6,255,35,126,254
820 DATA 6,32,7,12,62,8,119,16,-12,201
830 DATA 198,16,119,16,-18,201
840 REM >>SIDE SCROLL<<
850 DATA 6,64,33,0,72,17,32,0,62,0
860 DATA 119,25,16,252,33,0,89,6,8,17
870 DATA 32,0,62,7,119,25,16,-4,1,0
880 DATA 8,17,0,72,33,1,72,237,176,1
890 DATA 0,1,17,0,89,33,1,89,237,176
900 REM >>BUILD CITY<<
910 DATA 6,0,14,15,62,22,215,121,215,62
920 DATA 31,215,62,16,215,62,5,215,62,17
930 DATA 215,62,0,215,62,148,215,13,16,-26
940 DATA 201
950 REM >>CHECK FIRING<<
960 DATA 14,0,42,132,92,17,0,3,25,229,6,5,126,254,129,40,16,254,127,40,11,54,255,35,16,-14,118,118,118,118,24,47,12,197,237,82
970 DATA 229,205,142,243
980 DATA 0,66,36,24,24,36,66,0,118,118,225
990 DATA 229,205,142,243,153,90,16,199,227,8,90,153,118,a,225
1000 DATA 205,142,243,0,a,a,a,a,a,a
1010 DATA 0,193,12
1020 DATA 225,62,5,54,0,35,4,184,32,-7,6,0,201
1030 REM >> CODE DATA <<
1040 LET p1=199:LET p2=62399
1050 GO SUB 1110
1060 DATA 62,7,33,0,89,6,255,119,35,16,-4,118,61,32,-13,6,1,33,255,79,14,32,175,126,203,63,119,43,13,32,-8,62,71,188,32,-16,16,-21,201
1070 LET p1=39:LET p2=62299
1080 GO SUB 1110
1090 DATA 6,8,209,26,19,213,119,17,0,1,25,16,-11,201
1100 LET p1=14:LET p2=62349
1110 FOR n=1 TO p1
1120 READ a:POKE p2+n,a:NEXT n
1130 RETURN
1140 INK 7:PAPER 0:CLS
1150 LET a$=" DAVID MOLD "
1160 FOR n=1 TO LEN a$
1170 PRINT a$(n);
1180 NEXT n
1190 FOR n=0 TO 7:INK n:PRINT AT 3,9;"EARTH ATTACK":BEEP .1,n
1200 LET a$=" ":PRINT '"Use Keys:"'a$;"1-5......Up"''a$;"Q-T......Down"''a$;"6-0......Fire"''a$;"SHIFT....Extra Speed"''a$;"SPACE....Smart Bombs"
1210 PRINT '"PRESS 'S' TO START"
1220 NEXT n
1230 PAUSE 250
1240 IF INKEY$="s" THEN GO TO 80
1250 CLS
1260 PRINT "EARTH ATTACK"''''
1270 LET a$= CHR$ 13+ CHR$ 13:LET a$="[UDG-B]........10 POINTS"+a$+"[UDG-F]........20 POINTS"+a$+"[UDG-C]........REFUELLING Stn"+a$+a$+"PRESS 'S' TO START"
1280 FOR n=1 TO LEN a$
1290 PRINT a$(n);:BEEP .003,0:NEXT n
1300 PAUSE 250:IF INKEY$="s" THEN GO TO 80
1310 CLS
1320 PRINT "EARTH ATTACK"
1330 PRINT "------------"
1340 PAUSE 50
1350 PRINT '''"HIGH SCORE=";hs:BEEP .1,10:PAUSE 50
1360 PRINT '"LAST SCORE=";s:BEEP .1,9:PAUSE 50
1370 PRINT ''"HIGH SCORE BY ";s$:BEEP .1,8
1380 PRINT '''''"PRESS 'S' TO START"
1390 PAUSE 200:IF INKEY$="s" THEN GO TO 80
1400 GO TO 1140
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
