A real SNES routine · ActRaiser (1990) · $00:984E–$00:9883

Three ways to run the same 54 bytes

Emulation, recompilation and decompilation get mixed up all the time. This page takes one real routine from ActRaiser, a 1990 SNES game, and shows it each way. The routine decides what the player’s character does next while standing still. The difference between the three comes down to when the game’s code gets translated, and how much machinery is still running underneath it while you play.

On the cartridge

These 54 bytes are the routine exactly as it sits in the ROM. Point at a group to decode it.

Turning bytes into instructions is the job an emulator does while you play, and a recompiler does once before you play. Decompilation goes a step further and works out what the instructions mean.

Who does the work, and when

All three end with the game running on your computer. What changes is when the game’s code gets translated, and what is still running underneath it while you play.

Emulation

Pretend to be a SNES. The emulator reads the game’s machine code as data and carries out each instruction as it comes.

  1. The game’s machine coderead as data, a few bytes at a time
  2. Interpreter loopfor every instruction, every time it runs
  3. A simulated SNESCPU, RAM, graphics, sound and DMA, kept in step
  4. Your OS, GPU and audio
  5. Your CPU
  1. Emulator source codewritten once, for every game
  2. C compileroptimizes the emulator, not the game
  3. Nothing game-specificthe emulator loads the ROM as is

Recompilation

Translate the program once. A recompiler turns the machine code into C before you play. The compiled game runs natively on a layer that stands in for the SNES hardware.

  1. The game as native codegenerated C, compiled for your CPU
  2. Translation layerSNES registers and memory map, graphics, sound and DMA
  3. Your OS, GPU and audio
  4. Your CPU
  1. The game’s ROM
  2. Recompilerdecodes every instruction once and writes C
  3. Generated Cexact, but still shaped like the SNES
  4. C compileroptimizes the game’s own code
  5. Native programplus the translation layer

Decompilation

Recover what it means. People, with tools, work out what each routine does and rewrite it as ordinary code that needs no model of the SNES.

  1. The game as ordinary Cnames, types and plain functions
  2. Your OS, GPU and audioor any engine
  3. Your CPU
  1. The game’s ROM, disassembled
  2. People, with toolswork out what each routine means
  3. Readable Cnamed, typed and structured
  4. Testscheck it behaves exactly like the original
  5. C compileroptimizes it like any program
  6. Native program

While you play, the emulator carries the most machinery and the decompiled game carries the least.

Hatched layers are a model of the SNES hardware.

One decision, three ways

Each frame the player stands still, the game runs these 20 instructions to decide what happens next. They check, in this order:

  1. Nothing underfoot: fall.
  2. B pressed and nothing overhead: jump.
  3. Down held: crouch.
  4. Left or Right held: run.
  5. Otherwise, stand still.

Set up a situation and its path through the original code lights up. Switch tabs to follow the same path through each approach, and point at any line to see where it went. Attack and magic are checked just before this routine, so Y, X and A don’t appear here.

Situation

B jumps, Down crouches, ◀ and ▶ run. Press to toggle.

ResultJump
Original
Emulator
Recompiled
Decompiled
Original

65816 machine code

What the SNES CPU runs. X holds the address of the player’s object ($08A0), so $001A,X is the player’s pose.

The actual instructions, in the old machine’s language. The last column says what each one does in plain words.

● ran↷ branch taken→ leaves the routinefaded rows were skipped

The live translator

  • This is the translator’s rulebook. It knows what every kind of instruction means, but nothing about this game.
  • This piece of the game runs about 60 times a second, and the translator works through it again every single time.
  • All that translating is extra work for your computer, on top of actually playing the game.

An emulator: the game is data, and a loop drives it

  • This code knows nothing about the game. case 0xAD runs both LDA $00A1 instructions here, and every other absolute LDA in the game.
  • Each time an instruction runs, the emulator fetches its bytes again and works out what they mean, down to whether it is 8-bit or 16-bit (cpu->m_flag ? … : …). That even decides how many bytes the instruction has.
  • The C compiler optimized this loop, not the game. It never sees that the three instructions at $985B together mean “is B pressed?”, so it can’t simplify them.
  • After every instruction, the graphics, sound and timers have to catch up so the whole console stays in step.

The numbers in the margin count how many times the translator used each rule for the situation you picked.

Illustrative. It uses the same Cpu struct and memory helpers as the Recompiled tab, so the two line up. Real emulators differ in detail but share this shape. The counts in the margin show how often each part ran for the situation above.

Real projects mix all three

These aren’t exclusive choices. A recompiled game still needs something to play the part of the hardware, a project can leave some chips emulated, and decompiled code can replace recompiled code one routine at a time.

Here is one way a decompiled routine can take over from recompiled code without changing anything around it.

  1. The recompiled function checks for a replacement

    A hook at the top hands control to the decompiled version, but only when the machine is in the state the replacement expects, such as 16-bit registers. Otherwise the recompiled body runs, so the original stays available to compare against.

    
          
  2. An adapter translates between the machine and the types

    It still calls the original probe routines in the original order, reads the same buttons and D-pad value, asks choose_idle_action for a decision, and turns the answer back into the address the original would continue at.

    The rewrite speaks in plain words, but the rest of the game still speaks the old machine’s language. A small go-between collects what the rewrite needs to know, asks it what to do, and passes the answer back.

    A careful adapter also leaves registers and flags the way the original would have, because recompiled code that runs next may read them.

    
          

Each routine that moves across leaves the adapters less to translate. Once nothing is left, that part of the game no longer needs the SNES model at all, and it can grow past what the original hardware allowed.

At a glance

Emulation Recompilation Decompilation
In one lineA live translatorTranslate the whole book firstRewrite it in plain words
When does the translating happen?While you play, nonstopOnce, before you playOnce, by people, before you play
Extra work while you playLotsA little, for the stand-in partsAlmost none
Can people read it?NoBarelyYes
Easy to change the game?HardPossibleEasy
Work to support a new gameNone, it works with any gameSomeA lot
Emulation Recompilation Decompilation
When the game’s code is translatedWhile you play, every instruction, every time it runsOnce, before you play, by a toolOnce, before you play, by people using tools
What your CPU runsThe emulator’s loop. The game is data.The game as native code, on a translation layerThe game’s logic as native code
Model of the SNES neededAll of itCPU state, memory map, graphics, soundNone
Main runtime overheadFetching, decoding and dispatching every instruction, plus simulating the hardware in stepThe translation layer: emulated memory and hardware, SNES-style flagsNothing inherited from the SNES
Work per gameNone. One emulator runs the whole library.Teach the tool the game: what is code, which CPU modes, where jump tables leadUnderstand, name, rewrite and test each routine
Readable sourceNoneExact but nameless (L_9866, cpu->flag_z)Names, types and structure
Changing the gamePatch bytes in the ROMHooks and patches around translated codeEdit it like any program
How it stays faithfulReproduce the hardwareReproduce every instruction’s effectTest against the original