Commodore 64 · 1984 · Novagen Software

About this page

This minisite was contributed by air. It’s currently claimed by air who is editing it to reach a Gold tier standard.

Footprint

Every byte of the C64's 64 KB, one pixel each, 512 to a row. Lit pixels are what this game is: its code, graphics, level data, sound, text, tables and variables, exactly where they sit in memory. Dim areas are screen and working memory; dark is unused.

WhatBytesOf 64 KB
Program41,62763.5 %
Code8,69913.3 %
Graphics16,17224.7 %
Level data1,1041.7 %
Sound1280.2 %
Text2,3683.6 %
Tables2,0523.1 %
Variables11,10416.9 %
Screen, bitmap, colour, stack, I/O14,67822.4 %
ROM the game runs under8,19212.5 %
Unused1,0391.6 %

Status

Tiersilver (claimed)
Coverage100 % of the bytes the game uses have a description
Build analysedNovagen release as a PC64 program file (ENCOUNTR.P00), crunched; no trainer; possibly a crack (the level-select check is NOPed and seven junk bytes run at game start)
Copyagent-draft
Toolsemulator: VICE 3.10 via vice-mcp 3.11.0 (macOS arm64 GUI build), disassembler: regenerator2000 0.9.20, python: 3.9.6, host: macOS 26.5 (Darwin 25.5.0), Apple silicon
Modelclaude-fable-5-1
Kit version0.0.7

Contributors

Other places to love this game

What the game does, and where we found it

Read this before annotating code. What the game is documented to do, with verification status against the binary. Statuses: open (documented, not found yet), traced (in the code, could not be exercised; say what was tried), confirmed (in the code, consistent with the emulator), live (observed directly), differs (the code does something else). "Absent" is not a status.

Sources:

The sources disagree in places: the wiki says "seventeen UFO types" and "10–19 enemies per level"; the comparison blog says two adversary types (saucers and homing drones) with two new saucer attack strategies per level; the manual says eight levels. Each is a claim to check, not a fact.

Features

Title screen and setup

FeatureStatusWhere
Title screen: ENCOUNTER, BY PAUL WOAKES, © 1984 NOVAGEN SOFTWARE, SCORE / LAST SCORE / HIGH SCORE, F5 NOVICE, F7 BEGIN GAMElivetitle loop at $9CBA (orientation)
F5 cycles skill level: Novice, Advanced, Expert (wiki); the title shows the current onetraced$9D2B-$9D4B: F5 ($C5 = 6) steps $62 through 2 NOVICE (default), 1 ADVANCED, 0 EXPERT and copies the name from hidden title rows 20-21 ($6B20 + 16 × skill) to $6B88; skill picks a column of the tables at $8F80
F7 begins the gamelive$9D27 cmp #$03 (F7's $C5 code) → $9D68
Number keys restart at a level already completed in this session (manual, wiki: 1–8)differs (live)$9CD4 maps keys 8..1 through the table at $6931 to $A8 = index × 16; the comparison with the highest level reached ($68CB) at $9CE3 is followed by two NOPs instead of a branch, so any level can be chosen at any time
Attract / demo mode when the title is left alone: the game plays itselflivetitle loop counts $F7/$F8/$F9 down and jumps to $9D7A, which sets $A8 = $70 and starts play through $9E46; seen at level 2 with $A8 = $70 and no input given (reference/demo-level2.png)
Last score and high score kept across gamestraced$ABED copies the SCORE line of the title screen to LAST SCORE ($6AC2), compares $8BA0-$8BA3 with the high score $8BA4-$8BA7 in BCD and rewrites the HIGH SCORE digits at $6B12

Play

FeatureStatusWhere
First-person view: blue sky with clouds, horizon line, green ground, black cylindrical obelisks (pillars) on a flat plainlivereference/play-level1-start.png; sky, horizon and ground are $D021 bands from the raster chain $B2FE-$B3EA, clouds and the hill line are sprites 2-7 ($B9A5), obelisks are composited characters ($AD76)
Joystick in port 2: forward, reverse, turn left and right, and diagonals rotate (manual); fire button shoots; hold for continuous fire (wiki)traced (turning right seen live)$BC31 reads $DC00; $A1F7 dispatches up → $B508 forward, down → $B56C reverse, right → $B4D8, left → $B4F0; fire → $ACB6 with an 8-pass lock $5C, so holding fire repeats
Status row: score (7 digits), L<level>, E<enemies remaining>, S<shields> (S4 at the start of a game)livescreen row 0 at $6000, wide font
Radar (octagonal scanner under the view) shows enemies relative to the player at centretraced (dot seen live)sprite 0 is the blip, placed at $AB21-$AB44 from the enemy's bearing and range through a polar-to-cartesian conversion and clamped to the scanner box
Console lights either side of the radar: yellow = saucer present, blue flashing = saucer firing, red flashing plus a low whine rising in pitch as it approaches = homing missile (manual)traced$AA70 recolours five cells of rows 19/21/23: row 21 yellow (7) at $A466 when a saucer launches, back to 8 at $A8CC; row 23 light blue ($0E) at $AA22 when it fires, reset at $A0C6; row 19 red/pink at $A7FB/$A800 while a missile lives; the whine at $A811 uses range $31
Shields: start with four; losing the last ends the game; maximum nine (manual, wiki)live (four at start, plus one at the last kill, game over on the death after S0: six deaths in the demo run)the count is the status digit at $6026/$6027: $9F89 writes 4, $ABDC subtracts one on death, $ABDA goes to game over at zero, $A91D adds one when the last enemy of a level dies, capped at 9
Obelisks block movement, are indestructible and stop both player and enemy shots (wiki); 64 of them on the field (comparison blog)tracedobjects 32-95 are 64 obelisks of type 3 on a 32-unit grid with a cell map at $8A00; $AC30 stops shots and enemy shots (deflecting them), $B50C refuses a player move within $43 units and $B53F plays the bump
Homing missiles are not stopped by obelisks and must be shot (manual)traced (two missile spawns counted live)the missile is object 2 driven by $A7F0, which never calls the obelisk test $AC30; $A894 tests it against the player's shots
Enemies appear one at a time from a white warp gate (wiki)live (the flash rectangle $BA04 ran 483 passes for 13 spawns)one enemy slot (object 2); $A42C spawns a saucer and shows object 7 (shape family $E0) as the warp-in flash from the spawn until $20 passes after the saucer starts moving (live: passes 52 to 131 of a recorded spawn, the saucer starting at 100)
Saucer behaviours: evasion, scattered shots, clouds of fire, permanent fire (wiki); two new strategies per level (blog)traced16 types; type = random in 0..2 × level − 1 ($A42C), the first two spawns after a level-up being the two new types ($9E); dispatch table $AAB5 has four handlers: default $A5D6, burst fire $A563, script fire $A5C7, permanent fire $A70D; fire scripts at $8C00
Each level has 10–19 enemies (wiki)confirmed$AA8F: a random byte masked to $10-$1F into $8BA8 (BCD); E11 in the snapshot, E15 on another run
UFO point values 100–1600 (wiki)tracedtable at $8BB0: (type + 1) × 100 for 16 types; a missile scores $8BAC = 500; the award is paid level times ($A1 at $AAC8, $A8B2)
Explosion colour sequence white, yellow, orange, red, brown, black, the same for every enemy (blog)traced$A14F indexes the nibble-pair table at $8CF0 by the fragment lifetime; $B467 blends the two nibbles on alternate frames
Sound: two simultaneous voices for enemy indicators; missile whine; engine noise in the transition (blog)tracedvoice 3 saucer hum from range ($A5F2), voices 2 and 3 missile whine ($A811), voice 1 engine loop ($A231/$B415), warp tone ramp ($A4CD)
Space pauses and resumes; F1 aborts the game (manual, wiki)differs (live)F1 aborts to the title ($A118); SPACE pauses ($A12A → $A13B) but the pause ends on a joystick direction, not on SPACE

Between levels

FeatureStatusWhere
When the enemy counter reaches zero a stargate opens: a black rectangular hole whose position shows on the scanner; the player must line up with its centre (manual)live (opened once in the demo after the count was poked to 1)$A8B2 at count zero sets $69 and adds a shield; $A2A1 activates object 3 at the last enemy's X for $F0 passes; $BA04 draws it as a rectangle rising from the horizon; $A4EC tests entry with $AD26
Warp phase: propelled at high speed through a hail of spheres to avoid (manual); the wiki calls it a 30-second missile gauntletlive (351 passes of $A713 before a sphere hit)$A713/$A757: scene $80, speed ramps by $A5 toward $A7E8[level], spheres (shapes $A7E0) spawn in slots 8-23 every 3 passes, about four 256-frame periods long
Success awards an extra shield, maximum nine; missing the gate or hitting a sphere returns the player to the previous level and costs one shield (manual)differs (partly)the extra shield is awarded at the last kill ($A91D), before the gate; missing the gate ($A552) returns to play with a fresh enemy count and no shield change; a sphere hit runs the ordinary death ($AB9A), which does cost a shield
Eight levels (manual); odd and even levels alternate colour schemes (blog)tracedlevel index $63 runs $70 (level 1) down to $00 (level 8) in steps of $10; scene records at $8010 + $63 alternate a coloured sky (odd) and a black sky (even); $A798 increments the level digit

Cheats reported

FeatureStatusWhere
POKE 30430,0 ($76DE) or POKE 34722,0 ($87A2) for invulnerability (wiki, marked untested there)open$76DE is inside the sprite-image block at $7000 and $87A2 inside the maths tables; neither is code on the death path, so these POKEs most likely belong to a different memory layout (another crack or the tape version); not tested

Beyond the documentation

Found in the code, not in the manual.

Open questions

How the analysed state was reached

How to get from the contributor's own copy to the analysed state. Someone else must be able to follow this exactly.

The image

ENCOUNTR.P00, a PC64-format program file (26-byte C64File header, filename ENCOUNTR, then a PRG). Stripping the header gives a 20,263-byte PRG loading at $0801 ending at $5726. The BASIC stub is one line, 1001 SYS 2066, so the machine-code entry is $0812. The program is crunched: the loader at $0812 installs a decruncher in the stack page ($00FA–$01A0) and the tape buffer ($0333–$03D9) and jumps to $0100. No trainer, cracktro or menu appears; the title screen credits "Paul Woakes", "© 1984 Novagen Software". Whether this is the original release or a cracked copy is unknown: the cruncher is scene-style (self-modifying page copy, $01 = $30 all-RAM during depack) and the title screen shows nothing beyond the publisher's own credits.

The kit works from work/encounter.prg (the PRG with the P00 header removed: tail -c +27 encounter_0_original.p00 > encounter.prg).

From power-on to play

  1. Autostart work/encounter.prg (vice_autostart with the path). The loader depacks in under a second; the title screen appears with SCORE / LAST SCORE / HIGH SCORE and F5 NOVICE, F7 BEGIN GAME. Turn warp mode off (autostart leaves it on).
  2. No loader menu. Snapshot: work/title.vsf at the title screen.
  3. Press F7 through the keyboard matrix, held about 2.5 s (the game scans the keyboard itself via the KERNAL SCNKEY and reads $C5; a short tap can be missed). Play begins immediately: a first-person cockpit over a green plain with a blue sky, status row 0000000 L1 E11 54 (score, level 1, enemies remaining, a countdown), crosshair in the centre, black pillars on the horizon.
  4. Snapshot: work/play-level1.vsf, taken a few seconds into level 1 with no joystick input given yet. This is the image the disassembler is loaded on.

F5 selects NOVICE before F7; the snapshot was taken at the default (not novice) setting. Not yet explored.

Steady state

Observed in play-level1.vsf:

WhatValueMeaning
$01$36BASIC ROM banked out, KERNAL and I/O in: RAM at $A000–$BFFF holds game code
$0314/$0315$B34EIRQ vector in RAM: the game's raster interrupt chain
$0316–$0319$FE66, $FE47BRK and NMI vectors left at KERNAL defaults
$FFFA–$FFFF (ROM)$FE43, $FCE2, $FF48hardware vectors are the KERNAL's; IRQ goes through $0314
$DD00$96VIC bank 1: $4000–$7FFF
$D018$8Fscreen at $6000, character set at $7800
$D011$1Btext mode, 25 rows, screen on
$D01Araster bit setraster IRQ enabled; $D012 = 255 at the sampled moment
CIA1 ICRall sources disabled by $DC0D = $1F at $9F16only the raster interrupt runs during play

The IRQ handler at $B34E is a chain of short stages: each stores a colour into $D021, rewrites $0314 to the next stage and $D012 to the next raster line, acknowledges, and returns. That is how the sky, horizon and ground bands are drawn on a plain text screen.

Program counter samples during play scatter over $ADxx–$B8xx, so the main loop lives in the block under the banked-out BASIC ROM. The init code is at $9C00. Nothing is reloaded from disk: the whole game is resident after the single depack, and the title screen returns without a reload.

The loader, in a paragraph

SYS 2066 runs $0812: SEI, $01 = $30 (all RAM), copy 166 bytes from $082A to $00FA and from $08D0 to $0333, then JMP $0100. The decruncher in the stack page and tape buffer expands the packed data downwards from $55E5 over $0801 onwards, and finishes at $0960. That routine is a self-modifying page copy that moves the whole depacked image up by $3000 (every page $0D→$3D, $0C→$3C ... wrapping) and jumps to $9C00. The init at $9C00 copies 34 pages from $3E00 down to $1E00, writes the vector pair $9C3E into $8000–$8003, calls the KERNAL IOINIT, copies 160 bytes of initial variables from $8B00 into zero page $02–$A1, sets $01 = $36, selects VIC bank 1, and enters the title screen. Two checkpoints (at $0960 and $9C00) each hit exactly once on a fresh run, which is how the chain was confirmed. Not annotated further, by policy.

Emulator notes from this run

Loading play-level1.vsf back with vice_snapshot_load after a hard reset and a second autostart came back with $01 = $37, the CPU in the KERNAL screen-scroll routine and a garbage screen. Autostarting the PRG again and pressing F7 is faster and reliable; the snapshot file itself is fine for the disassembler and for offline sweeps.

The symbol map for this game is symbols.json; the listing behind the Source tab is listing.json. Write-ups, facts and symbol maps are CC BY-SA 4.0. The game itself is not hosted here.