Rockford a study of the 1988 game

What is on the disks

Three MS-DOS releases were examined, all from the Internet Archive: EGA builds from November 1987 and January 1988, and a VGA build from February 1988. A fourth copy, the VGA release on 5.25" disks, has a second build of the executable.

Their files hold the caves, two picture formats and the executable. This page also covers when the cartoons play and a debug keyboard the game never mentions.

The disks

What each copy carries
ReleaseBuiltMediaGraphics filesWorlds on the menu
EGA r8711099 Nov 19872 x 5.25" 360K.FIL, .CARhunter, scuba, cook, player, cowboy
EGA r88012929 Jan 19882 x 5.25" 360K.FIL, .CARhunter, cook, cowboy, space, body
VGA r88022525 Feb 19881 x 3.5" 720K.SCM, .CCMhunter, cook, cowboy, space, body
VGA on 5.25"no release number2 x 5.25" 360K.SCM, .CCMas r880225

The release number is the build date in the darkstar/tdc-database listing, which gives every file's size and CRC32 for every release; each copy here was matched against it. The images are plain FAT12, so a directory walk gets the files off.

The 1987 release is the odd one out. Its CELLMAPS.BIN is 17,600 bytes: twenty caves, byte for byte the first twenty of the 1988 file. It asks you to type a world number where 1988 has pictures to choose from. The two 1988 releases appended twenty more caves and share the cave file and a near-identical executable. The hidden worlds page has the detail.

A second VGA executable

A copy of the VGA release on two 5.25" disks, from goodolddays.net and labelled "1988, Unverified", has identical data files but a different ROCKFORD.EXE: 31,805 bytes and CRC32 6558ca05, against 31,789 and 4503fd98. Unpacked, the data segment is identical. The code differs by three small edits (two bytes inserted at 0xEA18, one at 0xFB0C, three deleted at 0x13C4D) and four identical video-register constants changed from 5 to 1, with every later call and jump shifted to match. All of it is display code: no table or rule changes, and no new world, cave or art. It has no release number because tdc-database does not list it.

CELLMAPS.BIN, the caves

In the 1988 releases this one file is the whole level set, as plain as a format gets.

35,200 bytes = 40 caves x 880 cells
880 cells    = 40 wide x 22 high, row-major
1 byte       = 1 cell

No header, no compression, no per-cave metadata. Four things together prove the layout rather than merely fit it:

  • 35,200 divides exactly by 880, leaving no room for a header.
  • Read at 40 by 22, every cave has an unbroken steel border on all four sides.
  • 0x74 occurs exactly once in every one of the 40 caves.
  • 0x20 occurs exactly once in every one of the 40 caves.

A wrong stride would not produce all four. The last two are the exit and the player start, one of each per cave; the first reading of this file had them the wrong way round (see the engine).

The cell values

Only 25 distinct byte values appear across all 40 caves.

Authored cell values, with the count across all 40 caves
ValueWhat it isCount
0x00Space6,544
0x01Dirt13,508
0x02Steel wall7,263
0x03Brick wall2,738
0x04Magic wall, the timed converter96
0x08Expanding wall163
0x0CQuicksand64
0x10Amoeba28
0x20Player start40
0x28Boulder2,126
0x2C-0x30Enemy, the family that yields treasure125
0x34-0x37Enemy, the family that does not412
0x38Fire tap7
0x70Evil worm3
0x74Exit40
0x78Treasure bonus2
0x7CTime bonus9
0x84Good worm8
0x88Treasure2,017
0xC4Acid tap7

The low two bits of a moving entity are its facing, so 0x34 to 0x37 are one entity in four rotations, not four entities.

How this table was wrong

An earlier version was built by census: counting each cave's contents and reasoning about which values must produce a collectable for the cave to be finishable. The reasoning was sound and several conclusions were wrong, because a census only constrains the answer if you already know what the bytes mean.

This one comes from the loader, the code that turns an authored byte into a runtime cell, read instruction by instruction, and every correction cites the instruction behind it.

EGA: .FIL and .CAR

Every file is exactly 32,000 bytes.

32,000 bytes = 320 x 200 at 4 bits per pixel
             = 4 sequential bitplanes of 8,000 bytes

Each plane is one bit per pixel, most significant bit leftmost: a straight dump of EGA video memory, not a packed format, in the order blue, green, red, intensity. .FIL holds a world's tile and sprite sheet, a 20 by 12 grid of 16 by 16 tiles. .CAR holds its cartoons, a 6 by 4 grid of 48 by 48 frames, of which the game plays three rows (see when the cartoons play).

The palettes are in the executable, sixteen registers and an overscan byte at a time. Matching them to worlds took an outside clue and then a check. That story is on the hidden worlds page, because the answer is about the world ordering.

VGA: .SCM and .CCM

320 by 200, one byte per pixel, and the palette travels in the file, which is the useful part.

[RLE image data, 64,000 pixels]
[01 1A]        in most files, one more pixel past the end of the screen
[80]           the terminator: exactly 0x80, with no value byte after it
[192 bytes]    32 RGB triples, 16-bit little-endian words, each 0-15

The run-length encoding is byte-oriented and four rules long:

0x80      ->  end of the stream
0x00      ->  nothing (allowed, unused)
n & 0x80  ->  run of (n & 0x7F) copies of the next byte
otherwise ->  n literal bytes follow

Every file fills the 64,000-pixel screen with values 0 to 31. The game does not count pixels: it decodes until the terminator, so most files write one byte past the screen, harmlessly, inside the same 64 KiB segment. The trailing palettes fit the pictures: the title screen's ends in a grey ramp for the rock, the jungle world's carries greens, the space world's blues. A .CCM has the terminator but no palette; it reuses its world's.

So the VGA colours come from the game's own data, with no guesswork. That makes VGA the colour reference and EGA the curiosity, the reverse of how the work started.

When the cartoons play

Each world's cartoon sheet holds four six-frame animations, one to a row, which the Amiga disk labels NEW, WIN, LOSE and TIME: a level starting, a level won, a life lost, and time running out. The PC loads every world's sheet into off-screen video memory and plays it over the cave from the main game loop, using rows 2 to 4. Row 1, NEW, is never read: Aleksi Sahala said the PC never shows the cartoons, and for this row he is right.

Which row plays depends on how the cave ended. This was read from the January 1988 EGA executable on 11 October 2026 and has not been run.

The three cartoons the PC plays
CartoonRowChosen whenPlays
WIN2Rockford walked into the open exit with the quota met, and no key ended the cavebefore the time bonus counts down
LOSE3Rockford died with time still on the clockwhen the death countdown reaches zero
TIME4Rockford died with the clock at zero or belowat the same point

Every death, from a hazard or from giving up with R, starts the same countdown of eight. The clock loses a second every 60 timer ticks and below zero kills Rockford the same way, which is how running out of time reaches the TIME row. After a LOSE or TIME cartoon the game takes a life.

How long one lasts

The six frames loop, each shown for 9 ticks of the 60 Hz timer (150 ms). A cartoon always runs at least 60 ticks, one second; after that any key or a joystick fire ends it, and otherwise it stops at 360 ticks, six seconds.

The VGA executable sets the same flags in the same places, but its own cartoon routine has not been read, so its timings are assumed to be the EGA's and are not confirmed.

In the two builds here

The faithful build plays the three cartoons by these rules, centred on Rockford; the original's formula for the vertical position was not worked out, so the centring is approximate. The port credits the time bonus as soon as the cave is complete, so its WIN cartoon comes after the bonus, where the original's comes before. VIP plays the same three redrawn at eight to one. Neither build plays a cartoon in the five hidden worlds.

ROCKFORD.EXE

16-bit real mode, EXEPACK-compressed, and unpackable.

The January 1988 EGA and February 1988 VGA executables, in bytes. Packed sizes leave out the 512-byte EXE header.
ReleasePackedUnpackedReal entry
VGA31,27784,8000E9F:02F7
EGA29,451142,8801CFB:02F5

The unpack checks itself: on both files the compressed stream is consumed to exactly offset -1 just as the output fills exactly the declared length. A wrong decoder would not hit both bounds at once, twice.

The rules, in the game's own words

Strings survive compression and give up the entire rule set:

Collect enough treasures to open exit door.
Flying and falling objects will kill you.
Drop things on flying enemies to kill them.
Use fire button (space bar) to pick up treasures, push
things over, and blow up dirt.

The last describes a ranged action: fire acts on the adjacent cell in the facing direction without moving Rockford. In Boulder Dash the only way to clear a cell is to walk into it, and that difference makes this an arcade game rather than a puzzle game.

Four tables, read straight out of the executable

Per-cave quota, a treasure's score before the quota is met and after it, and the time limit: forty words each, indexed by raw world * 4 + stage. A faithful port needs all four. The Rockford Remake Project transcribed them independently from the EGA executable twenty years ago, and 160 of 160 values match.

The debug keyboard

Both 1988 releases carry a developer keyboard behind the pause screen, each a different one, and the game never mentions it: no prompt, no message, no menu entry.

To reach it, pause, then hold Up and Down together with Shift: 8 and 2 on the numeric keypad the game steers with. The VGA build accepts either Shift key, the EGA build only the left. Nobody holds opposite directions together by accident.

The VGA build's debug keys
KeyWhat it does
CCollects one treasure, with its sound, and opens the exit if that meets the quota
LOne more life
MEnds the cave and makes the next one the world's last
NEnds the cave as completed, with no cartoon
PA thousand points
SOne more second on the clock
TSets the clock to 2 and leaves the pause, so time runs out at once
UCheat off
ZCheat on

Any other key returns to the ordinary pause. The EGA build has only L, N and P, and no cheat flag, so the keyboard grew between releases. With the cheat on, Rockford walks through anything, but he is not invulnerable: the game reads the flag in six places, none on a death path. The port reaches all six.

R, and a toggle that is really a patch

R is an ordinary key, not a debug key: it gives up on the cave, which counts as a death, and the manual calls it restart. The Rockford Remake page says it makes coins appear; it does not. s, next to it, toggles the sound.

The same page lists offset 0x0B98 of the packed EGA executable as a toggle for dying; it is the byte the Rockford Patcher rewrites for its god mode. Unpacked, the edit changes one byte, at 0x4300, which nothing reads as a variable. It is the middle entry of a table of offsets for a 3 by 3 neighbourhood, and the edit knocks it out of line with the other eight, so an explosion writes where it was never meant to. That is a patch; the game shipped no such cheat.

It is hand-written assembly

Four independent checks agree, and each one on its own would be awkward to explain away.

There is not one compiler stack frame in either executable

A scan of both unpacked images for 55 8B EC - push bp then mov bp,sp - returns zero hits across 84,800 and 142,880 bytes. Every DOS C and Pascal compiler of the period opened a non-trivial function that way. An optimised build would still have some; zero means a different kind of program.

There is no BP-relative addressing at all

No stack locals or stack parameters anywhere in the image. Variables are absolute ds: globals throughout.

Arguments are passed in registers

The entry point sets up calls as mov al,0Fh / mov bl,07h / call, and the callee reads them straight out of the registers. No DOS C compiler did this in 1987: cdecl put every argument on the stack, and Borland's __fastcall did not exist until 1990.

Subroutines save whatever they happen to use, by hand

11327:  push bx / push cx / push dx / push di / push si
        ...
1138F:  pop si / pop di / pop dx / pop cx / pop bx / ret

bx, cx and dx are caller-saved scratch under every DOS C convention, so a compiler would not preserve them. The pattern occurs 21 times in each image.

Two details support this. The stack segment holds 64 repeats of the literal STACK   , exactly what DB 64 DUP('STACK ') in a MASM stack segment produces. And the strings contain no runtime library of any kind: no __acrtused, no DGROUP, no divide-by-zero handler text, no vendor banner. The only English in the binary is the game's own.

The lineage has layers. The strings say Adapted from Amiga version and credit the MS-DOS version to Synergistic Software, programmed by John Conley, so this MASM code is a port. So was the Amiga version: Rockford began in 1987 as an arcade game for Arcadia Systems, on a board built around the Amiga, and Icon Design converted it for the Amiga home computer. Its programmer, Laurence Vanhelsuwé, has said he wrote it all in 68000 assembly. The arcade code has never been dumped. Behind them all is the 1984 Boulder Dash, 6502 code on the Atari 800. See the other versions.