// Minimal 6502 / 65C02 interpreter, extracted from tools/fs2trace.c // so chunk5Transform.c can run small fragments of the MAME-patched // chunk5 binary directly (= byte-faithful replication of the per- // vertex transform body at $7E8E..$8068 + helpers like $181A). // // All state is held in Cpu6502T so multiple interpreters can run // concurrently and the caller controls the 64K address space. No // I/O hooks, no SmartPort, no display hardware -- caller provides // raw RAM and runs the CPU until PC reaches a configured stop // address, or unknown opcode is hit. #ifndef CPU6502_H #define CPU6502_H #include #include typedef struct Cpu6502T { uint8_t *mem; // 64K RAM uint16_t pc; uint8_t a; uint8_t x; uint8_t y; uint8_t s; // stack pointer uint8_t flagN; uint8_t flagV; uint8_t flagD; uint8_t flagI; uint8_t flagZ; uint8_t flagC; bool unknownOp; // set when step() hits an unimplemented opcode uint8_t lastOp; // last opcode executed (for diagnostics) uint16_t lastOpPc; // PC where lastOp was fetched // Per-instruction trace hook (NULL = disabled). void (*traceFn)(struct Cpu6502T *cpu, void *userData); void *traceUserData; } Cpu6502T; // Initialise A/X/Y/S to 0, flags cleared, mem set to caller's // buffer. void cpu6502Init(Cpu6502T *cpu, uint8_t *mem); // Single instruction step. void cpu6502Step(Cpu6502T *cpu); // Push a 16-bit return address (high then low) onto the stack so // the next RTS pops back to (return_addr + 1). Mirrors what JSR // does: pushes pc-1 of the instruction after JSR. void cpu6502PushReturn(Cpu6502T *cpu, uint16_t returnAfter); // Run starting at `entry` until PC reaches `stopPc` or an unknown // opcode trips `cpu->unknownOp`. `maxSteps` bounds the work in // case of a runaway loop (set to 1000000 for transform body). // Returns true on clean halt at stopPc. bool cpu6502Run(Cpu6502T *cpu, uint16_t entry, uint16_t stopPc, int maxSteps); // Hook callback invoked BEFORE the instruction at `hookPc` executes. // If `cb` returns true, the hook handled the instruction (e.g. by // popping a return address and updating PC); the interpreter skips // the normal step. Use to intercept JSR targets and emulate them // in C (chunk5 DrawColorLine -> port renderer, etc.). typedef bool (*Cpu6502HookFn)(struct Cpu6502T *cpu, void *userData); bool cpu6502RunWithHook(Cpu6502T *cpu, uint16_t entry, uint16_t stopPc, uint16_t hookPc, Cpu6502HookFn cb, void *userData, int maxSteps); // Multi-hook variant. Each entry maps a PC to its handler. The // interpreter checks PC against each entry on every step (linear // scan; intended for small N). Same return convention as // cpu6502RunWithHook. typedef struct Cpu6502HookT { uint16_t pc; Cpu6502HookFn cb; void *userData; } Cpu6502HookT; bool cpu6502RunWithHooks(Cpu6502T *cpu, uint16_t entry, uint16_t stopPc, const Cpu6502HookT *hooks, int nHooks, int maxSteps); // Per-instruction trace callback. If set on a Cpu6502T, fires // before EVERY instruction executes. Receives pc + opcode + A/X/Y. // Set to NULL to disable. typedef void (*Cpu6502TraceFn)(struct Cpu6502T *cpu, void *userData); void cpu6502SetTrace(Cpu6502T *cpu, Cpu6502TraceFn fn, void *userData); #endif