Rifle

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

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:

  1. Lines 10–30: Initialization — defines helper functions, clears memory, and calls the machine code loader subroutine at line 1760.
  2. Lines 40–300: Draws the static rifle-range backdrop using PLOT, DRAW, CIRCLE, and PRINT.
  3. Lines 310–320: Launches the title animation via RANDOMIZE USR 60000, then jumps to the main menu.
  4. Lines 330–620: Core gameplay loop — manages ammunition, moving gun, target hit detection, and round progression.
  5. Lines 990–1060: Round-definition subroutines (lines 1000–1050), each setting target UDGs, point value, and ink color; line 1060 handles round-clear fanfare.
  6. Lines 1110–1210: Title/menu screen with animated border and key selection.
  7. Lines 1220–1410: Multi-page instruction screens.
  8. Lines 1420–1730: End-of-game sequence, high-score entry, bubble sort, and leaderboard display.
  9. 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:

AddressCalled viaPurpose
60000RANDOMIZE USR 60000Title screen animation (copies/manipulates display memory)
60050RANDOMIZE USR 60050Game-start screen setup (copies attribute or pixel area)
60100RANDOMIZE USR 60100Used on instruction pages — likely a border or screen effect
60200RANDOMIZE USR 60200Per-frame animation during menu loop (title scrolling/twinkle)
60250RANDOMIZE USR 60250Border 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 380 to advance the subroutine destination — since go starts 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 7 area 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 function FN 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

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

People

No people associated with this content.

Scroll to Top