Skip to content

Running the original ​

A run of the original shows what the code does with real inputs. Run the game wherever you can watch it, drive it, and capture what it shows, plays, reads and keeps in memory. Most measured evidence (a label in GameFrame) comes from these runs.

Where to run it ​

EnvironmentForWhat you can capture
GameFrame's emulators (pe_emu.py, dos_emu, dos_env)One function or the whole program, headless, scripted, in testsRegisters, memory, calls, random draws, frames from video memory, every byte (re-emulate)
DOSBox-XDOS games with a window, a debugger and file loggingBreakpoints, memory dumps, file access logs, screenshots, audio and video recording (DOS games)
DOSBox StagingDOS games, accurate and simple to configureScreenshots, audio and video, raw OPL output
86Box, PCemDOS and Windows 9x games that need exact period hardwareThe full machine, slowly
Wine, CrossOver, ProtonWindows games on macOS and LinuxDebug channels (WINEDEBUG=+relay shows every API call), screenshots
A Windows machine or virtual machineWindows games as players run themDebuggers (x64dbg, WinDbg), API monitors, frame capture (RenderDoc, PIX), Steam Remote Play to stream it to another machine
A console emulator with a debugger (Mesen, bsnes, Dolphin, PCSX2, MAME)Console and arcade gamesBreakpoints, memory, input movies, frame advance
Your own runtime (as Frontier's port did)An original that runs on the port's own CPU and DOS modelEverything, inside cargo test, natively and in the browser

Prefer headless and scripted runs for evidence: they repeat exactly and run in tests. Use the windowed runs to see what happens and to find where to look.

What to capture ​

CaptureWhyHow
FramesGround truth for drawing and for the interfaceVideo memory from the emulator; screenshots; screencapture of a stream window; frame capture tools for 3D APIs
StateGround truth for the rulesMemory dumps at chosen points (the data segment, the heap, named variables); the game's own saves
InputsTo replay the same run in the portThe game's demo files; breakpoints on input reads (Inputs)
CallsThe order of functions and random drawscall_trace.py; debugger traces; API logs (WINEDEBUG=+relay, API Monitor)
FilesFile formats measured from the original's own readsDOSBox-X file logs and GameFrame's fileio_trace.py; Process Monitor on Windows
SoundMusic and effects compared sample by sampleThe emulated chip's register writes (OPL), WAV recordings from the emulator
TimingTick rates, music tempoTimer programming, frame counts, audio recordings measured over a long run

Keep every capture in the project's ignored folders (derived/, game-data/): captures are game material (Legal).

Driving the original ​

Drive the original by script, so you can make each capture again:

  • In GameFrame's emulators, script the inputs: dos_env answers the BIOS and DOS key reads from a queue (env.keys). You can also write a game's own key table in memory between frames.
  • In DOSBox-X, script the debugger over its control channel (Tyrian's dbx_control.py) or type into the window with an automation tool.
  • On a Windows machine streamed to your desk, an agent with computer-use access can watch and press buttons. The video is compressed: compare by layout and with blurred image differences, not pixel by pixel.
  • Use the game's own demo and replay files whenever they exist.

Write down, next to each capture, the inputs that made it: the version of the game, the settings, the seed, the commands or the demo, and the frame.

Comparing ​

WhatHowTool
Numbers and statebit for bit, with the first difference reportedtools/verify/bitcompare.py; tests that load a captured state and run the port
Sequences (draws, calls, events)the first point where they differtools/verify/seqdiff.py
Framespixel for pixel when both are exact; image difference with blur and a threshold when one is a compressed streaman image-difference tool (Un-Banished's imgdiff)
Soundsample for sample when both come from the same chip model; by ear and spectrum otherwisethe OPL register log; a WAV difference

bitcompare.py and seqdiff.py on the sample game. The port matches the original in 600 of 600 values. Its 746 random draws are identical. A naive port in double precision matches only 7 values. It is 12 ULP off at most.

On the sample game, the port matches the emulated original bit for bit. Its random draws come in the same order. A port that computes in double precision instead of float is off by a few ULP. bitcompare.py reports each difference.

Further reading ​

Code: Apache 2.0. Content: CC0 1.0. No game material: bring your own copy.