Damn ... I still can't believe I'm running Kirby 3 at full speed on my work PC (it used to be a P4 1.4GHz for the last three years. Just got the E4600 system upgrade.) Wonder how quick I'd get fired for bringing in my Xbox 360 controller ;)
Oh and that cat is fucking
awesome:
For those interested, a look at the architecture (src/chip/sa1):
I decided against the Star Ocean approach of manually transforming addresses against the memory map settings. Instead, I use Bus::map() to dynamically change the layout when needed. I actually create a secondary bus (named SA1Bus) to handle the different address bus layouts between the two chips.
Though definitely overkill for the normal S-CPU, the SA-1 utilizes every last bit of flexibility in Bus::map() to pull this off. I use the size delimiter for 00-3f|80-bf:6000-7fff BW-RAM page mirroring, the built-in max size wrapping with Nach's mirror() function so that no matter what you set the MMC to, it won't access memory out of bounds, and I actually override the MMIO dispatch table for 00-3f|80-bf:3000-37ff for faster S-CPU side IRAM access. Both chips share the MMIO table between 00-3f|80-bf:2200-23ff, and the SA-1 has the rest of the MMIO range unmapped.
The SA-1 CPU and the S-CPU opcode cores both inherit from CPUcore (src/cpu/core). Nice thing is that both modules build in 1/10th of a second for changes, and the 2-3 second core only has to be built once. After I port the S-SMP to the new pre-processor, I'll make an SMPcore class. So if anyone wants to try something crazy, they can change:
to:
... and have a 10.74MHz S-SMP co-processor :D
Done more for consistency than for need, of course.
I deferred S-CPU reset vector fetch until emulation begins, allowing mapping after CPU::power/reset() to work. 100% of the SA-1 for both busses is mapped in SA1Bus::init(). Will probably handle BS-X this way in the future.
The current bus collision detection is pretty simple. CPUcore gains regs.bus, which holds the current address bus value. It's set before a memory read / write begins, and cleared for I/O access.
The SA-1 checks to see what area it's accessing for its timing. If it's a shared area, it checks the S-CPU regs.bus value. If they point to the same logical device, you get a speed penalty added immediately.
The more proper way to do this would be to cut sCPU::bus_write() in half like I do with bus_read(), and only hold the regs.bus value for half of each cycle (~5.38MHz.) Then when the SA-1 tries to access the same chip, it should instead keep waiting until the S-CPU is not asserting the bus. Problem is this will slow down non-SA1 games by quite a bit (maybe 10%?) for no gain to them. Might be worth making bus_read/write a function pointer. Make up the penalty by not setting regs.bus at all in the normal method.
Still, I believe the end result will be ~99% the same, timing wise. The most important part, the major speed difference between SA-1 running in ROM while S-CPU runs in WRAM vs both running in ROM, is emulated well either way.
This is where syncing at the clock/bus-level really shines: without it, you wouldn't be able to reliably detect this edge case. The best you could do would be to look at cpu.regs.pc, which would fail to catch penalties from WRAM code accessing ROM.
Given this kind of performance ... I'm starting to think there'd be no point to true multi-threading of the individual components. We'd need at least a hexa-core, and by then one core is already fast enough. It'd only benefit cycle-PPU+SA-1 (assuming it even worked, and the mutexes didn't kill performance), but really ... that's not meant for gaming anyway. Probably not worth the trouble when otherwise the cothreaded model is more than sufficient.
I recall Snes9X required an absolute top-of-the-line system at first, and when SA-1 support first appeared. And ten years later you can run it on cell phones. Let's hope Moore's Law keeps up ;)
I wish I knew more about the Genesis architecture. If it syncs at the same speed, then Steve Snake would be right about not needing multi-threading there either. Still, Nemesis' is the emulator to watch for multi-threaded processor design.
how does 68fps on the E4600 compare to your E8400?
E8400 gets ~87fps at stock 3GHz. Easy to push those things to 3.6GHz, too. The Core i7 will be slower due to anemic stock clock and L2 cache. E8400 is a much better value for single-threaded apps.
That's also the absolute worst-case and without PGO. Average SA-1 speed would be ~110fps + ~15% for E8400.