--- title: "Mole" id: 57276 type: "computer_media" slug: "mole" url: "http://localhost/computer_media/mole/" markdown_url: "http://localhost/computer_media/mole.md" published_at: "2024-10-04T07:59:08+00:00" modified_at: "2026-03-30T21:18:44+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/164_Mole.png" excerpt: "Move your crosshair with N and M keys, then strike when the mole surfaces — this BASIC game uses a clever display-file PEEK to detect a hit." 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: "Arcade" slug: "arcade" taxonomy: "genre" url: "http://localhost/type/arcade/" - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56734 title: "Timex Sinclair Public Domain Library Tape 1003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1003/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/164_Mole.png" media_type_tags: "Arcade, Game" --- This program implements a whack-a-mole style arcade game for one player. A mole (represented by a space in an inverse-video row) appears at a random horizontal position and the player moves a crosshair cursor left and right using the N and M keys. The game reads the keyboard via INKEY$ and detects a hit by PEEKing the ZX81’s display file pointer at system variables 16398–16399, checking whether the character under the cursor is 128 (inverse space), indicating the mole’s position. Score is accumulated in variable S and displayed when a hit is detected, after which the program halts. *** ## Program Analysis ### Program Structure The program is organised into a short main loop with two subroutines: 1. **Initialisation (lines 1–6):** Clears the screen by printing ten inverse-space rows, then sets the paddle column `P=3` and score `S=0`. 2. **Main game loop (lines 7–19):** Picks a random mole column, calls the display subroutines, checks for a hit or a random dwell timeout, then briefly animates the mole sinking back before repeating. 3. **Mole display subroutine (lines 20–23):** SCROLLs the play area and prints a row of inverse-space characters with a plain space gap at the mole’s position. 4. **Input/detection subroutine (lines 24–29):** Reads the display file at the cursor position to detect a hit, prints a crosshair `+`, increments the stroke counter, and moves the cursor via N/M keys. 5. **End of game (lines 30–40):** Prints the score at the cursor position and STOPs. ### Inverse-Video Graphics Technique The playing field is rendered entirely using inverse-space characters (character code 128 on the ZX81). The mole appears as a gap — a plain space — in an otherwise solid inverse row, making it visually distinct without any custom graphics. Line 3 prints the initial border rows using `"% % "` where `%` represents inverse characters. ### Display-File PEEK Hit Detection The most technically interesting aspect is the hit-detection method in lines 24–25. Rather than tracking the mole’s column numerically and comparing it to the cursor column `P`, the program directly reads the ZX81 display file: - System variables at addresses 16398 and 16399 hold the low and high bytes of the display file start address (D_FILE). - `PEEK 16398 + 256*PEEK 16399` calculates the base address of the display file. - `PEEK` of that address retrieves the character currently under the crosshair position. - A result of `128` (inverse space) means the cursor is over part of the mole row, i.e., a hit; line 10 and 17 branch to the end sequence if this is true. This is a compact and elegant approach that avoids storing or comparing the mole’s column explicitly. ### Key BASIC Idioms - **Movement via boolean arithmetic:** Line 28 uses `P = P + (INKEY$="M") - (INKEY$="N")`. On the ZX81, a true condition evaluates to 1 and false to 0, so this single expression moves the cursor right on M and left on N without any IF statements. - **Random dwell time:** Line 11 uses `IF RND<.7 THEN GOTO 8` to keep the mole visible for a geometrically distributed random duration before it dips back down, giving the game unpredictable timing. - **Partial disappearance animation:** Lines 12–18 loop four times, SCROLLing and occasionally printing an inverse-space pair at the cursor row (line 15) to simulate the mole partially hiding. ### Variable Summary | Variable | Role | | --- | --- | | `A` | Mole column position (lines 7, 22); also loop counter (line 12) | | `P` | Player crosshair column | | `S` | Stroke (swing) counter, displayed as score | | `R` | Character read from display file at cursor position | ### Bugs and Anomalies - **Dual use of `A`:** Variable `A` is used both as the mole’s column (set at line 7, used at line 22) and as the loop counter in the animation loop (lines 12–18). Because the FOR loop overwrites `A`, the mole’s original column is lost during the sink animation. This is intentional — the mole position is no longer needed at that stage — but could cause confusion. - **Score semantics:**`S` counts every pass through the input subroutine (line 27), not just successful hits. It therefore measures the total number of swings rather than hits, making it more of a “how many attempts” counter. A hit ends the game immediately, so the final value of `S` is always the number of misses plus one. - **No boundary check on `P`:** The cursor column `P` is not clamped, so the player can move it off the left or right edge of the screen, potentially causing a display error. - **PRINT AT row discrepancy:** The mole is printed at row 9 (lines 14–15, 21), but the crosshair is printed at row 3 (line 24). The crosshair and mole are on different rows, so the PEEK detection does not compare the crosshair character against the mole row directly — it reads from the display file base address, which on a real ZX81 would be the very first character of the display file (the top-left newline marker), not the character at row 3, column P. This may represent a bug in the intended hit-detection logic. ## Source Code ``` 1 REM "MOLE" 2 FOR A=1 TO 10 3 PRINT "% % " 4 NEXT A 5 LET P=3 6 LET S=0 7 LET A=INT (RND*5+1) 8 GOSUB 20 9 GOSUB 24 10 IF R=128 THEN GOTO 30 11 IF RND<.7 THEN GOTO 8 12 FOR A=1 TO 4 13 SCROLL 14 PRINT AT 9,0;"% % " 15 IF RND<.3 AND A>1 AND A<4 THEN PRINT AT 9,P;"% " 16 GOSUB 24 17 IF R=128 THEN GOTO 30 18 NEXT A 19 GOTO 7 20 SCROLL 21 PRINT AT 9,0;"% % % % % % % " 22 PRINT AT 9,A;" " 23 RETURN 24 PRINT AT 3,P; 25 LET R=PEEK (PEEK 16398+256*PEEK 16399) 26 PRINT "+" 27 LET S=S+1 28 LET P=P+(INKEY$="M")-(INKEY$="N") 29 RETURN 30 PRINT AT 4,P;S 40 STOP 50 SAVE "1016%4" 60 LIST ```