Dynamic Recompiler Author's Guide
This is intended to be an introduction to how to write a dynamic recompiler using the universal architecture in MAME. I suspect it will eventually need to be broken into multiple separate pieces, but we'll keep it all together to start with.
Logic Flow
The logic flow of an interpreter is pretty easy to understand, because it works like a CPU does:
mycpu_execute_interpreted(numcycles) { do { opcode = fetch_opcode(); execute_opcode(opcode); numcycles -= opcode_num_cycles; } while (numcycles >= 0); }
Essentially, the core execution of the interpreter consists of sitting in a loop, fetching a single opcode at a time, executing it, and counting up cycles until it is time to move on to something else in the emulation. The bulk of the code in an interpreter consists of the implementations of the individual opcodes, and must be relatively well-tuned because it is executed quite frequently.
In contrast, the logic flow of a dynamic recompiler is considerably less analagous to a CPU:
mycpu_execute_recompiled(numcycles) { cpustate.numcycles = numcycles; do { return_code = (*recompiler_entry_point)(); if (return_code == EXIT_MISSING_CODE) recompile_code_at_current_pc(); else if (return_code == EXIT_FLUSH_CACHE) code_cache_flush(); } while (return_code != EXIT_OUT_OF_CYCLES); }
This may seem kind of odd at first, but let's step through the high-level flow to understand what is happening.
Setup and Initialization
Compile-time versus run-time