Version for the Atari by Charles Brannon with revisions by Thomas A. Marshall.
In your corner of the universe, a zone of high-pressure radioactive plasma is contained by a platinum-iridium wall. Your ship, immersed in the red zone, is charged with a vital duty: defend the wall. The vengeful enemies of your civilization send wave after wave of attack ships in an effort to breach the wall. These semismart robot ships will concentrate their firepower on your weakest spot and mercilessly try to fire their way into the wall.
Your only defense is your powerful particle beam which you use to fend off the attacking drones. The enemy ships are wary of your power, so if you move too close to an attack point, you can spook the enemy ship into picking another target. Move to shoot at the new position, and it will just cruise back to another vulnerable spot. You must not let the enemy blast a hole in the wall since, like a balloon stuck with a pin, the radioactive plasma will explode, reducing your ship to an expanding shell of iridescent particles.
As the laser gunner, you try to react quickly to your enemy's shots. Follow the ship as well as you can, but do not stray too far from a weak spot. When you destroy one ship, another will appear at a random position, and will home in on a vulnerable spot in the wall.
For a game written in BASIC, "Laser Gunner" is reasonably fast and smooth. The smoothness of motion comes from player/missile graphics, but the speed comes from an unusual technique that lets you move player/missile graphics at machine language speed.
A special graphics technique is used here. Instead of storing the player/missile graphics at the top of memory, a large string is dimensioned to hold the player/missile data. When a string is dimensioned, a block of memory is reserved for it. The starting address of the string can be determined by using the ADR function. The problem is that player/missile graphics must start on an even 1K boundary (the address must be a multiple of 1024), or a 2K boundary (divisible by 2048) for single-resolution player/missile graphics. Strings are given the next available address when dimensioned, which would only be on an even kilobyte address by sheer coincidence.
So when the ADdRess of the string is determined, we must what offset to add to the address to reach the next boundary. It can be shown that in worst case conditions (i.e., the address is just one byte past a 1K or 2K boundary), we must allow for an offset of at least 1023 bytes for double-resolution, or 2048 bytes for single-resolution P/M graphics. So, although double-resolution P/M graphics require only 1024 bytes, we must dimension the holding string at least 2048 bytes. Then, a simple calculation (lines 150-160) will give us the starting address within the string of the base address, PMBASE. This value is then used to "set up" P/M as usual.
The advantage of using a string is twofold: one, we know that BASIC is covetously protecting the string from the "RAMTOP Dragon" (see COMPUTE!'s Second Book of Atari Graphics) and other nasties. Second, we can use BASIC's fast string manipulation commands to move segments of strings around, scroll a string, erase a string, copy one string to another, more. Since the memory being moved in the string is the P/M memory, these manipulations directly modify the players and missiles. And since these string operations internally proceed at language speed, we get fast P/M animation using BASIC. Although the code is not as straightforward as dedicated P/M commands such as PMMOVE or PMGRAPHICS, it sure beats cryptic USR statements. As a matter of fact, since BASIC permits such flexibility with strings, it may be the best solution to using P/M graphics from BASIC.
The original version of Laser Gunner required all other motion to stop when missiles were fired. By using a vertical blank interrupt routine, continuous and smooth motion can be achieved. The vertical blank (VB) is the time during which the television's electron beam is turned off while it returns from the lower-right comer of the screen to the top-left. Depending on the graphics mode and other interrupts, there are approximately 7980 machine cycles available during a single VB. (A machine cycle is the smallest measurement of time on your computer's internal clock.)
To utilize the VB, we first have to tell the operating system (OS) where to go. We do this by performing a Vertical Blank Interrupt (VBI) through the Set Vertical Blank Vector (SETVBV) routine. Before jumping to the SETVBV, we have to load the least significant byte (LSB) in the Y register and the most significant byte (MSB) in the X register of our VB machine language routine.
Into the accumulator we can place either a 6 or a 7. Six is for deferred mode; the OS does its housekeeping operations before it executes our code. Seven is for immediate mode; the OS executes our code first during the VB. Since we will be checking the collision registers, we will be loading a 6 into the accumulator. BASIC program initializes the SETVBV through the USR statement on line 1460. To return control to the OS, we jump back through $E45F.
The BASIC and the machine language (ML) programs interact through several PEEKs and POKEs. The ML program checks the STRIG(0), location $0284, for the press of a button, and moves both missiles horizontally. Since the player/missile graphics are defined in strings, it is easier to have BASIC draw and erase the missiles by PEEKing the flags that the ML program sets.
In the enhanced version, both missiles appear on the screen at the same time. This requires the additional coding located at $0607. The missiles are defined as:
BIT | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
M3 | M2 | M1 | M0 |
Since it is difficult for Atari BASIC to selectively tum bits off and on, we will use ML to change the bits. The AND instruction is used to set bits to zero (off). ANDing a bit with zero sets the bit to zero. The ORA instruction is used to set bits to one (on). By ORAing a bit with one, we set the bit to one. The flipping of the missile bits is done in the subroutines at lines 1300-1330.
The programming technique of performing graphics movement during the vertical blank enhances Laser Gunner almost to the level of difficulty of professional arcade games. Further program execution speed can be achieved by removing the REMs and moving the part of the program that does most of the action to the beginning. This shortens the memory that BASIC has to search to find line number references. An additional enhancement would be to add a sound routine during the VB each time the trigger is pressed.
0 REM LASER GUNNER.V2{17 SPACES} 1 REM An enchancement of Laser Gunner 2 REM in Issue 30 of COMPUTE!{9 SPACES} 3 REM The program allow simutaneous{3 SPACES} 4 REM motion of the missiles using the 5 REM verticle blank period.{10 SPACES} 6 REM Developed by Thomas A. Marshall 7 REM Albuquerque, NM 87123{11 SPACES} 10 GOSUB 1400 20 RESTORE 100 DIM PM$(2048):GRAPHICS 2+16 110 DIM ALIEN$(11),PLAYER$(11),NULL$(11),EXPLODE$(12*9),TARGET(20) 120 FOR I=1 TO 11:NULL$(I)=CHR$(0):NEXT I 130 LEVEL=15:CNT=15:REM DECREASE LEVEL FOR A HARDER GAME 140 A=ADR(PM$):REM RAW ADDRESS 150 PMBASE=INT(A/1024)*1024:REM NEAREST 1 K BOUNDARY 160 IF PMBASE<A THEN PMBASE=PMBASE+1024:REM IF BELOW STRING, GO TO NEXT 1K BOUNDARY 170 S=PMBASE-A:REM START OF PMBASE IN STRING (OFFSET) 180 POKE 559,46:REM SET DOUBLE-LINE RES. 190 POKE 54279,PMBASE/256:REM TELL ANTIC WHERE PMBASE IS 200 POKE 53277,3:REM TURN ON PLAYER/MISSILE DIRECT MEMORY ACCESS(DMA) 210 PM$=CHR$(0):PM$(2048)=CHR$(0):PM$(2)=PM$:REM CLEAR OUT ALL P/M MEMORY 220 POSITION 4,0:? #6;"laser gunner" 230 ? #6:FOR I=1 TO 10:? #6;"-":NEXT I:POSITION 0,0 240 REM STRING POS OF PLAYER 0-3, AND MISSILES IN STRING: 250 P0=S+512:P1=P0+128:P2=P1+128:P3=P2+128:MS=S+384 260 PM$(P2+32)=CHR$(255):PM$(P2+127)=CHR$(255):PM$(P2+33,P2+127)=PM$(P2+32):REM CREATE WALL 270 PM$(P3,P3+127)=PM$(P2,P2+127):REM CREATE "ZONE" 280 POKE 53250,92:REM POSITION PLAYER 2, THE WALL 290 POKE 53251,60:REM POSITION PLAYER 3, THE ZONE 300 POKE 53258,0:POKE 53259,3:REM REM MAXIMUM WIDTH 310 POKE 706,14:POKE 707,66:REM SET COLOR OF PLAYERS 2 AND 3 320 DATA 0,8,28,62,255,62,255,62,28,8,0 330 FOR I=1 TO 11:READ A:ALIEN$(I)=CHR$(A):NEXT I:REM PLACE INTO STRING, HENCE INTO P/M MEMORY 340 AY=32:REM ALIEN VERTICAL LOCATION 350 PM$(P1+AY,P1+AY+11)=ALIEN$:REM PLACE INTO STRING INTO P/M MEMORY 360 POKE 705,6*16+10:REM SET COLOR OF ALIEN TO PURPLE 370 POKE 53249,180:REM SET HORIZNONTAL POSITIN 380 POKE 53257,1:REM SET ALIEN TO DOUBLE-WIDTH 390 REM SET UP EXPLODE$, USE FOR EXPLOSION OF ALIEN 400 FOR I=1 TO 108:READ A:EXPLODE$(I)=CHR$(A):NEXT I:REM EXPLODE DATA 410 DATA 8,28,62,255,54,255,62,28,8,8,28,62,235,54,235,62,28,8,8,28,54,227,34,227,54,28,8 420 DATA 8,24,34,227,34,227,18,24,8,8,24,34,194,32,163,18,8,8 430 DATA 0,0,0,0,24,24,0,0,0,0,0,0,32,8,24,0,4,0,0,0,0,36,0,16,0,36,0,0,128,10,128,0,16,0,16,65 440 DATA 0,9,0,0,32,0,32,0,8,0,0,0,64,0,0,64,0,4,0,0,0,0,0,0,0,128,0 450 RY=INT(78*RND(0)+32):MH=190+RY*2:REM ATTRACT MODE: 455 POSITION 9,5:? #6;"PRESS":POSITION 9,6:? #6;"START" 460 FOR I=32 TO 110:PM$(P1+I,P1+I+11)=ALIEN$:IF I=RY THEN PM$(MS+RY+10,MS+RY+10)=CHR$(12) 470 IF I>RY THEN POKE 53253,MH-I*2 480 IF PEEK(53279)>6 THEN NEXT I 490 PM$(MS+RY+10,MS+RY+10)=CHR$(0) 500 FOR I=110 TO 32 STEP -1:PM$(P1+I,P1+I+11)=ALIEN$:IF PEEK(53279)>6 THEN NEXT I 510 IF PEEK(53279)>=7 THEN 450 515 POSITION 9,5:? #6;"{5 SPACES}":POSITION 9,6:? #6;"{5 SPACES}" 520 IF PEEK(53279)=3 THEN FOR I=0 TO 4:POKE 53248+I,0:NEXT I:GRAPHICS 0:END 530 DATA 0,0,224,48,120,63,120,48,224,0,0 540 FOR I=1 TO 11:READ A:PLAYER$(I)=CHR$(A):NEXT I 550 PY=60:REM SET PLAYER'S VERITCAL LOCATION 560 PM$(P0+PY,P0+PY+11)=PLAYER$ 570 PM$(P1,P1)=CHR$(0):PM$(P1+127,P1+127)=CHR$(0):PM$(P1+2,P1+127)=PM$(P1) 580 AY=INT(78*RND(0)+32):PM$(P1+AY,P1+AY+11)=ALIEN$:REM RESET ALIEN 590 POKE 53256,1:REM PLAYER 0 DOUBLE-WIDTH 600 POKE 53248,64:REM HORIZONTAL POSITION OF PLAYER 0 610 POKE 704,26:REM COLOR OF PLAYER 0 620 POKE 53260,1:REM MISSILE 0 DOUBLE-WIDTH 630 ST=STICK(0):IF ST<>15 THEN DIR=ST:F=2:SOUND 0,100,0,8 635 IF PEEK(CMPFLG)=1 THEN PM$(TMS,TMS)=CHR$(0):POKE CMPFLG,0:REM THE MISSILES HIT EACH OTHER 636 IF PEEK(COLFLG)=1 THEN POKE COLFLG,0:GOTO 900:REM THE ALIEN MISSILE HIT THE WALL OR ZONE 640 PY=PY-(DIR=14)*(PY>32)*F+(DIR=13)*(PY<110)*F:F=1:REM UPDATE PLAYER 650 PM$(P0+PY,P0+PY+11)=PLAYER$:SOUND 0,0,0,0 660 IF PEEK(M0FLG)=1 THEN GOSUB 1310:REM ERASE THE PLAYER'S MISSILE 670 IF PEEK(TRIGFLG)=0 THEN GOSUB 1310:POKE M0FLG,0:TMS=MS+PY+5:GOSUB 1300:POKE TRIGFLG,1:REM THE TRIGGER WAS PRESSED 720 IF PEEK(HITFLG)<>0 THEN 790:REM NO COLLISION 725 REM THE PLAYER'S MISSILE HIT THE ALIEN 730 SCR=SCR+10:POSITION 11-LEN(STR$(SCR))/2,5:? #6;SCR 735 PM$(TMS,TMS)=CHR$(0):POKE M0FLG,1:POKE HITFLG,1:POKE 53278,0 740 AY=AY+1:P=PEEK(705):REM PRESERVE COLOR OF ALIEN 750 FOR I=0 TO 11:Z=I*9:PM$(P1+AY,P1+AY+9)=EXPLODE$(Z+1,Z+9) 760 POKE 705,PEEK(53770):POKE 53279,0:SOUND 0,I*2,0,15-I:FOR W=1 TO 2:NEXT W:NEXT I 770 POSITION 5,5:? #6;"{10 SPACES}":REM ERASE SCORE 780 SOUND 0,0,0,0:POKE 705,P:GOTO 570 790 IF AY=PY THEN 870:REM TOO CLOSE FOR COMFORT 800 IF TARGET=0 THEN GOSUB 950:TARGET=TARGET(INDEX):REM SELECT A TARGET 810 IF AY<>TARGET THEN 840 820 CNT=CNT-1:IF CNT THEN 630 830 CNT=LEVEL:GOTO 870 840 AY=AY+SGN(TARGET-AY):REM MOVE TOWARDS TARGET 850 PM$(P1+AY,P1+AY+11)=ALIEN$ 860 GOTO 630 870 IF ABS(AY-PY)<10 THEN GOSUB 970 875 IF PEEK(ALIEFLG)=0 THEN 630 880 POKE ALIEFLG,0:TM1S=MS+AY+5:GOSUB 1320:TTAY=AY:GOTO 630 900 P=ASC(PM$(P2+TTAY+5))*2-256:GOSUB 1330:POKE 53278,0:REM CUT HOLE IN WALL 910 IF P<0 THEN 1000:REM WALL DESTROYED 920 PM$(P2+TTAY+5,P2+TTAY+5)=CHR$(P) 930 GOTO 630 940 REM PICK A TARGET 950 INDEX=INDEX+1:TARGET(INDEX)=INT(78*RND(0)+32):RETURN 970 IF INDEX=1 THEN 950 980 TARGET=TARGET(INT(INDEX*RND(0)+1)):RETURN 990 REM DESTRUCTION OF PLAYER 1000 FOR I=1 TO 100:Z1=TTAY+5+I:Z2=TTAY+5-I 1005 PM$(TMS,TMS)=CHR$(0):POKE M0FLG,1:POKE M0PFLG,72 1010 IF Z1<126 THEN PM$(P2+Z1,P2+Z1)=CHR$(0) 1020 IF Z2>30 THEN PM$(P2+Z2,P2+Z2)=CHR$(0) 1030 IF Z1<126 OR Z2>30 THEN NEXT I 1040 FOR I=30 TO 1 STEP -1:FOR J=0 TO 20 STEP 3:SOUND 0,J+1,10,8:POKE 707,PEEK(53770):NEXT J:NEXT I 1050 SOUND 0,0,0,0:SOUND 1,0,0,0:POKE 707,14:FOR W=1 TO 50:NEXT W:POKE 707,0 1060 FOR I=0 TO 15 STEP 0.2:SOUND 0,I,8,I:POKE 704,16+I:NEXT I 1070 SOUND 0,0,0,0 1080 Z1=PY:Z2=PY:INCR=0 1090 Z1=Z1+INCR*(Z1<128):Z2=Z2-INCR*(Z2>=0):POKE 704,PEEK(53770) 1100 PM$(P0+Z1,P0+Z1)=CHR$(255):PM$(P0+Z2,P0+Z2)=CHR$(255):POKE 53279,0 1110 INCR=INCR+0.5:IF Z1<127 OR Z2>0 THEN 1090 1120 FOR I=1 TO 100:POKE 704,PEEK(53770):NEXT I 1130 FOR I=0 TO 7:POKE 53248+I,0:NEXT I:GRAPHICS 18 1140 POSITION 4,0:? #6;"laser gunner":POSITION 3,5:? #6;"your score was:"; 1150 POSITION 10-LEN(STR$(SCR))/2,7:? #6;SCR 1160 FOR I=15 TO 0 STEP -0.2:SOUND 0,10+10*RND(0),0,I:SOUND 1,100+10*RND(0),16,I 1170 SETCOLOR 4,3,14*RND(0):NEXT I 1280 RUN 1299 REM M0 SET 1300 Q=USR(ANORA,ASC(PM$(TMS,TMS)),3,2):PM$(TMS,TMS)=CHR$(Q):RETURN 1309 REM M0 CLEAR 1310 Q=USR(ANORA,ASC(PM$(TMS,TMS)),12,1):PM$(TMS,TMS)=CHR$(Q):RETURN 1319 REM M1 SET 1320 Q=USR(ANORA,ASC(PM$(TM1S,TM1S)),12,2):PM$(TM1S,TM1S)=CHR$(Q):RETURN 1329 REM M1 CLEAR 1330 Q=USR(ANORA,ASC(PM$(TM1S,TM1S)),3,1):PM$(TM1S,TM1S)=CHR$(Q):RETURN 1400 TRIGFLG=1546:HITFLG=1547:M0FLG=1548:TMS=1:TM1S=1 1410 ALIEFLG=1550:COLFLG=1551 1420 ANORA=1753:CMPFLG=1553 1430 IF PEEK(1753)=104 THEN RETURN 1440 GRAPHICS 18:? #6;"INITIALIZING" 1450 RESTORE 1500:GOSUB 1500 1460 A=USR(1536):RETURN 1500 FOR I=1536 TO 1552:READ A:POKE I,A:NEXT I 1509 REM INIT 1536 TO 1552 1510 DATA 104,169,6,170,160,22,32,92,228,96,1,1,1,72,1,0,180 1520 FOR I=1558 TO 1709:READ A:POKE I,A:NEXT I 1530 REM MISSILE MOVING ROUTINE 1540 DATA 173,132,2,201,0,240,2,208,12,205,12,6,240,12,169,0,141,10,6,240 1550 DATA 58,205,12,6,240,53,238,13,6,238,13,6,173,13,6,141,4,208,173,8 1560 DATA 208,41,2,208,9,173,13,6,201,190,144,27,176,15,173,13,6,201,170,144 1570 DATA 18,169,0,141,30,208,141,11,6,169,1,141,12,6,169,72,141,13,6,173 1580 DATA 14,6,201,0,208,63,173,9,208,41,1,208,21,173,9,208,41,12,208,29 1590 DATA 206,16,6,206,16,6,173,16,6,141,5,208,208,35,169,1,141,17,6,141 1600 DATA 12,6,169,72,141,13,6,208,5,169,1,141,15,6,169,0,141,30,208,169 1610 DATA 1,141,14,6,169,180,141,16,6,76,95,228 1620 FOR I=1753 TO 1791:READ A:POKE I,A:NEXT I 1630 REM AND-OR ROUTINES 1640 DATA 104,104,104,141,215,6,104,104,141,216,6,104,104,201,1,208,9,173,215,6 1650 DATA 45,216,6,76,249,6,173,215,6,13,216,6,133,212,169,0,133,213,96 1660 RETURN
Listing. Laser Gunner II.
Download (Saved BASIC) / Download (Listed BASIC)
Return to Table of Contents | Previous Section | Next Section