HDMA enable at HDMA trigger

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
Near
Founder of higan project
Posts: 1550
Joined: Mon Mar 27, 2006 5:23 pm

HDMA enable at HDMA trigger

Post by Near » Thu Sep 26, 2019 10:16 am

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:

Code: Select all

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);
}

Code: Select all

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;
    }
  }
  ...
}

Code: Select all

auto CPU::Channel::hdmaActive() -> bool {
  return hdmaEnable && !hdmaCompleted;
}

Code: Select all

  case 0x420c:  //HDMAEN
    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? ^-^;;

Kannagi
Posts: 100
Joined: Sun May 11, 2014 8:36 am
Location: France

Re: HDMA enable at HDMA trigger

Post by Kannagi » Thu Sep 26, 2019 12:22 pm

It's interesting even if I do not necessarily have the time to test to know how many cycles it checks the HDMA ;)

bklD
Posts: 12
Joined: Fri Sep 01, 2017 8:56 am

Re: HDMA enable at HDMA trigger

Post by bklD » Fri Sep 27, 2019 7:20 am

Hardware and Mesen-S: https://streamable.com/7h41o

Snes9x v1.60, higan v106 and bsnes (Fast PPU on and off): https://streamable.com/0rsy3 (0:25)
Does that mean mesen-s emulate this correct, or is this more something like "timing is off enough that the game starts working again"?

Near
Founder of higan project
Posts: 1550
Joined: Mon Mar 27, 2006 5:23 pm

Re: HDMA enable at HDMA trigger

Post by Near » Fri Sep 27, 2019 12:09 pm

To my knowledge, Sour isn't doing his own hardware tests (yet ^-^), so it likely works by chance. The timing being off by 1/10-millionth of a second could prevent this from happening.

We don't yet know exactly when the channel enable flags are polled by the CPU to determine whether or not to start HDMA for a scanline or for the initialization.

Post Reply