4 ms·
Interestingly I wrote a 6502 (actually Atari 6507) emulator and Z80 emulator for the MIPS R3000 in the original SONY PSX, and the only way to get the speed I ne
by justinlloyd 4y ago
Interestingly I wrote a 6502 (actually Atari 6507) emulator and Z80 emulator for the MIPS R3000 in the original SONY PSX, and the only way to get the speed I needed was to use calculated jump tables rather than relying on a switch case. I know that C provides that calculated jump, but even that wasn't enough. Eventually I rewrote the entire emulator in R3000. And then I eventually switched to using CPU retargeting/recompilation from 6502 to R3000, and then used the same recompilation trick to get 6502 emulation running at a decent speed on the ARM CPU in the Nintendo Gameboy Advance.
- klelatti 4y ago> CPU retargeting/recompilation from 6502 to R3000 Is this converting 6502 assembly to R3000 - not recompiling the source?
- justinlloyd 4y agoJa. Take 2600 ROM dump and convert to R3000.
- flohofwoe 4y agoThat depends a lot on the compiler, I guess in the 90's compilers didn't optimize switch-case that well yet. In modern compilers a switch-case may end up faster than a function-pointer jump table, because even though the compiler will turn the switch-case into a jump table as well, the code that's called through the jump table doesn't have the prologue/epilogue overhead of regular function call. In my emulators I also haven't seen a difference between a regular switch case, and 'computed goto' (but depending on how the decoder loop looks like, computed goto may still provide have an advantage, just not in my CPU emulators).
- justinlloyd 4y agoThe old compilers optimized the switch/case, just not as well as something from 2022, and there were lots of edge cases too. And limited registers, and small caches and so forth. Plus, these were CPUs that were at the boundary of what could be done in terms of emulation of another CPU and everything else about the device being emulated too. Today we have the advantage of far more advanced compiler technology and far more advanced CPU technology that runs crazy fast, and a greater understanding of how to emulate the machines and best practices for creating those emulators. 25 years ago, not so much. The PS2 and the XBOX were the first consoles where you could just take MAME or the 2600 VCS emulator I wrote and compile it to the target console and "it just worked." I ported MAME to the PS2 and ran six different games on a tumbling cube, and then MAME to the XBOX shortly afterwards, and Dolphin emulator on XBOX and PS2 as well - though again, smacking up against what that generation of consoles could realistically emulate.
- justinlloyd 4y agoTo add to this I didn't do a function-pointer jump table. A function pointer jump table was too slow and involved multiple look-ups. I took the opcode of the 6502 or Z80 instruction, shifted it, then added that value to the PC (program counter) register. The new value in the program counter was effectively the goto. All the assembly functions that handled each opcode were aligned on a multiple of powers of two value, 2048 bytes IIRC. Though it might have been 1024 bytes, this is over 25 years ago. The instruction immediately after the PC write set the the return address for the function about to be called, the return address write worked due to the pipelining in the MIPS CPU. The return address was the instruction immediately after the PC write. The opcode function executes, and then returns back to the write instruction, which then falls through to the no-op (opcode zero), which handled the gathering of the next opcode to be processed, any updates to the display and hardware, cleaned up the stack, and then looped to process the next opcode.