> Memory access breaks would be nice. I am afraid it'd be some work (more as for RISC cpus) since the SNES has so many different opcodes with different addressing modes (not to mention all the coprocessors). For performance reasons, I'd need to implement each opcode twice (for normal fast execution, and slower debugging checks).
I have all of my opcodes call out to generic opcode read/write functions, so I only need a breakpoint hook in one place.
I really think one should have a debugger build and a regular build without the debug checks. With a bit of clever coding, you don't even need unsightly #ifdef DEBUGGER blocks around all your debugging hooks. So I'll have:
Code: Select all
alwaysinline void CPU::op_read(unsigned addr) {
mdr = bus.read(addr);
add_clocks(speed(addr));
debugger.op_read(addr, mdr);
return mdr;
}
And the debugger.op_read(), when DEBUGGER is not defined, is reduced to a no-operation, and thus zero overhead. The forced inlining makes sure that we don't pay for the abstraction, and we get to adhere better to
DRY.
Similarly, I have read/write/execute breakpoints for CPU, SMP, APU, VRAM, OAM, CGRAM with the read/write breakpoints accepting an optional "only if read/write value == N" condition. They are indeed immensely useful for ROM hacking / fan translations.
> breakpoints on coprocessor - yup, would be great, especially for the APU, currently only the one-shot breaks (F4-key) are working on APU side. The issue with normal (F2-key) breaks is that I'd need to store some "which break is for which cpu" info in the breakpoint list (and of course, needing to implement them on the different cpus).
Yeah, interleaving operations between the multiple processors is a real bitch. Gets even nastier when your emulator runs at cycle granularity and one processor may be in the middle of an opcode at an instruction breakpoint boundary on another CPU.
Cooperative threading also adds fun limitations to debugging in my own case.
There's a definite struggle between accuracy and debugging power. But in our case all of the less accurate SNES emulators have either no debuggers or shit debuggers, so the bar is pretty low.
> There are SNES/SFC games which were written in combinations of both assembly and C. Some of the later RPGs were done that way, based on (again if my memory serves me correct)
Yeah, it's a big library, probably all variations of anything you can imagine.
Square seems to have made their own bytecode language for their games, which I assume they had large script files and a compiler for. Masaya built games entirely from portable macro code (it was a nightmare to hack, the unrolling was legendary.)
The main issue is that the 65816 is just an absolute shit architecture for compiling C code on. You have exactly one register that you can add and subtract on. You can't even multiply or divide with the base processor.
Any code produced by a compiler is going to be inherently slow. You can definitely write your non-critical code sections with this slow code and probably be okay, but given the processor is effectively ~2.68MHz, with a throughput of less than half of one MIPS, you're going to end up degrading your game the more you use C. It's not like a desktop app where you can just code a few hot functions in ASM and have great performance. Some of the best games choke down the CPU despite being in pure assembler. Absolutely anything called at least once per frame would have hurt those games.
> This is exactly what I was thinking. It's a sacrilege.
It makes it vastly more difficult to share code, that's for sure.
The SMP mnemonics just drove me absolutely nuts. It was the most blatant 6502++ ripoff imaginable, yet they came up with this insane syntax. I would really be shocked if they didn't internally have a 6502-like mnemonic set for their CPU. I'd bet money that their public syntax was strictly a legal decision.
Of course the real fun in deviating from official syntax is that you'll never, ever get two people to agree to the same unofficial instruction set, so you end up with 15 variations.