Blargg's 7.nmi_timing.nes test

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Blargg's 7.nmi_timing.nes test
by on (#154269)
My emulator is still experiencing problems with NMI timing and I'm comparing it to Nintendulator's log to better understand the issues.

Here's a snippet of Nintendulator's log for 7.nmi_timing.nes:

Code:
E0DC  2C 02 20  BIT $2002 = FF                  A:44 X:00 Y:00 P:27 SP:F6 CYC:330 SL:240
E0DF  10 F4     BPL $E0D5                       A:44 X:00 Y:00 P:A7 SP:F6 CYC:  1 SL:241


The CPU in my emulator runs one CPU cycle at a time as opposed to advancing by the cycle length of each instruction. (It is based on this link.) And, for testing purposes, I can advance the PPU by 3 PPU cycles (for NTSC) before advancing the CPU by 1 CPU cycle or vice versa.

Nintendulator could print each statement just prior to the first CPU cycle of each instruction or immediately after the first CPU cycle. Meaning, it could print out the statement before the opcode fetch or after it. The effect is the same as running the PPU 3 cycles in advance of the CPU or after it.

The BIT instruction takes 4 CPU cycles to complete and it starts on PPU cycle 330, toward the end of the scanline, just before PPUSTATUS.7 is set. The read from the immediate address ($2002) occurs on the fourth CPU cycle. The break down should happen something like this:

Code:
CPU cycle 1 :: PPU cycles 330, 331, 332
CPU cycle 2 :: PPU cycles 333, 334, 335
CPU cycle 3 :: PPU cycles 336, 337, 338
CPU cycle 4 :: PPU cycles 339, 340,   0


According to the NTSC PPU Frame Timing Diagram, PPUSTATUS.7 is set on PPU cycle 1 of scanline 241. How is that captured by BIT?
Re: Blargg's 7.nmi_timing.nes test
by on (#154300)
zeroone wrote:
According to the NTSC PPU Frame Timing Diagram, PPUSTATUS.7 is set on PPU cycle 1 of scanline 241. How is that captured by BIT?


Correct me if I'm wrong, but I think these tests assume a certain PPU-CPU alignment. It could be the case that no emulator will ever get all the tests to pass without also simulating those alignments (and the reset behavior that selects between them). To my knowledge, no emulator even attempts to emulate more than one alignment.
Re: Blargg's 7.nmi_timing.nes test
by on (#154311)
beannaich wrote:
Correct me if I'm wrong, but I think these tests assume a certain PPU-CPU alignment. It could be the case that no emulator will ever get all the tests to pass without also simulating those alignments (and the reset behavior that selects between them). To my knowledge, no emulator even attempts to emulate more than one alignment.


Nintendulator and Nestopia consistently pass all of Blargg's NMI tests. And, a real NES supposedly can pass them all also.

In my emulator, Battletoads stage 2 freezes at random points due to an occasionally missed sprite 0 hit. I'm pretty certain it's a consequence of NMI timing since it fails Blargg's tests. At that point in the log, my emulator has yet to set PPUSTATUS.7. I do not understand why Nintendulator set it even after studying its source code.
Re: Blargg's 7.nmi_timing.nes test
by on (#154325)
It's a matter of PPU timing - if it's clocked before or after a CPU read/write. Yes, it does a difference.
Re: Blargg's 7.nmi_timing.nes test
by on (#154415)
Does Nintendulator run the CPU one CPU cycle at a time or does it jump ahead by the number of cycles in each instruction? The way the source is organized seems to suggest the second option, but I could be reading it wrong.
Re: Blargg's 7.nmi_timing.nes test
by on (#154429)
AFAIK, it does cycle by cycle, and the NTSC PPU runs for 3 cycles at each CPU memory access (read/write). It has, however, a frameskipping feature, but no clue if related.
Re: Blargg's 7.nmi_timing.nes test
by on (#154433)
I think I finally understand how Nintendulator produced that output. The CPU drives the PPU and APU. Every CPU cycle is associated with a memory read or a memory write. The CPU updates the PPU and APU in the read and write functions just prior to the memory access. It is structured in this unusual way because of the DMC transfer, which must wait for a read cycle. In addition, this CPU design avoids the need to maintain a state machine; instead of tracking the instruction cycle, it just counts the number of memory accesses after an instruction completes for timing purposes.

The scanline cycle number printed out in the log is the last PPU cycle that executed, not the next PPU cycle to be executed. So, the sequence behaves as follows:

Code:
CPU.BIT
  CPU cycle 1 (read)
    PPU cycle 331
    PPU cycle 332
    PPU cycle 333   
    Instruction Fetch
  CPU cycle 2 (read)
    PPU cycle 334
    PPU cycle 335
    PPU cycle 336
    Address low fetch
  CPU cycle 3 (read)
    PPU cycle 337
    PPU cycle 338
    PPU cycle 339
    Address high fetch
  CPU cycle 4 (read)
    PPU cycle 340
    PPU cycle   0
    PPU cycle   1
    Value fetch
    Apply BIT to value


To produce the same logs, I would have to modify my CPU to execute in that sequence. I am currently using a state machine. Nintendulator's CPU design likely runs a lot faster.
Re: Blargg's 7.nmi_timing.nes test
by on (#154443)
See? ;)
Re: Blargg's 7.nmi_timing.nes test
by on (#154493)
I transformed the state machine in my CPU implementation into the model used by Nintendulator. And, I subsequently was able to compare logs to patch the NMI issue. My emulator now passes all the vbl_nmi_timing test roms.

I moved onto the ppu_vbl_nmi tests. It currently fails 07-nmi_on_timing.nes. But, Nintendulator fails as well in exactly the same way:

Image

WTF?
Re: Blargg's 7.nmi_timing.nes test
by on (#154496)
It's a perfect timing about setting the VBlank flag 1 cycle before/after NMI enable ($2000 write). Plus, you can't set NMI on PPU cycle 0 (right after the 341th cycle).
Re: Blargg's 7.nmi_timing.nes test
by on (#154566)
After patching the NMI issues, my emulator still experiences problems with Battletoads stage 2. It does not seem to freeze any more, but occasionally the player and the enemies become invincible; neither can attack each other, rendering the stage unplayable after that happens. Also, in The Simpsons: Bart vs. the Space Mutants, the status bar at the bottom of the screen shakes.

My emulator seems to pass all the NMI and sprite 0 hit tests that Nintendulator passes. Can anyone suggest a test ROM that I should try?
Re: Blargg's 7.nmi_timing.nes test
by on (#154572)
zeroone wrote:
After patching the NMI issues


So, what was the problem?
Quote:
my emulator still experiences problems with Battletoads stage 2. It does not seem to freeze any more, but occasionally the player and the enemies become invincible; neither can attack each other, rendering the stage unplayable after that happens.


Are you sure about NOT allowing presses like up+down or left+right?

Quote:
Also, in The Simpsons: Bart vs. the Space Mutants, the status bar at the bottom of the screen shakes.


I'm quite positive it's related with MMC3 IRQ timing.

Quote:
My emulator seems to pass all the NMI and sprite 0 hit tests that Nintendulator passes. Can anyone suggest a test ROM that I should try?


Nope. :( Mine has the same problems... and I'm looking for something more precise for testing.
Re: Blargg's 7.nmi_timing.nes test
by on (#154577)
Zepper wrote:
Are you sure about NOT allowing presses like up+down or left+right?


Interesting. Actually, I did not add such a check. What makes you think there is a connection?

Edit: I just ran some tests. Pressing up+down in the Wookie Hole (stage 2) kills the player instantly. Pressing left+right creates the invincibility state that I described above. Also, I see that the wiki lists this as a known issue. Zepper, I think you might have solved one of my problems! Thanks.

Edit 2: With that check in place, Battletoads stage 2 runs fine. Thanks so much. As for what I did to the NMI issues, I only got it to perform as well as Nintendulator as mentioned above. And, that was achieved by making my emulator generate the same log format and then doing repeated diffs. The source code for Nintendulator provided many answers. For the Simpsons, I think it uses MMC2, not MMC3. I'll investigate the possibility that it is a mapper issue rather than an NMI issue. Thanks again.

Edit 3: Further testing revealed that it can still freeze. Nonetheless, this solved a contributor of the problem.
Re: Blargg's 7.nmi_timing.nes test
by on (#154593)
Code:
Filename....: Simpsons, The - Bart Vs. the Space Mutants (U).nes
Format......: NES
Mapper......: 001 (MMC1)
Data size...: 128K PRG ROM (8 x 16K banks)
             (PRG CRC $6F10097D)
              128K CHR ROM (16 x 8K banks)
Horizontal mirroring.
Battery data: no
Trainer.....: no
4-screen....: no
VS System...: no
PlayChoice10: no

File size: 262160 bytes.
Re: Blargg's 7.nmi_timing.nes test
by on (#154614)
I stand corrected: MMC1. But, more importantly, it is not an MMC3 issue.
Re: Blargg's 7.nmi_timing.nes test
by on (#154616)
Yup. My emu has the same problem of shaking the scorebar. It's probably related with sprite zero hits (Battletoads misses sprite zero hits, freezing the game).
Re: Blargg's 7.nmi_timing.nes test
by on (#154739)
In the latest beta version of Nintendulator, the status bar at the bottom on the screen in The Simpsons - Bart vs. the Space Mutants shakes as well. But, it doesn't in the latest official release.

Edit: Furthermore, the Wookie Hole (stage 2) of Battletoads freezes in the latest beta release. I can't pinpoint the change that broke these 2 games, but I found that delaying the sprite 0 hit flag by 1 PPU cycle prevents the status bar in The Simpsons from shaking. That is, it is almost as if the sprite 0 hit flag has an intermediate latch that delays the PPU register $2002 (PPUSTATUS) update by 1 PPU cycle.

Edit 2: With the intermediate sprite 0 hit flag latch in place that delays the PPUSTATUS register update by 1 PPU, it passes all the sprite 0 hit tests, including the timing related tests.

Edit 3: Battletoads still freezes even with that change.
Re: Blargg's 7.nmi_timing.nes test
by on (#154755)
@ Quietust

If you get a chance, please test Battletoads stage 2 and The Simpsons - Bart vs. the Space Mutants in the latest beta of Nintendulator.
Re: Blargg's 7.nmi_timing.nes test
by on (#154777)
It works, but here these tests have failed - 5. left clipping (#2) and 11. edge timing (#2).
Re: Blargg's 7.nmi_timing.nes test
by on (#154782)
I think I found a way to fix both games in the latest Nintendulator beta. The MemGet function is organized like this:

Code:
Handle PCM cycles
Run mapper cycle / Run PPU cycles / Run APU cycle
Read value from memory


I moved the final operation to the top:

Code:
Read value from memory
Handle PCM cycles
Run mapper cycle / Run PPU cycles / Run APU cycle


Here's the actual modified code:

Code:
unsigned char   MemGet (unsigned int Addr)
{
   int buf;

   // avoid a function call if possible - if it's ReadPRG, then run it inline
   if (ReadHandler[(Addr >> 12) & 0xF] == ReadPRG)
   {
      if (Readable[(Addr >> 12) & 0xF])
         buf = PRGPointer[(Addr >> 12) & 0xF][Addr & 0xFFF];
      else   buf = -1;
   }
   else   buf = ReadHandler[(Addr >> 12) & 0xF]((Addr >> 12) & 0xF, Addr & 0xFFF);
   if (buf != -1)
      LastRead = (unsigned char)buf;

   if (PCMCycles)
   {
      // zero out PCMCycles so we don't recurse back into this case during the following fetches
      int _PCMCycles = PCMCycles - 1;
      PCMCycles = 0;
      if (((Addr == 0x4016) || (Addr == 0x4017)))
      {
         // Consecutive controller port reads from this are treated as one
         if (_PCMCycles--)
            MemGet(Addr);
         while (_PCMCycles--)
            RunCycle();
      }
      else
      {
         // but other addresses see multiple reads as expected
         while (_PCMCycles--)
            MemGet(Addr);
      }
      APU::DPCM::Fetch();
   }

   RunCycle();

   return LastRead;
}


I have not been able to freeze Battletoads stage 2 after this change. And, the status bar no longer shakes in The Simpsons - Bart vs. the Space Mutants.

I did not modify SetMem. In fact, if I reorganize SetMem in an analogous way, the freezing problem reappears.

With that change, it passes all the Sprite 0 Hit tests. (@ Zepper: Ignore what I said about the sprite 0 hit latch theory.)

However, Nintendulator still does not pass all of Blargg's NMI tests. But, the prior version never was able to pass all the tests anyway.

Edit: It looks like I spoke too soon. That changes makes Battletoads far more stable. But, after a dozen runs through the Wookie Hole, it finally froze. I wonder if the older version of Nintendulator also has this issue.
Re: Blargg's 7.nmi_timing.nes test
by on (#154921)
I continued to play around with the Nintendulator source versions trying to further understand what change made the beta less stable. In the current release (Nintendulator 0.970), the coarse X increments occur every PPU cycle % 3. Whereas, in Nintendulator 0.975 Beta, it was adjusted to PPU cycle % 7, making it more authentic. Perhaps, more significantly, the Y increment was changed from PPU cycle 251 to 255. Both are shifted by 4 PPU cycles. Consequentially, the increments happen at least 1 CPU cycle earlier in the current release.

The Battletoads and The Simpsons issues are both sprite 0 hit related. And, this slight timing difference is the only explanation I can find for why the beta appears less accurate. As noted earlier in the thread, this may have be fudged to some extent since CPU and PPU cycles cannot execute simultaneously.