Re: Mesen-S - SNES Emulator
Posted: Wed Apr 10, 2019 9:21 pm
Both Circuit USA and Jumbo Ozaki rely on extremely accurate HDMA timing.
I see you implemented the early termination oddity with HDMA indirect transfers.
A fairly major issue in your current implementation is running all eight HDMA channels sequentially. You need to run through all eight channels and perform the HDMA transfers, and then go through all eight channels again to perform the HDMA indirect address fetches. Like so: https://gitlab.com/higan/higan/blob/mas ... ma.cpp#L33
Next, there are actually two parts to HDMA per-frame initialization. Regardless of whether any HDMA channels are enabled, you do this:
It takes no CPU time to complete (done in parallel.)
And then only if at least one HDMA channel is enabled:
Note the strange case of setting hdmaDoTransfer there. This was a weird edge case that ladida uncovered with HDMA.
That discussion, plus a test ROM, is here: https://forums.nesdev.com/viewtopic.php?f=12&t=16330
It's extremely odd behavior, I know. If you come up with a better theory, please do share.
Lastly, you should also implement the CPU->DMA->CPU sync timing instead of relying on 18 cycles being the average case. It's definitely annoying, and be careful about the edge case where HDMA triggers during DMA, that avoids the need for the DMA sync portion.
I'm fairly confident the above changes will fix those two bugs.
EDIT: well I looked it over, and it looks good as far as I can tell ... darn. I did find something interesting, though.
I'm not sure nocash's fullsnes is entirely accurate here, or based on real hardware testing. I've never heard any confirmation that the SMP TEST register flags affect the DSP. If that's true, then value & 0x04 (RAM disable) would completely cripple the DSP.
I suspect this is not the case. The DSP controls the RAM and clock, and gives the SMP interleaved access to it. It doesn't make sense for the DSP to be inspecting the state of SMP I/O registers for its own RAM accesses.
It really doesn't matter in practice since no games ever write to this register, of course.
I see you implemented the early termination oddity with HDMA indirect transfers.
A fairly major issue in your current implementation is running all eight HDMA channels sequentially. You need to run through all eight channels and perform the HDMA transfers, and then go through all eight channels again to perform the HDMA indirect address fetches. Like so: https://gitlab.com/higan/higan/blob/mas ... ma.cpp#L33
Next, there are actually two parts to HDMA per-frame initialization. Regardless of whether any HDMA channels are enabled, you do this:
Code: Select all
auto CPU::Channel::hdmaReset() -> void {
hdmaCompleted = false;
hdmaDoTransfer = false;
}And then only if at least one HDMA channel is enabled:
Code: Select all
auto CPU::hdmaSetup() -> void {
step(8); //we assume we've performed CPU->DMA sync already
for(auto& channel : channels) channel.hdmaSetup();
status.irqLock = true;
}
auto CPU::Channel::hdmaSetup() -> void {
hdmaDoTransfer = true; //note: needs hardware verification
if(!hdmaEnable) return;
dmaEnable = false; //HDMA will stop active DMA mid-transfer
hdmaAddress = sourceAddress;
lineCounter = 0;
hdmaReload();
}That discussion, plus a test ROM, is here: https://forums.nesdev.com/viewtopic.php?f=12&t=16330
It's extremely odd behavior, I know. If you come up with a better theory, please do share.
Lastly, you should also implement the CPU->DMA->CPU sync timing instead of relying on 18 cycles being the average case. It's definitely annoying, and be careful about the edge case where HDMA triggers during DMA, that avoids the need for the DMA sync portion.
I'm fairly confident the above changes will fix those two bugs.
Check your implementation of PCALL/TCALL very closely, especially related to how they interact with the IPLROM enabled or disabled.Then I still have to figure out why Illusion of Gaia and ActRaiser 2 refuse to boot (almost certain this is SPC-related).
EDIT: well I looked it over, and it looks good as far as I can tell ... darn. I did find something interesting, though.
Code: Select all
switch(addr) {
case 0xF0:
if(!CheckFlag(SpcFlags::DirectPage)) {
...
_state.WriteEnabled = value & 0x02;
_dsp->setEchoWriteEnabled(_state.WriteEnabled);I suspect this is not the case. The DSP controls the RAM and clock, and gives the SMP interleaved access to it. It doesn't make sense for the DSP to be inspecting the state of SMP I/O registers for its own RAM accesses.
It really doesn't matter in practice since no games ever write to this register, of course.