Source: https://github.com/byuu/bsnes/issues/126
Full Throttle Racing's wave race writes to HDMA enable ($420c) at V=142,H=~1108-1110.
bsnes/higan looks like this:
status.hdmaPosition is H=1104.
So basically what ends up happening is occasionally, FTR will write to $420c exactly before the next call to dmaEdge(), which makes the HDMA enable for V=142, and most of the time it misses it by two clock cycles (the smallest unit of time) or more, and doesn't start HDMA until V=143. It's supposed to always be the latter.
From my experience, dmaEdge() really needs to be before the bus.write() inside write(), otherwise the DMA<>CPU sync functionality won't work properly.
So the only other possibility is that the CPU checks the HDMA channel enable bits earlier than in the dmaEdge() at H>=1104. If I make it check even 4 clocks earlier, FTR runs fine.
But the million dollar question is, how much earlier? What exact cycle does the CPU check $420c on?
For that we are going to need a new test ROM, and ideally we should test it on all channels in case it matters.
But I am really, really swamped for time and don't have my 21fx setup in commission right now.
So I'll throw it out there here in hopes I can find someone interested in uncovering and documenting a new SNES edge case. Any takers? ^-^;;
Full Throttle Racing's wave race writes to HDMA enable ($420c) at V=142,H=~1108-1110.
bsnes/higan looks like this:
Code:
auto CPU::write(uint24 address, uint8 data) -> void {
aluEdge();
status.clockCount = wait(address);
dmaEdge();
r.mar = address;
step(status.clockCount);
bus.write(address, r.mdr = data);
}
aluEdge();
status.clockCount = wait(address);
dmaEdge();
r.mar = address;
step(status.clockCount);
bus.write(address, r.mdr = data);
}
Code:
auto CPU::dmaEdge() -> void {
if(status.dmaActive) {
if(status.hdmaPending) {
status.hdmaPending = false;
if(hdmaEnable()) {
if(!dmaEnable()) {
step(counter.dma = 8 - dmaCounter());
}
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnable()) {
step(status.clockCount - counter.dma % status.clockCount);
status.dmaActive = false;
}
}
}
...
}
...
if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) {
status.hdmaTriggered = true;
if(hdmaActive()) {
status.hdmaPending = true;
status.hdmaMode = 1;
}
}
...
}
if(status.dmaActive) {
if(status.hdmaPending) {
status.hdmaPending = false;
if(hdmaEnable()) {
if(!dmaEnable()) {
step(counter.dma = 8 - dmaCounter());
}
status.hdmaMode == 0 ? hdmaSetup() : hdmaRun();
if(!dmaEnable()) {
step(status.clockCount - counter.dma % status.clockCount);
status.dmaActive = false;
}
}
}
...
}
...
if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) {
status.hdmaTriggered = true;
if(hdmaActive()) {
status.hdmaPending = true;
status.hdmaMode = 1;
}
}
...
}
Code:
auto CPU::Channel::hdmaActive() -> bool {
return hdmaEnable && !hdmaCompleted;
}
return hdmaEnable && !hdmaCompleted;
}
Code:
case 0x420c: //HDMAEN
for(uint n : range(8)) channels[n].hdmaEnable = data.bit(n);
return;
for(uint n : range(8)) channels[n].hdmaEnable = data.bit(n);
return;
status.hdmaPosition is H=1104.
So basically what ends up happening is occasionally, FTR will write to $420c exactly before the next call to dmaEdge(), which makes the HDMA enable for V=142, and most of the time it misses it by two clock cycles (the smallest unit of time) or more, and doesn't start HDMA until V=143. It's supposed to always be the latter.
From my experience, dmaEdge() really needs to be before the bus.write() inside write(), otherwise the DMA<>CPU sync functionality won't work properly.
So the only other possibility is that the CPU checks the HDMA channel enable bits earlier than in the dmaEdge() at H>=1104. If I make it check even 4 clocks earlier, FTR runs fine.
But the million dollar question is, how much earlier? What exact cycle does the CPU check $420c on?
For that we are going to need a new test ROM, and ideally we should test it on all channels in case it matters.
But I am really, really swamped for time and don't have my 21fx setup in commission right now.
So I'll throw it out there here in hopes I can find someone interested in uncovering and documenting a new SNES edge case. Any takers? ^-^;;