--- title: "Gunner" id: 57118 type: "computer_media" slug: "gunner" url: "http://localhost/computer_media/gunner/" markdown_url: "http://localhost/computer_media/gunner.md" published_at: "2024-10-03T23:48:18+00:00" modified_at: "2026-03-30T21:19:49+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/67_Gunnr.png" excerpt: "A scrolling-gallery shooter where alphabetic targets race across the screen and a mystery symbol spells game over if you let it escape your cannon's sight." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56733 title: "Timex Sinclair Public Domain Library Tape 1002" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1002/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/67_Gunnr.png" media_type_tags: "Game" --- # Gunner Gunner is a shooting gallery game where the player moves a cannon left and right along the bottom of the screen, firing upward at two scrolling rows of random alphabetic characters. The scrolling text is stored in a single one-dimensional string array DIM A$(1,6000), with a window into it advancing by two characters per frame to create the horizontal scroll effect. Characters are drawn from CHR$(166) to CHR$(191), corresponding to the ZX81’s block-graphic and letter characters. The player scores points based on the ASCII value of the character hit (R-165), making letters deeper in the alphabet worth more. A special “@@ ” target occasionally appears in the scroll; if it reaches the leftmost visible position without being shot, the round ends. *** ## Program Analysis ### Program Structure The program begins with a title/instructions block (lines 500–600) before jumping back to initialization. The main game loop runs from line 60 onward, with the bullet flight handled inline rather than in a subroutine. 1. **Lines 1–2:** REM header and `GOTO 500` to skip to the title screen. 2. **Lines 5–15:** Initialize high score, cannon height, and the large scroll buffer array. 3. **Lines 20–40:** Pre-fill odd-indexed positions 1–65 of `A$` with random characters (CHR$ 166–191). 4. **Lines 45–135:** Main game loop — display scores, draw the two scrolling rows and the cannon, handle keyboard input. 5. **Lines 140–210:** Bullet movement upward; uses PEEK of the display file to detect hits. 6. **Lines 250–310:** Hit processing — calculate score, erase the hit character from the scroll buffer, reset bullet. 7. **Lines 350–470:** End-of-round sequence; checks high score, prompts replay. 8. **Lines 500–600:** Title screen and instructions. 9. **Lines 700–710:** SAVE and RUN trailer. ### Scroll Buffer Design All scrolling data lives in `DIM A$(1,6000)`, a single-row string array 6000 characters wide. The pointer `N` starts at 2 and increments by 2 each frame. The display routine prints `A$(1,N TO N+31)` on row 3 and `A$(1,N+33 TO N+65)` on row 5, giving two 32-character wide lanes. New characters are appended at `A$(1,N+65)` each frame, so the buffer grows rightward as the game progresses. Because `N` only increments, the program will eventually exhaust the 6000-character buffer after approximately 2967 frames without a round end. ### Hit Detection via PEEK Rather than tracking bullet-vs-character coordinates algebraically, the game uses a direct display-file inspection technique. Lines 155–156 place the print position at the bullet’s location, PEEK the display file address from system variables at addresses 16398–16399, read the character code already at that screen cell, then overprint with `"."`. If the PEEKed value `R` is non-zero (i.e., something other than a space was already there), a hit is registered. - `PEEK 16398 + 256 * PEEK 16399` gives the address of the current print position in the display file. - A score contribution of `R - 165` is calculated, so CHR$(166)=’A’ scores 1, up to CHR$(191) scoring 26. ### Special Target (“@@”) Approximately once every 40 frames (`INT(RND*40)=10`), the character appended to the buffer is the two-character sequence `\@@` (rendered as a block-graphic pair). Line 95 checks whether `A$(1,N+1)` equals this token; if so, the round ends with a “got away” message. This means the special target must scroll entirely off the left edge of the visible window without being shot to trigger the loss condition. ### Cannon Rendering The cannon graphic occupies two screen rows: row 21 shows `" \.: "` (the barrel, using ZX81 block graphics) and row 20 shows `" . "` (the base). The horizontal position is controlled by variable `Z`, clamped between 1 and 28 by lines 110–120. Keys `5` and `8` move left and right; key `7` fires. ### Bullet Flight The bullet is a single `"."` character that travels from row 19 upward to row 3 in steps of 2 (`H` decrements by 2 per frame). Flag variable `X` tracks whether a bullet is in flight: `X=1` means in-flight, `X=0` means idle. When `X<>0`, keyboard input is skipped (line 105), so the player cannot move or fire again until the bullet resolves. ### Score Erasure on Hit When a hit occurs, the program attempts to blank the character in the scroll buffer at the appropriate offset. Line 280 handles a hit on row 3 (`H=3`) using `A$(1,N+(Z-1))=" "`, and line 290 handles row 5 (`H=5`) using `A$(1,N+(32+Z))=" "`. The index arithmetic is approximate and may be off by one depending on exact scroll position, which can cause occasional visual artifacts. ### Notable Bugs and Anomalies - The odd-index pre-fill loop (lines 20–40, `STEP 2`) only populates positions 1, 3, 5, …, 65, leaving even positions as null bytes (CHR$ 0). This means the initial scroll contains interleaved garbage/null characters until the per-frame append (line 80) fills the buffer further right. - The bullet height variable `H` is initialized to 19 at line 8 but is reset to 19 only at line 300 (after a hit). A missed shot that reaches row 3 resets `X=0` at line 200 but does not reset `H`, so the next shot starts from wherever `H` was last left (row 1 or 3), not from row 19. This is a definite bug. - The INPUT at line 430 accepts full words “YES”/”NO” as well as single letters, but the GOTO loop is only re-entered if *none* of the four options match, meaning any other input is also silently accepted and falls through — the condition logic at line 460 then treats an unrecognized answer as “no”. - The `DIM A$(1,6000)` on line 10 declares a two-index string array but is accessed throughout with single-index slice notation `A$(1,…)`. This is valid ZX81 syntax for a 1-row string array. ### Key Variables | Variable | Role | | --- | --- | | `A$` | 6000-character scroll buffer | | `N` | Current read offset into scroll buffer | | `Z` | Cannon horizontal position (column) | | `H` | Bullet vertical position (row) | | `X` | Bullet in-flight flag (0=idle, 1=in-flight) | | `S` | Current score | | `HS` | High score (persists across rounds) | | `R` | PEEKed character code at bullet position | ## Source Code ``` 1 REM ***GUNNER*** 2 GOTO 500 5 LET HS=0 8 LET H=19 10 DIM A$(1,6000) 15 LET X=0 20 FOR N=1 TO 65 STEP 2 30 LET A$(1,N)=CHR$ (INT (RND*26)+166) 40 NEXT N 45 LET N=2 50 LET Z=0 55 LET S=0 60 PRINT AT 0,0;"SCORE","HIGH-SCORE";AT 1,0;S,HS 70 PRINT AT 3,0;A$(1,N TO N+31);AT 5,0;A$(1,N+33 TO N+65);AT 21,Z;" \.: ";AT 20,Z;" . " 80 LET A$(1,N+65)=CHR$ (INT (RND*26)+166) 90 IF INT (RND*40)=10 THEN LET A$(1,N+65)="\@@" 95 IF A$(1,N+1)="\@@" THEN GOTO 350 100 LET N=N+2 105 IF X<>0 THEN GOTO 140 110 IF INKEY$="8" AND Z<28 THEN LET Z=Z+1 120 IF INKEY$="5" AND Z>1 THEN LET Z=Z-1 130 IF INKEY$="7" THEN GOTO 140 135 GOTO 70 140 LET H=H-2 150 PRINT AT H,Z+1; 155 LET R=PEEK (PEEK 16398+256*PEEK 16399) 156 PRINT "." 160 PRINT AT H+2,Z+1;" " 170 LET X=1 180 IF R<>0 THEN GOTO 250 190 IF H<3 THEN PRINT AT H,Z+1;" " 200 IF H<3 THEN LET X=0 210 GOTO 70 250 IF R-165<0 THEN LET R=165 255 LET S=S+(R-165) 260 LET X=0 270 PRINT AT 1,0;S 280 IF H=3 THEN LET A$(1,N+(Z-1))=" " 290 IF H=5 THEN LET A$(1,N+(32+Z))=" " 300 LET H=19 310 GOTO 70 350 CLS 360 PRINT "SCORE","HIGH-SCORE" 370 IF S>HS THEN LET HS=S 380 PRINT S,HS 390 PRINT 400 PRINT "THE ""\@@"" GOT AWAY." 410 PRINT 420 PRINT "DO YOU WANT TO TRY AGAIN(Y/N)?" 430 INPUT Z$ 440 IF Z$<>"YES" AND Z$<>"Y" AND Z$<>"NO" AND Z$<>"N" THEN GOTO 430 450 CLS 460 IF Z$="YES" OR Z$="Y" THEN GOTO 8 470 STOP 500 PRINT TAB 12;"GUNNER" 510 PRINT TAB 12;"------" 520 PRINT 530 PRINT "THE POINT OF THIS GAME IS TO GET AS MANY POINTS AS POSSIBLE BY SHOOTING THE LETTERS; BUT YOU MUST NOT LET THE ""\@@"" GET AWAY." 540 PRINT "THE FURTHER THE LETTER IS IN THE ALPHABET THE MORE ITS WORTH. FOR EXAMPLE ""%Z"" IS WORTH MORE THAN ""%A"". TO MOVE THE GUN YOU USE THE" 550 PRINT "CURSOR KEYS AND THE UPWARD POINTING CURSOR KEY TO FIRE.",,,"GOOD LUCK" 560 PRINT 570 PRINT "HIT ANY KEY TO CONTINUE." 580 IF INKEY$="" THEN GOTO 580 590 CLS 600 GOTO 5 700 SAVE "1006%7" 710 RUN ```