Rifle is a shooting gallery game in which the player fires at a row of moving targets that scroll across the screen, with the gun position oscillating left and right along the bottom. The program uses multiple machine code routines POKEd into memory at address 60000, handling display effects including a border color-cycling routine and screen-copy operations. Six progressively harder rounds are defined at lines 1000–1050, each using a different set of User Defined Graphics characters to represent two-row sprite targets worth 100 to 600 points each; clearing all targets in a round awards bonus points and reduces available ammunition. A bubble-sort high-score table (lines 1610–1670) stores the top five names and scores, pre-initialized with placeholder entries, and persists across games within the same session. The graphical title screen at lines 40–300 is drawn entirely with PLOT, DRAW, CIRCLE, and PRINT statements to create a detailed rifle-range backdrop.
Program Analysis
Program Structure
The program divides into several logical sections:
- Lines 10–30: Initialization — defines helper functions, clears memory, and calls the machine code loader subroutine at line 1760.
- Lines 40–300: Draws the static rifle-range backdrop using PLOT, DRAW, CIRCLE, and PRINT.
- Lines 310–320: Launches the title animation via
RANDOMIZE USR 60000, then jumps to the main menu. - Lines 330–620: Core gameplay loop — manages ammunition, moving gun, target hit detection, and round progression.
- Lines 990–1060: Round-definition subroutines (lines 1000–1050), each setting target UDGs, point value, and ink color; line 1060 handles round-clear fanfare.
- Lines 1110–1210: Title/menu screen with animated border and key selection.
- Lines 1220–1410: Multi-page instruction screens.
- Lines 1420–1730: End-of-game sequence, high-score entry, bubble sort, and leaderboard display.
- Lines 1760–1880: Machine code loader — reads DATA bytes, POKEs them to addresses 60000–60134 and 60200–60266, initializes the high-score arrays, and relocates the UDG table.
Machine Code Routines
Three distinct machine code blocks are installed by the loader:
| Address | Called via | Purpose |
|---|---|---|
| 60000 | RANDOMIZE USR 60000 | Title screen animation (copies/manipulates display memory) |
| 60050 | RANDOMIZE USR 60050 | Game-start screen setup (copies attribute or pixel area) |
| 60100 | RANDOMIZE USR 60100 | Used on instruction pages — likely a border or screen effect |
| 60200 | RANDOMIZE USR 60200 | Per-frame animation during menu loop (title scrolling/twinkle) |
| 60250 | RANDOMIZE USR 60250 | Border color cycling; driven by POKE 59990,g before the call |
The loader at lines 1780–1850 applies two skip conditions: when the loop variable F equals 60012 or 60062 (first block) or 60242 (second block), it advances F by 38 or 8 bytes respectively, skipping those addresses — a technique used to leave certain memory locations unpopulated or to jump over self-modifying regions.
The RANDOMIZE USR idiom is used throughout because the return value of a machine code routine is loaded into the RANDOMIZE seed, so it is a convenient way to call machine code without a numeric result interfering with the program flow.
UDG Sprite System
Each of the six target types is built from two rows of two UDG characters, giving a 2×2 character sprite. The UDGs used are \a–\u (the full set of 21 user-defined graphics). Line 1880 relocates the UDG table pointer (system variables at addresses 23675–23676) to point 6 bytes before the calculated UDG base, effectively remapping all UDG slots. The instruction screen (lines 1330–1380) uses the same UDG strings to illustrate each prize tier.
Gameplay Mechanics
The gun position is stored in variable a and oscillates between columns 7 and 23 using direction variable x (lines 450–470). Firing is detected by any key press setting f=1 (line 420). Hit detection at line 480 checks whether the character in the bottom row of the target string b$ at the gun’s column offset has a code greater than 32 (i.e., is not a space), providing a simple but effective 1D collision test. When a hit occurs, lines 540–570 clear both the hit character and its immediate neighbors in both a$ and b$, simulating a two-character-wide target being destroyed.
The variable go starts at 1000 and is incremented by 10 each time all five targets in a round are cleared (line 590), so successive rounds call GO SUB 1010, 1020, etc., cycling through progressively harder target types. The sixth round at line 1050 mixes all five target sprites together for maximum difficulty.
High-Score Table
The high-score system uses parallel arrays: h(5) for numeric scores and h$(5,10) for 10-character names (lines 1810–1830), pre-seeded with scores 5000 down to 1000 and the name “SPECTRUM”. Name entry at lines 1500–1570 supports backspace via CHR$ 12 detection. After entry, a bubble sort (lines 1610–1670) uses a shrinking-range variable zx starting at 4 and decrementing each pass, correctly sorting both arrays in tandem.
Helper Functions
Line 20 defines two functions used for splitting 16-bit values into bytes: FN h(x) returns the high byte (INT(x/256)) and FN l(x) returns the low byte. These are applied in line 1880 to split the UDG table address for POKEing into the system variable pair at 23675–23676, and also in line 1430 where h(5) is used as a numeric array element — note that h is both a DEF FN name and a numeric array, which works in Spectrum BASIC because functions are called with FN h(...) syntax.
Notable Techniques and Anomalies
- The scrolling target strings use a rotate idiom:
LET a$=a$(2 TO )+a$(1)(line 430), efficiently cycling the target row left by one character each frame without any loop. - Line 590 uses
LET go=go+10: GO TO 380to advance the subroutine destination — sincegostarts at 1000 and each round subroutine is at 1000, 1010, 1020 …, this elegantly steps through round definitions using a numeric variable as the GO SUB target. - The backdrop drawing at lines 110–210 uses perspective-like diagonal lines and arcs to suggest a 3D shooting gallery booth, all in pure BASIC PLOT/DRAW commands.
- Line 290 uses a
PAPER 0; INK 7area for the ammo display in the attribute-colored title bar, exploiting attribute color to integrate the HUD into the static backdrop drawn at startup. - Line 1140 and line 1680 use long PRINT strings of block graphics characters to render multi-line pixel-art title text, a common technique to embed graphics in BASIC without a separate data-loading routine.
- The variable name collision between array
h()and functionFN h()at line 1430 (IF sc>h(5)) accesses the array element, not the function — a valid distinction in Spectrum BASIC but potentially confusing to read.
Content
Source Code
10 REM by Michael Housley of Bradford,Yorkshire
20 DEF FN h(x)=INT (x/256): DEF FN l(x)=x-256*FN h(x)
30 CLEAR 60000: GO SUB 1760
40 PAPER 4: INK 0: BRIGHT 1: BORDER 4: CLS
50 PRINT PAPER 5;AT 0,0,,,,,,,,
60 FOR f=1 TO 14 STEP .5: CIRCLE INK 6;235,160,f: NEXT f
70 INVERSE 1: PLOT PAPER 0; INK 6;225,156: DRAW INK 6; PAPER 0;20,0,1.5: DRAW PAPER 0; INK 6;-20,0,-.75
80 CIRCLE INK 6; PAPER 0;232,162,3: PLOT INK 6; PAPER 0;233,162
90 CIRCLE INK 6; PAPER 0;238,162,3: PLOT INK 6; PAPER 0;237,162
100 CIRCLE INK 6; PAPER 0;235,157,2: INVERSE 0
110 PLOT 0,0: FOR f=0 TO 15: PLOT f,f: DRAW 15-f,0: PLOT 255-f,-f: DRAW f-15,0: NEXT f
120 PLOT 0,0: DRAW 48,48: DRAW 159,0: DRAW 48,-48: DRAW 0,140: DRAW -255,0: DRAW 0,-140
130 PLOT 48,48: DRAW 0,92: PLOT 207,48: DRAW 0,92: PLOT 16,16: DRAW 223,0
140 PLOT 10,10: DRAW 0,130: PLOT 20,20: DRAW 0,120: PLOT 30,30: DRAW 0,110: PLOT 40,40: DRAW 0,100
150 PLOT 245,10: DRAW 0,130: PLOT 235,20: DRAW 0,120: PLOT 225,30: DRAW 0,110: PLOT 215,40: DRAW 0,100
160 PLOT 58,48: DRAW -24,-32: PLOT 68,48: DRAW -14,-32: PLOT 78,48: DRAW -9,-32: PLOT 88,48: DRAW -5,-32
170 PLOT 98,48: DRAW -4,-32: PLOT 108,48: DRAW -3,-32: PLOT 118,48: DRAW -2,-32: PLOT 128,48: DRAW 0,-32
180 PLOT 138,48: DRAW 2,-32: PLOT 148,48: DRAW 3,-32: PLOT 158,48: DRAW 4,-32: PLOT 168,48: DRAW 5,-32
190 PLOT 178,48: DRAW 9,-32: PLOT 188,48: DRAW 14,-32: PLOT 188,48: DRAW 14,-32: PLOT 198,48: DRAW 24,-32
200 PLOT 48,95: DRAW 159,0: DRAW 10,-3: DRAW -179,0: DRAW 8,3: PLOT 38,92: DRAW 0,-2: DRAW 179,0: DRAW 0,2
210 PLOT 0,140: DRAW 0,2: DRAW 255,0: DRAW 0,-2
220 FOR f=67 TO 187 STEP 20: CIRCLE INK 7;f,106,10: CIRCLE INK 7;f,106,7: NEXT f
230 PRINT PAPER 0; INK 7;AT 2,6;"YE OLDE RIFLE RANGE."
240 PLOT 45,149: DRAW 165,0: DRAW 0,13: DRAW -165,0: DRAW 0,-13
250 PLOT 80,149: DRAW 0,-6: PLOT 82,149: DRAW 0,-6: PLOT 175,149: DRAW 0,-6: PLOT 173,149: DRAW 0,-6
260 PLOT 80,142: PLOT 82,142: PLOT 175,142: PLOT 173,142
270 PRINT PAPER 2; INK 6;AT 5,8;"\c";AT 6,8;"\d\e";AT 5,22;"\c";AT 6,22;"\d\e"
280 PRINT PAPER 7;AT 5,11;"YE PRIZES.";AT 11,12;"YE SCORE"; PAPER 0;AT 12,13," ";AT 14,6," ";AT 15,6," "
290 PRINT PAPER 7;AT 0,11;"YE AMMO "; PAPER 0;" ": PRINT PAPER 0;AT 20,2," ";AT 21,1," "
300 PAUSE 160
310 RANDOMIZE USR 60000
320 GO TO 1110
330 BORDER 0
340 LET sc=0: LET bon=1000: LET tam=51
350 RANDOMIZE USR 60050
360 LET co=0: LET a=15: LET f=co: LET x=1: LET am=tam: LET go=1000
370 PRINT PAPER 0; INK 6;AT 0,19;am
380 GO SUB go
390 LET f=0: LET am=am-1
400 PRINT PAPER 0; INK 6;AT 0,19;am;" "
410 PRINT PAPER 0; INK in;AT 14,6;a$;AT 15,6;b$
420 IF INKEY$<>"" THEN LET f=1: BEEP .01,-10
430 LET a$=a$(2 TO )+a$(1): LET b$=b$(2 TO )+b$(1)
440 PRINT INK 5; PAPER 0;AT 20,a;" \a ";AT 21,a;" \b "
450 LET a=a+x
460 IF a<7 THEN LET x=1
470 IF a>23 THEN LET x=-1
480 IF f=1 AND CODE (b$(a-5))>32 THEN GO TO 520
490 IF f=1 THEN LET f=0: LET am=am-1
500 IF am=0 THEN GO TO 1420
510 GO TO 400
520 BEEP .01,20
530 LET co=co+1
540 LET b$(a-5)=" "
550 IF CODE (b$(a-6))>32 THEN LET b$(a-6)=" "
560 IF CODE (b$(a-4))>32 THEN LET b$(a-4)=" "
570 LET a$(a-6 TO a-4)=" "
580 LET sc=sc+va: PRINT INK RND*6+1; PAPER 0;AT 12,13;sc;" "
590 IF co=5 THEN LET go=go+10: GO TO 380
600 IF am=0 THEN GO TO 1420
610 GO TO 490
990 REM go
1000 LET va=100: LET in=6: LET a$="\c \c \c \c \c ": LET b$="\d\e \d\e \d\e \d\e \d\e ": RETURN
1010 LET co=0: LET va=200: LET in=7: LET a$="\f\h \f\h \f\h \f\h \f\h ": LET b$="\g\i \g\i \g\i \g\i \g\i ": RETURN
1020 LET co=0: LET va=300: LET in=4: LET a$="\j\l \j\l \j\l \j\l \j\l ": LET b$="\k\m \k\m \k\m \k\m \k\m ": RETURN
1030 LET co=0: LET va=400: LET in=5: LET a$="\n\o \n\o \n\o \n\o \n\o ": LET b$="\p\q \p\q \p\q \p\q \p\q ": RETURN
1040 LET co=0: LET va=500: LET in=3: LET a$="\r\s \r\s \r\s \r\s \r\s ": LET b$="\t\u \t\u \t\u \t\u \t\u ": RETURN
1050 LET co=0: LET va=600: LET in=7: LET a$="\r\s \c \f\h \j\l \n\o ": LET b$="\t\u \d\e \g\i \k\m \p\q ": RETURN
1060 FOR f=0 TO 10: FOR g=7 TO 0 STEP -1: POKE 59990,g: RANDOMIZE USR 60250: BEEP .001,f*2+20: NEXT g: NEXT f
1070 RANDOMIZE USR 60050: BEEP .01,20
1080 PRINT AT 20,a;" ";AT 21,a;" ": LET sc=sc+bon: LET tam=tam-5
1090 IF tam<30 THEN LET sc=sc+10000: GO TO 1420
1100 GO TO 360
1110 BORDER 0: PAPER 0: INK 7: CLS
1120 LET ink=RND*5+1
1130 FOR f=4 TO 5: PRINT INK ink;AT f,0;"▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚▚";AT f+13,0;"▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜▜": NEXT f
1140 PRINT INK ink;AT 8,6;"▗▜▜▘▗▘▐ ▗▘ ▗▜▗▐▝▐▐▀▐▀ ▗▜▐ ▗ ▐ ▗ ▗▜▗▐▐▜▐▐▐▘ ▘▘▀▘▘ ▀▘▀▘ ▘▘▘▘▘▝▝▀▝▀"
1150 PRINT AT 12,0;"PRESS EITHER"''"1.FOR INSTRUCTIONS"'" OR 2.TO PLAY"
1160 FOR f=0 TO 500
1170 RANDOMIZE USR 60200
1180 IF INKEY$="1" THEN BEEP .01,10: GO TO 1220
1190 IF INKEY$="2" THEN BEEP .01,10: GO TO 330
1200 NEXT f
1210 CLS : GO TO 1680
1220 CLS : INK 0
1230 PRINT "USE ANY KEY TO SHOOT AT THE"''"TARGETS."
1240 PRINT '"THE GAME WILL END WHEN YOU HAVE"''"NO AMMUNITION LEFT."
1250 PRINT '"IF YOU SHOOT ALL THE TARGETS"''"THEN YOU WILL GET SOME BONUS"''"POINTS AND START AGAIN WITH LESS"''"AMMUNITION."
1260 PRINT ''"PRESS ANY KEY.": RANDOMIZE USR 60100: BORDER 0
1270 PAUSE 0: CLS
1280 PRINT "WHILST YOU ARE PLAYING THE GAME"''"YOUR GUN WILL MOVE BACKWARDS"''"AND FORWARDS ACROSS THE"''"BOTTOM OF THE SCREEN JUST TO "''"MAKE THINGS HARDER."
1290 PRINT '"REMEMBER TO ALLOW FOR THE DELAY"''"BEFORE THE BULLET REACHES THE"''"TARGET."
1300 PRINT ''"PRESS ANY KEY."
1310 RANDOMIZE USR 60100: BORDER 0
1320 PAUSE 0: CLS
1330 PRINT "\c \c \c \c \c 100 POINTS"'"\d\e \d\e \d\e \d\e \d\e EACH"
1340 PRINT '"\f\h \f\h \f\h \f\h \f\h 200 POINTS"'"\g\i \g\i \g\i \g\i \g\i EACH"
1350 PRINT '"\j\l \j\l \j\l \j\l \j\l 300 POINTS"'"\k\m \k\m \k\m \k\m \k\m EACH"
1360 PRINT '"\n\o \n\o \n\o \n\o \n\o 400 POINTS"'"\p\q \p\q \p\q \p\q \p\q EACH"
1370 PRINT '"\r\s \r\s \r\s \r\s \r\s 500 POINTS"'"\t\u \t\u \t\u \t\u \t\u EACH"
1380 PRINT '"\c \f\h \j\l \n\o \r\s 600 POINTS"'"\d\e \g\i \k\m \p\q \t\u EACH"
1390 PRINT ''"PRESS ANY KEY."
1400 RANDOMIZE USR 60100: BORDER 0
1410 PAUSE 0: INK 7: CLS : GO TO 1120
1420 FOR f=7 TO 0 STEP -1: POKE 59990,f: RANDOMIZE USR 60250: BEEP .01,f*2: PAUSE 10: NEXT f: BORDER 0: BEEP .2,-20: CLS
1430 IF sc>h(5) THEN GO TO 1470
1440 PRINT FLASH 1;AT 8,8;"YOU SCORED ";sc
1450 PRINT INK 7;AT 16,9;"PRESS ANY KEY."
1460 PAUSE 0: GO TO 1110
1470 LET h$(5)="": PRINT " PLEASE ENTER YOUR NAME"
1480 PRINT AT 10,10;"----------": PRINT AT 15,8;"YOU SCORED ";sc
1490 LET g=1
1500 PAUSE 0
1510 LET l$=INKEY$: BEEP .005,10
1520 IF l$=CHR$ 13 THEN GO TO 1580
1530 IF g>1 AND l$=CHR$ 12 THEN LET h$(5,g)="": PRINT CHR$ 8;"-";CHR$ 8;: LET g=g-1: GO TO 1500
1540 LET h$(5,g)=l$
1550 PRINT AT 10,g+10;l$;
1560 LET g=g+1
1570 IF g<11 THEN GO TO 1500
1580 CLS
1590 LET h(5)=sc
1600 LET zx=4
1610 FOR f=1 TO zx
1620 IF h(f)>=h(f+1) THEN GO TO 1650
1630 LET v=h(f): LET h(f)=h(f+1): LET h(f+1)=v
1640 LET v$=h$(f): LET h$(f)=h$(f+1): LET h$(f+1)=v$
1650 NEXT f
1660 LET zx=zx-1
1670 IF zx>0 THEN GO TO 1610
1680 PRINT AT 0,2; INK RND*6+1;"▐▐▗▘ ▐▐▗▐▐ ▐ ▗▐▗▘ ▗▘▗▐▗▗▐▗▘ ▜▘▗ ▗▐▗▐▐ ▐ ▐▐▗ ▗ ▗▐▐▐▐▗ ▝ ▀▘ ▘▘▘▘▀▘▀▘ ▀▘▘ ▘ ▘▘▘▘▘▀▘"
1690 FOR f=1 TO 5: PRINT INK f+2;AT (f*2)+3,0;f;"-------- ";h$(f);AT (f*2)+3,21;" ---- ";h(f): NEXT f
1700 PRINT AT 18,5;" PRESS ANY KEY. "
1710 FOR f=0 TO 1000
1720 IF INKEY$<>"" THEN GO TO 320
1730 NEXT f: GO TO 1110
1740 STOP
1750 SAVE /"RIFLE" LINE 10
1760 RESTORE 1760
1770 DATA 17,64,156,33,0,64,1,0,27,237,176,201,17,0,64,33,64,156,1,0,27,237,176,201,33,0,88,1,0,3,197,62,0,1,100,0,11,60,211,254,120,177,32,248,54,7,35,54,54,193,11,120,177,32,231,201,32,228,201
1780 FOR f=60000 TO 60134
1790 IF F=60012 OR F=60062 THEN LET F=F+38
1800 READ a: POKE f,a: NEXT f
1810 DIM h(5): DIM h$(5,10)
1820 FOR f=1 TO 5: LET h(f)=6000-(f*1000)
1830 LET h$(f)="SPECTRUM": NEXT f
1840 FOR f=60200 TO 60266: IF F=60242 THEN LET F=F+8
1850 READ a: POKE f,a: NEXT f
1860 DATA 1,2,0,197,33,0,64,1,0,4,126,15,119,35,126,7,119,35,11,120,177,32,243,193,11,120,177,32,1,201,33,0,80,120,177,197,1,0,4,32,225,201,33,0,88,1,0,3,58,86,234,119,35,11,120,177,32,246,201
1870 REM UDG's
1880 LET x=VAL "(PEEK 23637+PEEK 23638*256)+6": POKE 23675,FN l(x): POKE 23676,FN h(x): RETURN
1890 REM lACS ACS ACS ACS ACS ACS ACS ACS ACS ACS ACS ACS ACS ACS ACS .FREE <>STICK DRAW DRAW SAVE SAVE LIST LPRINT DH\b\c\c\bHD#USR 0BIN "II▞BIN 0USR COPY COPY USR USR USR SAVE COPY COPY SAVE USR USR USR *D@©@ LPRINT FN D DRAW 5I▝ COPY 0@USR 8$ RETURN COPY ? USR USR COPY DRAW LPRINT LPRINT LIST p8
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

