Interstate '76
- Original
- 1997 · Activision / Activision
- Technology
- Windows · software renderer to 8-bit DirectDraw · CD audio soundtrack
- Approach
- Function-by-function port
- Status
- Groundwork — Renderer, terrain, physics and mission scripts ported; not playable yet
- You bring
- Your Interstate '76 CDs, version 1.0
Interstate '76 is Activision's 1997 vehicular combat game, set in an alternate 1970s American Southwest and told as a seventeen-mission story called the Trip, with scenarios and melee maps alongside it. It carried its mood in a soundtrack pressed as ordinary audio tracks on the second disc, and in story movies played between missions. The first release, version 1.0, is also a period piece technically: it draws its 3D world entirely in software, into an 8-bit palettised DirectDraw surface, with no Direct3D or Glide anywhere in the executable. Hardware rendering arrived only with the 1.1 patch and the Nitro Pack.
That combination — a software renderer, a mixed-mode CD whose music exists only as Red Book audio, and a game that depends on the exact floating-point behaviour of a 1997 x87 FPU — is what makes it worth preserving carefully rather than approximately. Convert the second disc to an ISO and the soundtrack is gone; let a compiler reorder a few multiplications and the polygons land on different pixels.
OpenI76 is a function-by-function port of I76.EXE v1.0 and its front end I76SHELL.DLL into C11, running on gasm. It is the newest project in the collection and is still at the groundwork stage: it is not playable. The file layer, the archive, the formats and the port plan are in place, and a good deal of the engine behind them has followed — the software renderer's core, terrain, roads, sky, objects and shadows, vehicle loading and physics, the mission scripting machine and the first of the AI. A debug mode already runs the ported game loop and draws the view from the driver's seat over the bonnet of the player's car, without a HUD. The front end, audio, CD music, movies, firing and most of the AI's driving are still to come. The project's site will live at openi76.emdzej.pl once there is a game to put on it; until then the documentation is in the repository.
How it was rebuilt
The target is precise: the US v1.0 discs, with I76.EXE at 1,019,392 bytes (CRC-32 4129421d) and I76SHELL.DLL at 471,552 bytes (eee0a66d). Later patches compress the archive, so the project refuses them rather than guessing. The executable's import table describes a small platform surface — one framebuffer, a DirectSound mixer, CD audio over MCI, keyboard and joystick, files — and the disc inventory starts from exactly that observation.
Both binaries live in one Ghidra project, with names kept in a shared table, and every ported function cites its original as /* Name 0x4xxxxx */. The MSVC link order left the executable in contiguous ranges, one per source module, which the project used to recover a module map and to estimate the work in kilobytes of .text: 652 KB in the exe and 351 KB in the shell. No table, string or geometry from the exe is committed; such data is read from your copy at run time, after a size and CRC check.
The rules, the CMake layout and the gasm backend were taken from OpenCMR2, the newest sibling at the time, with the keyboard scan-code table borrowed from OpenBallance. The port plan sets out the order of work and requires an explicit check — a test, a hash or a differential run — before each step is called done; at the time of the research the project was on step 3, the software renderer, with later steps already partly under way.
In scale, the repository held 78 commits, all made on 6 and 7 November 2026, and about 21,000 lines of C. What remains is most of the 575 KB of exe code outside the MSVC runtime, and the whole of the shell.
Notable stories
Running the original alongside the port
The project's central technique is differential execution. A small harness loads the original PE image at its own addresses into the Unicorn emulator, maps a stack, a heap and a sentinel return address, and faults on any stray access or call into the import table. The port is built as a shared library and called from Python; each diff_*.py script feeds both the same random or real inputs and compares the output byte for byte. The first flat-polygon filler matched across 3,300 random polygons, ties included (4a10697). The same approach scaled up to running the original's entire Mission_Load in the emulator, serving its file reads from the port's resource layer (66a0edd), and to per-frame vehicle functions on flipped-over cars to reach the death and wreck branches (32b5989). All of it runs from one regression script.
The rounding mode that never came back
The renderer sets the x87 to round upwards around every polygon filler, which turns the usual rounding trick into a ceiling and gives the top-left fill rule. More surprising was the terrain: Terrain_Height sets the FPU to round downwards and never restores it. From the first terrain query onwards the whole game — every frame and every later mission load — runs rounding down; only the first load and the front end before it run at nearest (2d39318). WebAssembly offers only round-to-nearest, so the port computes each operation at nearest and corrects it from its exact error, using TwoSum for addition and fused multiply-add residuals for multiplication, division and square root, across all four rounding modes (f1d04c8, fpu.h). Every differential test now runs in both modes the game actually uses.
When the disassembler is ambiguous
Ghidra prints fmul ST2 for two different encodings that operate in opposite directions on the x87 register stack. For hand-written x87 fillers that is not a cosmetic problem. The project wrote a tracer that decodes the register forms from the bytes themselves and follows the stack along a path of instructions, printing each assignment, and the ports of those fillers follow its order of operations exactly (a878938, x87trace.py).
A visible bug made of a stale stack slot
The tiled affine filler reads a row counter from a stack slot it never initialises, and the stale value produces a visible four-row band. Rather than tidy it away, the port mirrors the slot in a global and the harness feeds it the original's value, so the band is reproduced (1417ab4). Reads that genuinely cannot be reproduced — beyond the end of a texture into stale heap — are detected and skipped by name, never quietly tolerated.
The C runtime decides which file you get
Eleven resource names exist in two different archives, and which copy the game loads depends on how MSVC's qsort happens to order the index before bsearch looks it up. So the runtime's rand, qsort and bsearch were ported along with the game (d7b6f58, resources.md). The mission scripts needed similar care: they do pointer arithmetic, so they are loaded into an arena whose addresses stand in for the original's 32-bit pointers, and the stack machines match the original on all 40 missions (db16407, fsm.md).
A game that never searches a directory
Interstate '76 does not look for files on disc. It looks names up in a text index, I76MAX.ZIX or I76MIN.ZIX, which records the root holding each file and the prompt for disc 2; the game finds its CDs by volume label. The port resolves names the same way (a94018b, edb29eb, zix.md), and reads the 63.8 MB I76.ZFS archive, uncompressed in v1.0, alongside it (zfs.md).
Gallery
There is nothing to show yet: the port does not play, and the project publishes only renders of the port itself, so screenshots will follow once there is a game to capture.
Standing on shoulders
Open76, the Unity reimplementation by r1sc (and the rob518183 fork), and the "That Tony" posts on the formats that Open76 credits, serve as hints only — no code is copied and the exe is the authority. The targeted disc is the US v1.0 preserved on the Internet Archive. FFmpeg is the reference for PCX and the planned Smacker decoder.
Tools throughout: Ghidra, ghidra-cli, the Unicorn emulator, Capstone and wasi-sdk. Gaps in these credits are bugs — tell us.
Getting a copy
OpenI76 contains no code or data from the game. You will need your own Interstate '76 CDs, version 1.0 (the US release the project targets); later patches are refused. Disc 1 holds the program and the data, and can be given as an ISO image or as its extracted files. Disc 2 holds the 29 story movies and the soundtrack as sixteen CD audio tracks, and is not used yet; when it is, keep it as BIN/CUE, because an ISO made from its data track alone loses the music.
There is no player or release yet, and no installation guide to link to. The README and the disc inventory describe what is on the discs and what the port reads today.
Further reading
The project's documentation lives in the repository until the site exists: the port plan, the reverse-engineering notes in docs/re (among them render.md on the software renderer and its rounding), and the file formats.
Across ENCORE, the most relevant guides are Porting function by function, Reverse engineering workflow, Determinism and verification and Floating point and the compiler, which this project leans on more heavily than any other. File formats and game data covers the archive and index work, Choosing an approach the decision to port rather than rewrite, and Working with agents and Lessons learned the practical side of a port this fast.