Turns out there is a bug... and has been there forever hidden in the gcc internals. As you can see the sh2_read_handler type lacks the regparm decorator, so in functions p32x_sh2_read8/16/32, the arguments are passed via stack. The functions expect the arguments to be passed via regs, so this would fail... Unless we get lucky and gcc happens to store the values in the same regs, out of chance!
And this is indeed what happens, just download the latest nightly binary and objdump'ed it to find:
The regs eax and edx are pushed to the stack as arguments, but the function will ignore them and use eax, edx as args, lucky!
In my machine, using gcc 11 instead of gcc 9 I'm not that lucky at all:
As you can see eax is correct (that's why my stack trace shows a correct reference to SH2* and doesn't really crash the emulator) but the address is passed via ecx (well, via stack, but it is also allocated in ecx) so it just reads whatever garbage edx has and doesn't work.
But wait a sec David, this doesn't really matter since we call the memory handlers from the DRC code, which knows how to use the right calling convention! And yeah you are right, except there's one very important call from C side (well actually a couple more): in sh2_reset we read the PC value from the reset vector of the CPU, which in this case makes the initial PC be garbage, and fun things happen (it starts from a weird addr and, amazingly, in some games it manages to display something on screen!)
TLDR: fun bug, I though I'd document it cause it's worth an explanation on why it has worked all this time if it's so obviously broken.
Turns out there is a bug... and has been there forever hidden in the gcc internals. As you can see the sh2_read_handler type lacks the regparm decorator, so in functions p32x_sh2_read8/16/32, the arguments are passed via stack. The functions expect the arguments to be passed via regs, so this would fail... Unless we get lucky and gcc happens to store the values in the same regs, out of chance!
And this is indeed what happens, just download the latest nightly binary and objdump'ed it to find:
The regs eax and edx are pushed to the stack as arguments, but the function will ignore them and use eax, edx as args, lucky! In my machine, using gcc 11 instead of gcc 9 I'm not that lucky at all:
As you can see eax is correct (that's why my stack trace shows a correct reference to SH2* and doesn't really crash the emulator) but the address is passed via ecx (well, via stack, but it is also allocated in ecx) so it just reads whatever garbage edx has and doesn't work.
But wait a sec David, this doesn't really matter since we call the memory handlers from the DRC code, which knows how to use the right calling convention! And yeah you are right, except there's one very important call from C side (well actually a couple more): in
sh2_reset
we read the PC value from the reset vector of the CPU, which in this case makes the initial PC begarbage
, and fun things happen (it starts from a weird addr and, amazingly, in some games it manages to display something on screen!)TLDR: fun bug, I though I'd document it cause it's worth an explanation on why it has worked all this time if it's so obviously broken.