Goblin

This file is part of and Timex Sinclair Public Domain Library Tape 1002. Download the collection to get this file.
Developer(s): Ryan Gray
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Game

Goblin is a single-player maze/chase game in which the player navigates a character around a screen filled with randomly placed coins (character code 180) and goblins (character code 8), trying to collect all 25 coins without touching a goblin. The screen is built using POKE operations directly into the ZX81 display file starting at address 17859, with the player represented by character code 151 and the background by code 128 (inverse space). Movement is controlled by keys 5 and 8, translating to a one-dimensional offset of ±1 applied to the display file address, wrapping around via a 726-byte modulus corresponding to the 33-byte-wide display file rows. When all 25 coins are collected the screen is reinitialized, and touching a goblin triggers a flashing death animation before prompting for a new game with high score persistence across restarts.


Program Analysis

Program Structure

The program is divided into clearly separated phases:

  1. Initialization (lines 1–5): Resets score, randomizes, and clears the border register via POKE 16418,0.
  2. Screen setup (lines 10–50): Fills the screen with inverse spaces, then randomly scatters 60 goblins (code 8) and 25 coins (code 180) into the display file.
  3. Game loop (lines 97–1007): Reads a keypress, computes a new display-file address for the player, checks for collisions, and updates the screen.
  4. Goblin collision (lines 2000–2005): Flashes the player position and jumps to game-over.
  5. Coin collection (lines 3000–3006): Awards 5 points, increments a coin counter, and restarts the level at 25.
  6. Game over (lines 9000–9020): Prompts replay, updates high score, and either restarts or saves/runs.

Display File Manipulation

Rather than using PRINT AT for all game objects, the program works directly with ZX81 display file memory. The display file starts at address 17859 (line 25/35 base), and each logical row is 33 bytes wide (32 character cells plus a NEWLINE token, code 118). The player occupies address 18435 initially (POS=18435), which corresponds to row 18, column 15 — consistent with the PRINT AT 18,15 marker placed at line 13.

Movement and Address Arithmetic

Movement is strictly horizontal: key “8” moves right (D=1), key “5” moves left (D=-1), and no key means D=0. The new position is calculated as POS = POS - 33 + D at line 1000, which is unusual — subtracting 33 then adding D effectively moves one row up and then adjusts horizontally. This means the game has no downward movement and wraps vertically via the modulus check at line 1002 (POS < 17859 → POS + 726, where 726 = 22 rows × 33 bytes).

Line 1001 provides a simple newline-avoidance check: if the target cell contains code 118 (NEWLINE), the position is decremented by 1 to land on the last character of the previous row instead of the line terminator.

Character Codes Used

CodeMeaningUsage
8Goblin / shift-H graphicHazard; collision = death
23Flash animation frameDeath/coin flicker effect
118NEWLINERow boundary detection
128Inverse space (background)Empty cell; erasing player trail
151Player characterCurrent player position
180Coin graphicCollectible; collision = score

Collision Detection

Collision is handled purely by PEEKing the target display file address after computing the new position. Code 8 triggers the goblin-death routine (line 2000); code 180 triggers coin collection (line 3000). Because the check happens before the player is drawn to the new cell, there is no risk of the player overwriting itself.

Coin and Goblin Placement

Lines 25–40 use a rejection-sampling loop to place objects randomly within the 726-byte display file window. For goblin placement (lines 25–26), addresses are rejected if they contain a NEWLINE (118), equal the player start address (18435), or equal 18402 (row 18, col 12 — the area near the start). For coin placement (lines 35–36), only cells already containing code 128 (empty background) that are not the player start are accepted, preventing coins from overwriting goblins or each other.

High Score Persistence

The high score variable HS is initialized at line 1 but is only reset there on a cold start. When the player chooses to replay (line 9006: GOTO 2), execution skips line 1, preserving HS across games within the same session. On exit, SAVE followed by RUN at lines 9010–9020 reloads the program, which will then reset HS — so high score does not survive a tape reload.

Notable Techniques and Anomalies

  • The polling loop at lines 9003–9005 waits for a non-empty INKEY$ rather than using PAUSE, which is a common ZX81 input-wait idiom.
  • Line 9006 checks INKEY$="Y" immediately after the previous loop exits on a non-empty key — if the key pressed was not “Y”, execution falls through to the SAVE/RUN sequence. There is no explicit “N” check, so any key other than “Y” causes a save/reload.
  • The death flash loop at lines 2001–2004 alternates the player cell between code 151 and code 23 eight times without any delay, so the visual effect may be extremely brief on real hardware.
  • At line 26, the condition IF PEEK A=118 OR A=18435 OR A=18402 is parsed as IF (PEEK A=118) OR (A=18435) OR (A=18402) — the last two conditions test the address value directly rather than what it contains, which is the intended behavior but may read confusingly.
  • The player start is placed using both PRINT AT 18,15;"%*" (line 13) and POKE POS,151 (line 102); the PRINT at setup is essentially overwritten by the game loop immediately.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10051 – 10121.

Related Products

Related Articles

Related Content

Image Gallery

Goblin

Source Code

   0 REM  GOBLIN- ZX81 VERSION            ADAPTED BY RYAN GRAY
   1 LET HS=0
   2 LET SC=0
   3 RAND 
   5 POKE 16418,0
  10 FOR A=0 TO 23
  11 PRINT AT A,0;"% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % "
  12 NEXT A
  13 PRINT AT 18,15;"%*"
  20 FOR C=1 TO 60
  25 LET A=INT (RND*726)+17859
  26 IF PEEK A=118 OR A=18435 OR A=18402 THEN GOTO 25
  30 POKE A,8
  31 NEXT C
  32 FOR C=1 TO 25
  35 LET A=INT (RND*726)+17859
  36 IF PEEK A<>128 OR A=18435 THEN GOTO 35
  40 POKE A,180
  50 NEXT C
  97 LET N=0
  98 LET POS=18435
  99 PRINT AT 23,15;"%H%I%G%H% %S%C%O%R%E%=";HS
 100 PRINT AT 0,10;"G% O% B% L% I% N"
 101 PRINT AT 23,1;"%S%C%O%R%E%=";SC
 102 POKE POS,151
 103 LET D$=INKEY$
 105 LET D=(D$="8")-(D$="5")
 106 LET OLD=POS
\n1000 LET POS=POS-33+D
\n1001 IF PEEK POS=118 THEN LET POS=POS-1
\n1002 IF POS<17859 THEN LET POS=POS+726
\n1004 IF PEEK POS=8 THEN GOTO 2000
\n1005 IF PEEK POS=180 THEN GOTO 3000
\n1006 POKE OLD,128
\n1007 GOTO 102
\n2000 POKE OLD,128
\n2001 FOR X=1 TO 8
\n2002 POKE POS,151
\n2003 POKE POS,23
\n2004 NEXT X
\n2005 GOTO 9000
\n3000 POKE OLD,128
\n3001 POKE POS,151
\n3002 POKE POS,23
\n3003 LET SC=SC+5
\n3004 LET N=N+1
\n3005 IF N=25 THEN GOTO 5
\n3006 GOTO 101
\n9000 PRINT AT 10,7;"%A%N%O%T%H%E%R% %G%A%M%E%?% %(%Y%/%N%)"
\n9001 IF SC>HS THEN LET HS=SC
\n9002 PRINT AT 23,15;"%H%I%G%H% %S%C%O%R%E%=";HS
\n9003 FOR Q=1 TO 10
\n9004 NEXT Q
\n9005 IF INKEY$="" THEN GOTO 9003
\n9006 IF INKEY$="Y" THEN GOTO 2
\n9010 SAVE "1009%9"
\n9020 RUN 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top