The program is highly structured. This (hopefully) makes it very east to read and adapt. As it stands, the game is not particularly playable. This is due to a number of things:
1 Lack of high scores and sound effects.
2 Lack of originality.
3 Lack of speed.
Lack of sound can be overcome by using the sound routine in section 13.1, when in assembler. A high score routine is very easy to write, and even a table of high scores should present no real problem.
The second problem with the game is that it is unexciting. This is because thinking of new ideas is probably as difficult as writing a game, which is why most games are copies of others.
The final problem can be overcome by either writing the entire program in assembler or writing the BASIC sections in optimised BASIC (single-letter integer variables, no spaces, multi-line statements, etc).
The first of these methods is undoubtedly the better of the two. If all of the program is written in assembler, the speed problem disappears, and you will find that you can probably handle about twenty shapes before the program slows down to the same speed as the original BASIC one. Also, writing the program in assembler means that you can preserve the structure, and, because there is no speed disadvantage in using long variable names, the program can be largely self-commenting.
The game has been written in such a way that it can be easily converted into assembler. All the variables are treated as integers and none of them has a range which exceeds a single byte. In addition, none of the procedures has more than two parameters and any parameters passed to them are only single bytes. This means that the procedures can be replaced by subroutines and the parameters can be passed in the registers.
The game has been listed mainly in BASIC rather than assembler because the assembler version would take up double the amount of room. Converting it has been left as an exercise for the reader.
Now for the listing:
0 REM Demonstration Game For Book 10 20 REM Space Invader (!!) 30 +-----------------------------------------------------+ | Top level of program | +-----------------------------------------------------+ 40 MODE2:VDU23;8202;0;0;0; 50 PROCinitialise 60 PROCscore(0) 70 PROClives(0) 80 REPEAT 90 PROCgame 100 PROClives(-1) 110 UNTIL lives=0 120 *FX15 130 END +-----------------------------------------------------+ | Initialise all variables, assemble the machine-code | | and plot the alien and the base. | +-----------------------------------------------------+ 140 150 DEFPROCinitialise 160 DIM code 500 170 PROCassemble(code,2) 180 alien=0 190 base=1 200 bomb=2 210 bullet=3 220 alienX=40 230 alienY=128 240 baseX=40 250 baseY=20 260 bulletflag=FALSE 270 bombflag=FALSE 280 lives=3 290 PROCplot(alien,alienX,alienY) 300 PROCplot(base,baseX,baseY) 310 ENDPROC 320 +-----------------------------------------------------+ | Play the game, until the player is dead. | +-----------------------------------------------------+ 330 DEFPROCgame 340 REPEAT 350 PROCmovealien 360 PROCmovebase 370 PROCmovebomb 380 PROCmovebullet 390 IF FNdeadalien THEN PROCkillalien:PROCscore(10) :PROCnewalien 400 UNTIL FNdead 410 ENDPROC 420 +-----------------------------------------------------+ | Wipe the alien shape, update its coordinates, and | | plot the alien back up again. Also give it a chance| | to launch a bomb | +-----------------------------------------------------+ 430 DEFPROCmovealien 440 PROCplot(alien,alienX,alienY) 450 alienX=alienX+RND(5)-3 460 alienY=alienY+RND(5)-3 470 PROCplot(alien,alienX,alienY) 480 IF RND(5) = 1 THEN PROCdropbomb(alienX+2,alienY-14) 490 ENDPROC +-----------------------------------------------------+ | Get keys from the keyboard to move the base and fire| | bullets. The keys are 'Z' and 'X' to move left and | | right, and 'RETURN' to fire a bullet. Note that | | only one bullet may be in the air at once. | +-----------------------------------------------------+ 500 510 DEFPROCmovebase 520 PROCplot(base,baseX,baseY) 530 baseX=baseX+INKEY-98-INKEY-67 540 PROCplot(base,baseX,baseY) 550 IF INKEY-74 THEN PROCfirebullet(baseX+1,baseY+8) 560 ENDPROC 570 +-----------------------------------------------------+ | Launch a bomb, by setting 'bombflag' to true, and | | initialising its coordinates. Also plot the bomb. | +-----------------------------------------------------+ 590 IF bombflag THEN ENDPROC 600 bombflag=TRUE 610 bombX=X 620 bombY=Y 630 PROCplot(bomb,bombX,bombY) 640 ENDPROC 650 +-----------------------------------------------------+ | Launch a bullet, in much the same way as the bomb | +-----------------------------------------------------+ 660 DEFPROCfirebullet(X,Y) 670 IF bulletflag THEN ENDPROC 680 bulletflag=TRUE 690 bulletX=X 700 bulletY=Y 710 PROCplot(bullet,bulletX,bulletY) 720 ENDPROC 730 +-----------------------------------------------------+ | Update the bomb's position, and kill it off if it | | hits the ground. | +-----------------------------------------------------+ 740 DEFPROCmovebomb 750 IF bombflag=FALSE THEN ENDPROC 760 PROCplot(bomb,bombX,bombY) 770 bombY=bombY-4 780 IF bombY<=8 THEN bombflag=FALSE:ENDPROC 790 PROCplot(bomb,bombX,bombY) 800 ENDPROC 810 +-----------------------------------------------------+ | Update the bullet's position, and stop the bullet if| | it hits the ceiling | +-----------------------------------------------------+ 820 DEFPROCmovebullet 830 IF bulletflag=FALSE THEN ENDPROC 840 PROCplot(bullet,bulletX,bulletY) 850 bulletY=bulletY+6 860 IF bulletY >= 248 THEN bulletflag=FALSE : ENDPROC 870 PROCplot(bullet,bulletX,bulletY) 880 ENDPROC 890 +-----------------------------------------------------+ | Check if player's bullet has hit the alien | +-----------------------------------------------------+ 900 DEFFNdeadalien 910 IF bulletflag THEN IF ABS(bulletY-alienY)<5 AND ABS(bulletX-alienX)<5 THEN PROCplot(bullet, bulletX,bulletY):bulletflag=FALSE: =TRUE 920 = FALSE 930 +-----------------------------------------------------+ | If alien is dead, then knock the shape off the | | screen. | +-----------------------------------------------------+ 940 DEFPROCkillalien 950 PROCplot(alien,alienX,alienY) 960 ENDPROC 970 +-----------------------------------------------------+ | Adjust the score, and display it | +-----------------------------------------------------+ 980 DEFPROCscore(increment) 990 score=score+increment 1000 PRINTCHR$(30);"Score :";score; 1010 ENDPROC 1020 +-----------------------------------------------------+ | Do much the same with the lives. | +-----------------------------------------------------+ 1030 DEFPROClives(increment) 1040 lives=lives+increment 1050 PRINTTAB(12,0);"Lives :";lives 1060 ENDPROC 1070 +-----------------------------------------------------+ | Initialise an alien. | +-----------------------------------------------------+ 1080 DEFPROCnewalien 1090 alienX=40 1100 alienY=128 1110 PROCplot(alien,alienX,alienY) 1120 ENDPROC 1130 +-----------------------------------------------------+ | Check if alien's bomb has hit player's base. | +-----------------------------------------------------+ 1140 DEFFNdead 1150 IF bombflag THEN IF ABS(bombX-baseX)<4 AND ABS(bombY-baseY)<8 THEN bombflag=FALSE :P ROCplot(bomb,bombX,bombY): =TRUE 1160 =FALSE 1170 +-----------------------------------------------------+ | Nice interface for BASIC to the machine-code plotter| +-----------------------------------------------------+ 1180 DEFPROCplot(A%,X%,Y%) 1190 CALL plotshape 1200 ENDPROC 1210 +-----------------------------------------------------+ | Assemble the plotter routine. BASIC I users | | should convert all EQUB's and EQUW's to OPT FNequb's| | and OPT FNequw's | +-----------------------------------------------------+ 1220 DEFPROCassemble(origin,maxpass) 1230 P%=&70 1240 [OPT 0 1250 .addr 1260 EQUW 0 1270 .top 1280 EQUW &3000 1290 .rowcounter 1300 EQUB 0 1310 .counter 1320 EQUB 0 1330 .temp 1340 EQUB 0 1350 .temp1 1360 EQUB 0 1370 .depth 1380 EQUB 0 1390 .shape 1400 EQUW 0 1410 .offset 1420 EQUB 0 1430 ] +-----------------------------------------------------+ | This sets up the shapes. This means that, if you | | are using cassettes, the shapes must come AFTER this| | program, or, alternatively, they may be on another | | cassette. Also, if on cassette, you should put a | | 'CLS' at, say, line 1585, so that the tape messages | | are not left on the screen, when the game starts | +-----------------------------------------------------+ 1440 DIM shapeloaddr 3, shapehiaddr 3, shapesize 3, shapedepth 3 1450 DIM shapes 300 1460 FOR I%=0TO3 1470 READfilename$ 1480 OSCLI("LOAD "+filename$+" "+STR$~shapes) 1490 I%?shapeloaddr=shapes AND&FF 1500 I%?shapehiaddr=shapes DIV256 1510 READ I%?shapesize 1520 READ I%?shapedepth 1530 shapes=shapes+I%?shapedepth*I%?shapesize 1540 NEXT 1550 DATA alien, 5, 14 1560 DATA base, 4, 20 1570 DATA bomb, 2, 8 1580 DATA bullet, 2, 8 1590 FOR pass =0 TO maxpass STEP maxpass 1600 P%=origin 1610 [OPT pass +-------------------------------------------------------+ | See section 13.2 for details on the following routines| +-------------------------------------------------------+ 1620.getaddr 1630 LDA #&00 Set the high byte of addr 1640 STA addr+1 to 0 1650 TYA Invert the Y coordinate 1660 EOR #&FF and save on the stack 1670 PHA 1680 LSR A Divide Y coordinate by 8 1690 LSR A 1700 LSR A 1710 TAY and leave in Y 1720 LSR A Adjust carry for * &80 1730 STA temp Save Y/16 in temp 1740 LDA #&00 Set bottom byte of addr to 0 1750 ROR A Put carry into top bit 1760 ADC top and add in top of screen 1770 PHP Save carry flag 1780 STA addr Store result in addr 1790 TYA Get Y/8 1800 ASL A Double it for top byte 1810 ADC temp of addr. Add in Y/16 1820 PLP Restore carry 1830 ADC top+1 and add in top of screen 1840 STA addr+1 1850 LDA #&00 Set temp to 0 1860 STA temp 1870 TXA Get X coordinate 1880 ASL A Perform 2-byte multiplication 1890 ROL temp of temp by 8, because of 1900 ASL A the memory map 1910 ROL temp 1920 ASL A 1930 ROL temp 1940 ADC addr Add in rest of result to 1950 STA addr far, and store it 1960 LDA temp 1970 ADC addr+1 1980 BPL ok Check for hardware scroll 1990 SEC If over 3000-8000 boundary 2000 SBC #&50 then correct address 2010.ok 2020 STA addr+1 And store it 2030 PLA Restore inverted y coord 2040 AND #&07 Get row number in computed 2050 ORA addr column, and add it in 2060 STA addr 2070 RTS Return 2080 2090.plotshape 2100 PHA Save shape number 2110 JSR getaddr Get address 2120 PLA Restore shape 2130 TAY 2140 LDA shapeloaddr, Y Set up parameters 2150 STA shape This assumes that 2160 LDA shapehiaddr, Y the User has set 2170 STA shape + 1 up the relevent 2180 LDA shapesize, Y tables 2190 STA counter (shapeloaddr, 2200 LDA shapedepth, Y shapesize, etc) 2210 STA depth 2220 \fall through to 'doplot' 2230.doplot 2240 LDA addr+1 If highbyte is 0 2250 BEQ return then return 2260 .label 2270 LDY #&00 Set shape offset to 0 2280 LDA addr+1 Push screen address onto 2290 PHA stack, for later use 2300 LDA addr 2310 PHA 2320 LDA depth Get depth of shape 2330 STA rowcounter 2340 LDA addr Put offset in character 2350 AND #&07 cell into Y 2360 STA offset 2370 LDA addr Adjust addr accordingly 2380 AND #&F8 Go to top of character 2390 STA addr cell 2400.innerloop 2410 LDA (shape),Y Get byte from shape 2420 INY 2430 STY temp 2440 LDY offset 2450 EOR (addr),Y 2460 STA (addr),Y 2470 INY Y holds offset on screen 2480 CPY #&08 Bottom of character cell? 2490 BEQ block If so, then go down a line 2500.noblock 2510 STY offset 2520 LDY temp 2530 DEC rowcounter 2540 BNE innerloop 2550.nextblock 2560 LDA shape 2570 CLC 2580 ADC depth 2590 STA shape 2600 BCC nohi 2610 INC shape+1 2620.nohi 2630 CLC Go to top of next column, 2640 PLA by resetting address to top 2650 ADC #&08 of current column, and 2660 STA addr moving to next character 2670 PLA cell 2680 ADC #&00 2690 BPL nobound1 2700 SEC 2710 SBC #&50 2720.nobound1 2730 STA addr+1 2740 DEC counter Easier to DEC counter in 2750 BNE label two places and test 2760.return 2770 RTS 2780.block 2790 LDY #&00 Go down a line 2800 LDA addr addr=addr+&280 2810 CLC 2820 ADC #&80 2830 STA addr 2840 LDA addr+1 2850 ADC #&02 2860 BPL noboundary If contents of addr 2870 SEC greater than &8000, then 2880 SBC #&50 subtract &5000 2890 .noboundary 2900 STA addr+1 2910 BNE noblock Always jump 2920 2930] 2940NEXTpass 2950 ENDPROC
See inset colour illustrations for shape design.