Skip to content

Debugging the original ​

A debugger stops the original game at a chosen instruction or when a chosen value changes, and shows its registers and memory at that moment. It answers questions that reading the code cannot settle quickly: which of three functions runs when a unit dies, what a table holds once the level has loaded, which code writes the score. This page lists the debuggers for each kind of game, and how to connect Ghidra's debugger to an emulator.

Debuggers by kind of game ​

GameDebuggerNotes
DOSDOSBox-X's debuggerBreakpoints on addresses, interrupts and memory changes, memory dumps. tools/dos/dbx_control.py drives it from a script
DOS, early PC, bootersBochsA built-in debugger. Bochs can also be built with a GDB stub
Any PC, raw machineQEMU-s -S waits for a GDB connection on port 1234 before it starts
Windowsx64dbg, WinDbgOn Windows itself. Under Wine, winedbg --gdb game.exe serves the process to GDB
Arcade and many consoles and computersMAMEA built-in debugger with memory views and watchpoints. -debugger gdbstub serves GDB for some CPUs
Game Boy AdvancemGBA-g starts a GDB stub
Nintendo DSDeSmuME--arm9gdb=PORT and --arm7gdb=PORT start GDB stubs for the two CPUs
GameCube and WiiDolphinA built-in debugger, and a GDB stub on some builds (set in its configuration)
PlayStationPCSX-ReduxA built-in debugger, and a GDB server in its settings
Other consolesThe emulator's own debuggerThe page for each console lists them (Console games)

Each debugger has its own commands. The parts that matter most are the same everywhere.

  • Memory breakpoints (watchpoints) stop when an address is written or read. Use them with Finding values in memory: find the address of the score, then stop at the code that changes it.
  • Conditional breakpoints stop only when a condition holds, such as a register value.
  • Logging breakpoints print registers or memory and carry on without stopping. A series of them gives a trace of values over time. Compare that trace with the port's own trace using tools/verify/seqdiff.py.
  • Memory dumps save a region to a file for memscan.py, Ghidra or a decoder.

Ghidra's debugger ​

Ghidra 10 and later include a debugger. It shows the running program inside the same listing and decompiler you use for reading, with your labels and types. It connects to GDB, LLDB and WinDbg. Through GDB it reaches any GDB stub in the table above. Start the emulator with its stub, then use Ghidra's GDB launcher with a remote target (target remote localhost:PORT). The addresses in the emulator must match the addresses of the program in Ghidra. For a game that loads at a fixed address, they match. For a program that moves, Ghidra maps the module by its base address.

Ghidra records the session as a trace that you can step back through. A memory value or register can be followed back to the instruction that set it.

Ghidra's emulator ​

Ghidra can also run code without the game, with its P-Code emulator (EmulatorHelper in a script, or the Emulator in the debugger). It runs any processor Ghidra can decompile: 68000, MIPS, SH-2, Z80, 6502, ARM and others. For those processors it can take the place of Unicorn in re-emulate. Set the registers and memory, run one function, and read the result. It is slower than Unicorn and needs the same care with the hardware the function reads.

Code coverage: what runs when ​

Coverage of two runs: the action's code is what ran only in the second

A game runs most of its code every frame. The code for one action, such as buying a unit, saving or opening a door, runs only when that action happens. Coverage shows it. Record which basic blocks run while you play without the action. Then record again, doing the same and the action once. The blocks in the second run and not the first are the action's code.

Keep the two runs alike: the same save, the same menus, the same length. Every extra difference adds functions to the list.

Patching a copy ​

Some questions are quicker to answer by changing the game. Examples are skipping a long intro on every test run, or forcing a level to load first. Patch a copy, never the copy you analyse (Handling the game as evidence). Record each patch with its address, the old and new bytes, and its reason. Patches that defeat copy protection are a different matter (Packed and protected code).

What to record ​

  • The emulator or debugger, its version, and the commands or script that set the breakpoints.
  • For each answer, the breakpoint, the address it stopped at, and the values seen.
  • Any patch to a copy, as above.

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