Firepower is a single-screen shooting gallery game in which the player maneuvers a gun sight around the screen to intercept falling enemy sprites. The player’s turret graphic is built from a 65-character DIM array using CHR$ codes for vertical bars, dashes, and a plus sign to form a crosshair shape. A User Defined Graphic (UDG) is loaded via a DATA statement and POKE loop into the UDG area (USR CHR$ 144) to display the falling target — an oval or bomb shape defined by the byte pattern 0,60,126,126,126,126,60,0. Timing is handled by reading the system frames counter at addresses 23672–23673, converting ticks to seconds by dividing by 60, giving a 60-second countdown. Scoring awards 2 points for a direct hit (same screen position) and 1 point for a near-miss (adjacent cell), detected by comparing linearized screen coordinates.
Program Structure
The program is organized into distinct phases:
- Initialization (lines 2–15): UDG data is POKEd, the crosshair string array is constructed, and the screen is set up.
- Star field generation (line 20): 50 asterisks are placed at random positions.
- Game variable reset (lines 25–45): Score, player position, frame counter, and bomb state are initialized.
- Main game loop (lines 100–240): Timer update, score display, bomb movement, player movement, collision detection, and redraw all occur here.
- End screen (lines 1000–1010): Game over message with restart prompt.
- Data (line 1020): Eight bytes defining the UDG sprite.
UDG Sprite
Line 5 loads 8 bytes from the DATA statement at line 1020 into the UDG slot at USR CHR$ 144, defining a symmetrical oval/bomb shape: 0,60,126,126,126,126,60,0. This is displayed via CHR$ 144 throughout the game. The loop iterates from USR CHR$ 144 to USR CHR$ 144+7, covering all 8 pixel rows of the character.
Crosshair Construction
The player’s gun sight is stored in a 65-character string array a$. Rather than printing individual characters, the array is populated sparsely: position 1 gets CHR$ 73 (pipe/vertical bar), position 32 gets CHR$ 45 (dash), position 33 gets CHR$ 43 (plus), position 34 gets CHR$ 45 (dash), and position 65 gets CHR$ 73. The remaining characters default to spaces, giving a two-row crosshair occupying a 32-wide by 2-row region when printed with AT x,y.
Timing Mechanism
The system’s frame counter is stored across two bytes at addresses 23672 and 23673. Line 100 reads these with PEEK 23673*256 + PEEK 23672 and divides by 60 to convert frames to seconds, storing the result in t1. Line 105 detects when t changes to produce a tick beep and update the displayed time. The POKE statements at line 35 reset this counter to zero at the start of each game.
Bomb Movement Logic
A single bomb is tracked by position variables a (row) and b (column), with a horizontal drift value d set to −1, 0, or 1 randomly. Line 120 spawns a new bomb when b=0 (inactive); line 125 erases the old position (via OVER 1 XOR mode) and advances the bomb down and sideways; line 130 deactivates the bomb if it reaches row 21 or the screen edges.
The character code \{16}\{0} in the conditional at line 120 is an INK 0 color control embedded in the condition check — effectively printing an invisible character or acting as a flag to test b.
Collision Detection
Collision uses linearized screen coordinates. Line 205 computes p = (x+1)*22 + y for the player and p1 = a*22 + b for the bomb. A direct hit (p = p1) awards 2 points and triggers a long beep. Line 215 checks for a near-miss by testing ABS(p-p1) = 1 (same row, adjacent column) or ABS(p-p1) = 22 (adjacent row), awarding 1 point. This uses a column-width of 22 rather than 32 in the linearization, which is a minor inaccuracy but produces a workable approximation.
Player Movement
Lines 220–225 read INKEY$ for keys 5/6/7/8 and compute new coordinates x1 and y1 using Boolean arithmetic — adding or subtracting 1 based on the result of logical comparisons. Boundary checks (x<19, x>0, y<30, y>1) are embedded in the same expressions. Line 230 erases the crosshair at the old position (OVER 1 mode), then lines 235–240 update the position and loop.
Display Technique
OVER 1 is set globally at line 15 and used throughout the main loop. This XOR drawing mode means printing a character twice restores the background, enabling flicker-free movement of both the bomb and the player crosshair without explicitly clearing them. The star field placed at line 20 persists beneath the sprites because OVER 1 toggles pixels rather than overwriting them.
Notable Anomalies
- The linearization factor of 22 (instead of 32) at line 205 means near-miss detection is slightly off for columns beyond 22, but the hit detection at exact equality is unaffected.
- Line 205 redraws the UDG at the bomb position after a direct hit (
PRINT AT a,b; CHR$ 144), which in OVER 1 mode XORs it again — this may visually leave a ghost, though the bomb variablebis immediately zeroed. - The crosshair occupies two rows (65 characters = 2×32 + 1), but only specific positions are non-space, so the printed region looks like a sparse two-row symbol rather than a solid block.
Content
Source Code
2 REM "firepower"
5 FOR j= USR CHR$ 144 TO USR CHR$ 144+7:READ a:POKE j,a:NEXT j
10 DIM a$(65):LET a$(1)= CHR$ 73:LET a$(32)= CHR$ 45:LET a$(34)= CHR$ 45:LET a$(65)= CHR$ 73
12 LET a$(33)= CHR$ 43
15 BORDER 5:PAPER 5:INK 0:OVER 1:CLS
20 FOR j=1 TO 50:PRINT AT INT (RND*22), INT (RND*32);"*":NEXT j
25 LET s=0
30 LET x=11:LET y=15
35 POKE 23673,0:POKE 23672,0
40 LET t=0
45 LET b=0
100 LET t1= INT ((PEEK 23673*256+ PEEK 23672)/60)
105 IF t <>t1 THEN BEEP .01,t:LET t=t1
110 PRINT AT 0,0; OVER 0;"SCORE ";s,"TIME ";t
115 IF t >=60 THEN GO TO 1000
120 IF \{16}\{0} NOT b THEN LET b= INT (RND*31)+1:LET a=1:LET d= INT (RND*3)-1:GO TO 130
125 PRINT AT a,b; CHR$ 144:LET a=a+1:LET b=b+d
130 IF a=21 OR b=0 OR b=32 THEN LET b=0:GO TO 200
135 PRINT AT a,b; INK 6; CHR$ 144
200 PRINT AT x,y; INK 8;a$
205 LET p=(x+1)*22+y:LET p1=a*22+b:IF p=p1 THEN BEEP 1,50:LET s=s+2:PRINT AT a,b; CHR$ 144:LET b=0:GO TO 220
210 IF INKEY$ <>"0" THEN GO TO 220
215 IF ABS (p-p1)=1 OR ABS (p-p1)=22 THEN BEEP .5,50:LET s=s+1
220 LET x1=x+(INKEY$="6" AND x<19)-(INKEY$="7" AND x>0)
225 LET y1=y+(INKEY$="8" AND y<30)-(INKEY$="5" AND y>1)
230 PRINT AT x,y; INK 8;a$
235 LET x=x1:LET y=y1
240 GO TO 100
1000 PRINT OVER 0; FLASH 1; PAPER 2; INK 7; AT 10,12;"GAME OVER"; FLASH 0; AT 21,5;"PRESS""R"" TO PLAY AGAIN"
1005 IF INKEY$="r" OR INKEY$="R" THEN RUN
1010 GO TO 1005
1020 DATA 0,60,126,126,126,126,60,0
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
