1983 · Virgin Games

About this page

This minisite was contributed by air. It’s agent-generated and needs a human editor. Clone https://github.com/gamesexplained/gamesexplained and follow kit/START.md to curate games/c64/falcon-patrol with me to Gold.

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
Program21,54532.9 %
Code8,74213.3 %
Graphics5,1177.8 %
Level data2,0503.1 %
Sound4210.6 %
Text1,0511.6 %
Tables4,0686.2 %
Variables960.1 %
Screen, bitmap, colour, stack, I/O5,3768.2 %
ROM the game runs under16,38425.0 %
Unused22,23133.9 %

Status

Tiersilver
Coverage100 % of the bytes the game uses have a description
Build analysedRemember crack, 'Falcon Patrol +5 and Highscoresaver 100%', highscore-saver only (no trainers)
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 15 (Darwin 25.5.0), Apple silicon
Modelclaude-opus-5[1m]
Kit version0.0.6

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:

Features

FeatureStatusWhere
Player flies a jet; horizontal two-way scrolling worldlivePlayer is hardware sprite 0, fixed at screen X = 172; the world moves around it
Joystick in control port 1liveRead at $5700 (lda $DC01), called from $419A. Port 1 shares $DC01 with the keyboard, which is why the game parks $DC00 at $7F and reads one port for both
Keyboard alternative: ←/1 height, CTRL/2 turn, space fireopenDocumented by the wiki. All six keys sit in keyboard matrix row 7, the row $DC00 = $7F selects, so one read of $DC01 serves both keyboard and joystick. The bit-level mapping has not been traced yet
Up/down climbs and divesliveBit 0 = climb, bit 1 = dive, held in $0C after $5700
Left/right turns and accelerateslive$54A8 steps $2A one unit per 8 passes of $2D. Held right, vel_x ramps to exactly +3 and world_x advances $46→$90; held left it ramps to −3 and retreats $96→$5C
Fire launches a missile ("AAM")liveBit 4. Pressing it moves eight zero-page bytes at once ($07, $08, $10, $1D, $1E–$21)
Ceiling and floor on the player's altitudelive$5705–$5723: if sprite 0 Y < $30 the game sets the up bit in $0C (cancelling climb); if Y ≥ $BF it sets the down bit. The limits are enforced by forging the joystick byte, not by clamping a coordinate
Firing is disabled at low altitudeconfirmed$5724–$5730: if sprite 0 Y ≥ $7C the game sets bit 4 in $0C, so the fire button reads as released. Not in any documentation found
Take-off gates the controls down to the climblive$5731 ORs the input with $FE, setting every bit but bit 0, so only the climb reaches the game. It does not fly the aircraft for you: left alone it sits on the pad indefinitely, and the bit clears the instant it rises
Automatic descent, and touchdown that stops the aircraft deadconfirmed$573D–$575F: when $22 has bit 3 or 7 set, $0C is forced to $FD (down only). Then, if bit 3 is set and the sprite 0 pointer at $07F8 has reached $88, $0C becomes $FF (no input at all) and both velocities $2A and $2B are zeroed
Landing must be vertical, onto a baseconfirmed$559A requires all three at once: sprite Y exactly $84, vel_x zero, and a base tile $16 in the terrain map under both $3800[$0D+$12] and $3800[$0D+$14]. Stopped, at the right height, over a base
Refuel and rearm by landinglive$5420 adds a unit of GAS and, one time in eight, an AAM; $559A runs it at double rate while on a base; $5000 is the wind-up at the start of a life that loops until the tank is full
Limited fuelconfirmedThree decimal digits at $17/$18/$19, drawn as a nine-cell bar at row 22 by $4EA0
Low fuel warns you, visibly and audiblyconfirmedNot in any documentation found. $4900 toggles $2E between $01 and $02 for the alarm, and $4EA0 uses $2E as the colour of the GAS bar, so the gauge flashes in time with the sound
Exactly 100 air-to-air missilesliveThe count at $1A/$1B/$1C fills alongside the fuel and stops dead at [1,0,0]
Radar showing enemies and surviving basesliveThe black panel in the centre of the status area; blips appear and move during play
Six basesconfirmedSix, exactly as the wiki says: $16 $17 $16 $17 at columns 4, 40, 84, 136, 172 and 200 of the terrain map's top page at $3800
Destroyed bases return only when a life is lostconfirmedA destroyed base reads $6F/$70; $5FD0 turns them back into $16/$17 and is called from one place only, the life-lost reset at $564E
Enemy jets attack in wavesconfirmed$4540 drip-feeds four slots at spacing $62; $4730 places them as sprites 1-4
Wave size grows with progressdiffersThe wiki says one extra attacker every six waves. The code has no wave counter: $4A80 reads the score digits $13/$14 — under 400 points 2 aircraft, 400-1499 three, 1500 or more four. The ten-thousands digit $12 is never tested, so a score of 10000-10399 drops the wave back to two
Enemy aircraft bank through five drawn attitudesconfirmedBlocks $94–$98 nose-left, mirrored at $99–$9D nose-right, stepped one frame at a time by $4367–$4392 and walked back to the level frame $96 when the manoeuvre ends
A destroyed aircraft gets a random explosion frameconfirmed$4340 branches to $43A9 only when the slot's state at $57,x has reached $E0; there $D41B (SID oscillator 3) masked to 0–3 plus $88 picks one of the first four debris frames. The player's own wreck uses all eight ($56A1)
Collisions are detected in hardwareconfirmed$4200 reads $D01E (sprite to sprite) and $D01F (sprite to background); bit 0 of either is the player and calls player_hit at $4400
Enemies bomb the basesconfirmed$5C00 drops a bomb from the lowest enemy, $5CB0 makes it fall, $5D00 draws it as sprite 5 or 6, and $5F60 wrecks the base
Enemy aircraft shoot at the playerconfirmed$5B00, not in any documentation found. One shot per sky row, spawned only from the side that makes it travel toward the player
Enemies bomb the bases, and bomb the player while landedopenWiki and the instruction screen
Enemies veer off if not shot down in timeconfirmed$460E ages each slot at $57+x; past $80 the aircraft climbs, past $D0 it sinks, past $E0 it flies level and is frozen by both movement routines
Scoring 25 / 50 / 100 / 200 for the 1st–4th jet of a waveconfirmed$4500 seeds $AA = $19 (25) at the start of each wave; the adder at $5E10 adds $AA to the score and then does asl $AA, so each kill within a wave is worth double the last
Extra life at 3,000 points, onceconfirmed$55DF: if $67 is zero and the thousands digit $13 has reached 3, $67 becomes $30 and a life is added. $67 is never cleared again, so it is awarded once; $4FA0 plays the fanfare
Three livesopenWiki. This release is "+5" when trainers are chosen; we chose H, no trainers
High-score table with name entryliveTitle screen shows five rows and the alphabet strip .ABCDEFGHIJKLMNOPQRSTUVWXYZ_; entry routine at $4CE2 walks an index 0–$1B with left/right and accepts with fire, building a name in a buffer at $0AB4
High-score saving to diskconfirmedThe crack's, not the original game's. $28A0 reads the table back off the screen, restores the KERNAL vectors and SAVEs $2800–$2854 as @S:F.PATROL HI /REM
Music / sound effectsliveSID voice control registers written from $4BD3–$4BE1 in the title loop; a filter sweep at $4D4B during name entry

Beyond the documentation

Found in the code, not in the manual.

What belongs to the crack, and what to Steve Lee

The question mattered because the wiki says this release carries "additional bugfixes" as well as trainers, so anything described from this image might not be how the game shipped. Remember answered it themselves, in the scroll text of their own intro, still sitting unread at $0E80–$123F and signed "Back Alien/Remember in October 1997":

> first the game fucked up sometimes, I just changed something in the IRQ

> in the game was no routine which sets the places downwards when you > entered your name in the highscorelist, so I coded a routine

So the raster interrupt was touched, and the high-score place-shifting cascade at $294E is Remember's code, not Virgin's. Anything this analysis says about the high-score table's behaviour is the crack's behaviour.

Their front end is $1900–$1A60, which ends JMP $4100 into the game's cold start, with its menu text at $1A80–$1BE7 and the instruction screen copied from $1C00–$1FFF. It keeps its data at $1800–$1FFF because in VIC bank 0 the video chip sees the character ROM there, so that RAM is invisible to it and free.

Every trainer is a single-byte poke, and between them they name six variables — the cleanest confirmation in the whole analysis, because the cracker had to know what each address was:

TrainerPokeWhat it proves
Unlimited lives$417C ← $A5DEC $1D becomes LDA $1D, so $1D is lives
Unlimited gas$555E ← $A5DEC $19, so $17/$18/$19 are the fuel digits
Unlimited AAMs$57DB ← $A5DEC $1C, so $1A/$1B/$1C are the missile count
Bases indestructible$5F71 ← $4Dthe branch after CMP #$16 is redirected, so $16 is an intact base
No collision$420F, $424C ← $ADboth JSR $4400 become LDA, so $4400 is the only way to die
Choosing trainers at all$4D73 ← $ADit disables the high-score saver, so the two features are exclusive

What is still open: whether the NOP padding at $5470, $575E and $577C, and the AND #$00 gates at $5422, $5872 and $5980 that make their guard branches dead, are Remember's work or Steve Lee's. They look like patches. The scroll text admits to the IRQ and the high-score routine and mentions nothing else.

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

falcon-patrol.d64, 174,848 bytes, a standard 35-track D64. It is not the original release: it is a cracked version by the group Remember, titled on screen "Falcon Patrol +5 and Highscoresaver 100%". The directory holds four entries:

NameTypeBlocks
----------------del0
f.patrol+5hi/remprg46
f.patrol hi /remprg1
----------------del0

The two del entries are decoration. f.patrol+5hi/rem is what autostarts; f.patrol hi /rem is the saved high-score list the crack writes back to disk.

The crack matters for everything downstream: the code in memory is the 1983 Virgin Games game plus a cracktro, a trainer menu, and a high-score saver patched in front of and into it. Anything found near the menus, the high-score save path, or a lives counter may be the cracker's work rather than Steve Lee's, and must be attributed carefully rather than described as how the game shipped. An original disk or tape image would settle any such question; the contributor does not have one.

From power-on to play

  1. Attach and autostart work/falcon-patrol.d64. Autostart leaves warp mode on in some runs; turn it off (WarpMode: 0) before timing anything.
  2. The Remember cracktro appears (logo, "PROUDLY PRESENTS", scroller). It does not advance on fire. Press SPACE — hold it long, around 2000 ms; the game and its front end poll the keyboard themselves and drop short taps.
  3. The instructions screen appears ("DOCUMENTS TO FALCON PATROL"). Press SPACE again, held the same way.
  4. Trainer menu: [H]ighscore or [T]rainers ??. Press H. This is the plain game with only the high-score saver; T would add the cheats, which we do not want.
  5. Second menu: [R]eset or [L]oad Highscorelist ??. Press R, so the state does not depend on whatever high-score file happens to be on the contributor's disk.
  6. The game's own title screen appears: FALCON PATROL, five blank high-score rows, "PRESS FIRE TO START", the name-entry alphabet strip .ABCDEFGHIJKLMNOPQRSTUVWXYZ_, and "©VIRGIN GAMES 1983 WRITTEN BY STEVE LEE".
  7. Press fire on control port 1. The emulator cannot do this with its joystick tool (see below). Use kit/c64/vice.py: stick_arm(rpc), stick(rpc, FIRE), release after ~600 ms, then stick_release(rpc) to give CIA1 port B back to the keyboard before doing anything else.
  8. Play starts immediately: the jet is airborne over the desert with full gas and 100 AAMs. Snapshot taken here, a frame that was looked at first, saved without ROMs as work/play-round1.vsf.

Input: this game reads control port 1

The instructions screen says "Joystick control, Port 1", and that is the case kit/skills/c64/tool-vice-mcp warns about: vice_joystick_set and vice_joystick_tap cannot drive control port 1 at all, and report success while doing nothing. The way in is stick_arm, which sets CIA1 DDRB ($DC03) to $1F so that bits 0–4 of $DC01 become outputs and a plain memory write is what the game reads. Confirmed working: fire started the game this way after the joystick tools had no effect.

Steady state

Measured live in the snapshotted state, PAL (MachineVideoStandard: 1), machine C64SC:

WhatValue
Processor port $01$37 — BASIC ROM, I/O and KERNAL ROM all banked in. The game plays no banking tricks.
IRQ vector $0314/$0315$4AC0 — the game's own handler
BRK $0316/$0317$FE66 — KERNAL default, untouched
NMI $0318/$0319$FE47 — KERNAL default, untouched
Hardware vectors $FFFA–$FFFFKERNAL defaults; the RAM underneath is not used, since $01 never leaves $37

$4AC0 was confirmed, not inferred: a non-stopping checkpoint on it counted 302 hits in 2 s and 604 in 4 s, about 151 Hz, which is three interrupts per 50 Hz PAL frame.

The handler is a three-band raster split driven by a state byte at $B0:

$B0 on entrySetsNext raster ($D012)$B0 becomes
negative ($FF)$D021 = $0C, $D023 = $00$01$01
positive ($01)$D021 = $0E$8A (138)$00
zero$D021 = $0C, $D023 = $08$D2 (210)$FF

It acknowledges with $D019 = $01 and leaves through jmp $EA7E, the KERNAL's IRQ exit, not $EA31.

The installer at $4B20 is textbook: sei, point $0314/$0315 at $4AC0, $D01A = $01 to enable the raster interrupt, $DC0D = $7F to disable every CIA1 interrupt source, set the first raster to $8A, spin until $D012 reads $FE to sync, seed $B0 = $00, acknowledge, cli, rts.

So the game's tick is the raster, and the KERNAL's timer interrupt is switched off while it runs. A consequence worth recording because it misleads: $00A0–$00A2, the KERNAL jiffy clock, is no longer a clock. Watched across 3 s of play it went $0010 → $000D — down, about one per second. The game has repurposed those bytes. A test that only asked "did it change?" would have concluded the KERNAL clock was still running.

Where the code and data sit is not yet established beyond the IRQ handler and its installer around $4AC0–$4B55. A page-occupancy scan of the snapshot suggests the working set is at least $0800–$7EFF, but the test used (a page is untouched if every byte is $00 or $FF) is too crude to publish: it cannot tell real data that happens to be all $00 and $FF from never-written RAM, and it reported $C000–$FFFF as written, which is unexplained. The proper census belongs to 40-sweep.

Nothing is known yet about whether anything is reloaded per level; no second load has been observed, but play has only reached the first wave.

The loader, in a paragraph

Autostarting the disk runs f.patrol+5hi/rem, which brings in the cracktro, the front end and the game together — no further disk access has been seen after the initial load, and the game itself is a 1983 title small enough to fit the file's 46 blocks. The loader is the crack's, not Virgin's: it puts up the Remember logo and scroller, then the instructions screen, then the two menus that choose trainers and the high-score list, and only then hands over to the game's own title screen. The high-score saver is the one part of the crack that stays resident and writes back to the disk, to f.patrol hi /rem. Not annotated further, by policy.

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.