Surround is a two-player snake-style game where each player steers a continuously moving trail across a 32×24 character grid, attempting to force the opponent into colliding with a filled cell. The board is drawn using PRINT AT with “+” borders, and player positions are tracked with screen memory read-backs via PEEK on the display file address stored at system variable addresses 16396/16397. Keyboard input is read by PEEKing the ZX81 keyboard port mirror at address 16421, with each player having five keys mapped to up, down, left, and right movement, decoded by comparing the raw port byte against a set of expected values. Scores are maintained in variables SX and SB, and the first player to reach 15 wins before the program saves and restarts.
Program Analysis
Program Structure
The program is a two-player “Surround” (snake/trail) game. The main flow is:
- Lines 1–6: Initialise scores
SX(player 1) andSB(player 2) to 0. - Line 7: Record the display file base address in
Ofrom system variables 16396/16397. - Lines 10–60: Clear keyboard queue (POKE 16418,0) and draw the bordered playing field.
- Lines 70–145: Set starting positions and velocity vectors for both players.
- Lines 150–295: Main game loop — collision detection, drawing, keyboard reading, movement.
- Lines 300–320 / 400–410: Score a point for the surviving player; restart at line 7 or stop at 15.
- Lines 500–600: Save and auto-run on game over.
Display File Collision Detection
Rather than maintaining a separate data structure to record the trail, the program reads back screen memory directly. Line 7 calculates O, the base address of the ZX81 display file, by combining the low and high bytes at system variable addresses 16396 and 16397:
LET O=PEEK 16396+256*PEEK 16397
Lines 150 and 160 then test whether the cell a player is about to move into is already occupied:
IF PEEK (O+1+Y+33*X)<>0 THEN GOTO 400
The ZX81 display file stores each row as 33 bytes (32 character cells plus a NEWLINE token 0x76 at the end); a cell value of 0 represents a space (empty). The offset formula 1+Y+33*X accounts for the leading HALT byte at the start of the display file. Any non-zero value means the cell is occupied and a collision has occurred.
Keyboard Handling
Input is read by PEEKing address 16421, which on the ZX81 holds the last keyboard scan result. Each of the five keyboard ports is reflected in the byte, with bits going low for pressed keys. The program decodes directions by comparing A against a fixed set of expected byte values for each key group:
| Lines | Player | Direction | Key group (per REM) |
|---|---|---|---|
| 185 | 1 (X) | Down (+X1) | 1–5 |
| 190 | 1 (X) | Up (−X1) | Z–V / Q–T via port bytes |
| 210 | 1 (X) | Right (+Y1) | Q–T row |
| 215 | 1 (X) | Left (−Y1) | A–G row |
| 230 | 2 (B) | Up (−B1) | 6–0 / Y–P |
| 235 | 2 (B) | Down (+B1) | B–M / H–ENTER |
| 245 | 2 (B) | Right (+C1) | Y–P row |
| 250 | 2 (B) | Left (−C1) | H–ENTER row |
Lines 200 and 220 enforce single-axis movement for player 1 (if a row direction is set, the column direction is zeroed), and lines 240 and 260 do the same for player 2, preventing diagonal movement.
Player Representation
Player 1’s head is drawn with CHR$ 128 (a filled block graphic) and player 2’s with CHR$ 136 (a different block graphic). Because neither character is space (code 0), the trail left behind is automatically detectable by the collision-detection PEEK. No explicit trail array or erase step is needed — the screen itself serves as the game state.
Scoring and Restart
When a collision is detected, the opponent’s score is incremented (lines 300 or 400). If the score is less than 15, the program jumps back to line 7, which re-reads the display file address and redraws the board while preserving the score variables SX and SB (lines 5–6 are only executed once at the very start via RUN at line 600). Reaching 15 triggers STOP (line 320) or falls through to the SAVE/RUN sequence.
Notable Techniques and Anomalies
- The display-file base address is recalculated each round (line 7) to handle any shift in the display file that might occur during the redraw — a robust defensive measure for ZX81 dynamic display files.
- The POKE to 16418 at line 10 clears the keyboard repeat/debounce system variable, ensuring clean input at the start of each round.
- The value
125(byte0x7D) appears in both line 215 (Y1=−1, left for player 1) and line 235 (B1=1, down for player 2), suggesting a key that is shared or an overlap in the keyboard matrix decoding. This could cause simultaneous incorrect directional changes under certain key combinations. - Player 1’s initial direction is set to move downward (
X1=0, Y1=1), and player 2’s to move leftward (B1=0, C1=−1), giving both players an immediate movement from their starting positions in opposite directions. - There is no explicit loop delay; the game speed is entirely determined by BASIC interpreter execution speed, which is naturally limited on the ZX81.
Content
Source Code
1 REM SURROUND
2 REM BLACK 1-5 UP,Z-V DOWN,Q-T RIGHT,AG LEFT.
3 REM GREY 6-0 UP,Y-P RIGHT,B-M DOWN; H-ENTER LEFT.
5 LET SX=0
6 LET SB=0
7 LET O=PEEK 16396+256*PEEK 16397
10 POKE 16418,0
20 PRINT AT 0,0;"++++++++++++++++++++++++++++++++"
30 FOR P=1 TO 22
40 PRINT "+ +"
50 NEXT P
60 PRINT ;"++++++++++++++++++++++++++++++++"
70 PRINT AT 2,2;STR$ SX;AT 2,28;STR$ SB
80 LET X=12
90 LET Y=3
100 LET B=12
110 LET C=28
120 LET X1=0
130 LET Y1=1
140 LET B1=0
145 LET C1=-1
150 IF PEEK (O+1+Y+33*X)<>0 THEN GOTO 400
160 IF PEEK (O+1+C+33*B)<>0 THEN GOTO 300
170 PRINT AT X,Y;CHR$ (128);AT B,C;CHR$ (136)
180 LET A=PEEK 16421
185 IF A=238 OR A=222 OR A=190 OR A=254 OR A=126 THEN LET X1=1
190 IF A=247 OR A=231 OR A=215 OR A=183 OR A=119 THEN LET X1=-1
200 IF X1<>0 THEN LET Y1=0
210 IF A=235 OR A=251 OR A=219 OR A=187 OR A=125 THEN LET Y1=1
215 IF A=237 OR A=221 OR A=253 OR A=189 OR A=125 THEN LET Y1=-1
220 IF Y1<>0 THEN LET X1=0
230 IF A=231 OR A=239 OR A=235 OR A=237 OR A=238 THEN LET B1=-1
235 IF A=119 OR A=123 OR A=125 OR A=126 OR A=127 THEN LET B1=1
240 IF B1<>0 THEN LET C1=0
245 IF A=215 OR A=219 OR A=223 OR A=221 OR A=222 THEN LET C1=1
250 IF A=183 OR A=187 OR A=189 OR A=191 OR A=190 THEN LET C1=-1
260 IF C1<>0 THEN LET B1=0
270 LET X=X+X1
280 LET Y=Y+Y1
290 LET B=B+B1
295 GOTO 150
300 LET SX=SX+1
310 IF SX<15 THEN GOTO 7
320 STOP
400 LET SB=SB+1
410 IF SB<15 THEN GOTO 7
500 SAVE "1013%5"
600 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
