NMI timing

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NMI timing
by on (#223479)
Hi all!

So, I'm writing yet another NES emulator, for my own amusement and learning purposes. I've got most of the basic stuff working and decided to dive into some log standing timing issues.

I'm currently failing blargg's NMI timing test (05-nmi_timing, from ppu_vbl_nmi test suite).

Each loop of my CPU code is an instruction and it calls PPU/APU as needed to let them catch up before reads/writes. I poll interrupts at the second-to-last cycle, except in branches and interrupt routines.

This is my output:
4, 3, 3, 3, 3, 3, 3, 2, 2, 2

And a more detailed log:

Quote:
STA 0x2000.7: x 326 y 240
LDX: x 326 y 240
Interrupt polling: x 329 y 240
LDX: x 332 y 240
Interrupt polling: x 335 y 240
LDX: x 338 y 240
Interrupt polling: x 0 y 241
NMI triggered: x 1 y 241
LDX: x 3 y 241
Interrupt polling: x 6 y 241
Load NMI pointer:x 9 y 241

STA 0x2000.7: x 327 y 240
LDX: x 327 y 240
Interrupt polling: x 330 y 240
LDX: x 333 y 240
Interrupt polling: x 336 y 240
LDX: x 339 y 240
NMI triggered: x 1 y 241
Interrupt polling: x 1 y 241
Load NMI pointer:x 4 y 241

STA 0x2000.7: x 328 y 240
LDX: x 328 y 240
Interrupt polling: x 331 y 240
LDX: x 334 y 240
Interrupt polling: x 337 y 240
LDX: x 340 y 240
NMI triggered: x 1 y 241
Interrupt polling: x 2 y 241
Load NMI pointer:x 5 y 241

STA 0x2000.7: x 329 y 240
LDX: x 329 y 240
Interrupt polling: x 332 y 240
LDX: x 335 y 240
Interrupt polling: x 338 y 240
LDX: x 0 y 241
NMI triggered: x 1 y 241
Interrupt polling: x 3 y 241
Load NMI pointer:x 6 y 241

STA 0x2000.7: x 330 y 240
LDX: x 330 y 240
Interrupt polling: x 333 y 240
LDX: x 336 y 240
Interrupt polling: x 339 y 240
NMI triggered: x 1 y 241
LDX: x 1 y 241
Interrupt polling: x 4 y 241
Load NMI pointer:x 7 y 241

STA 0x2000.7: x 331 y 240
LDX: x 331 y 240
Interrupt polling: x 334 y 240
LDX: x 337 y 240
Interrupt polling: x 340 y 240
NMI triggered: x 1 y 241
LDX: x 2 y 241
Interrupt polling: x 5 y 241
Load NMI pointer:x 8 y 241

STA 0x2000.7: x 332 y 240
LDX: x 332 y 240
Interrupt polling: x 335 y 240
LDX: x 338 y 240
Interrupt polling: x 0 y 241
NMI triggered: x 1 y 241
LDX: x 3 y 241
Interrupt polling: x 6 y 241
Load NMI pointer:x 9 y 241

STA 0x2000.7: x 333 y 240
LDX: x 333 y 240
Interrupt polling: x 336 y 240
LDX: x 339 y 240
NMI triggered: x 1 y 241
Interrupt polling: x 1 y 241
Load NMI pointer:x 4 y 241

STA 0x2000.7: x 334 y 240
LDX: x 334 y 240
Interrupt polling: x 337 y 240
LDX: x 340 y 240
NMI triggered: x 1 y 241
Interrupt polling: x 2 y 241
Load NMI pointer:x 5 y 241

STA 0x2000.7: x 335 y 240
LDX: x 335 y 240
Interrupt polling: x 338 y 240
LDX: x 0 y 241
NMI triggered: x 1 y 241
Interrupt polling: x 3 y 241
Load NMI pointer:x 6 y 241


Any input is much appreciated!
Re: NMI timing
by on (#223482)
Does Nintendulator pass that particular test?
Re: NMI timing
by on (#223486)
zeroone wrote:
Does Nintendulator pass that particular test?


I'll be sure to check that out!

I found that I can pass the test by polling NMI 2 ppu cycles earlier. Basically I was polling it at the first ppu cycle of the last CPU cycle of the instruction. Not sure if this is a hack or proper fix though....
Re: NMI timing
by on (#223495)
I'll help you. Firstly, about $2002 reads...
Code:
8) Reading (VBlank) flag 4 PPU clocks before set shouldn't suppress NMI
2) Reading flag 3 PPU clocks before set shouldn't suppress NMI
5) Reading flag 2 PPU clocks before set shouldn't suppress NMI
9)*Reading flag 1 PPU clock before set should suppress NMI
3)*Reading flag when it's set should suppress NMI
6)*Reading flag 1 PPU clock after set should suppress NMI
10)Reading flag 2 PPU clocks after set shouldn't suppress NMI
4) Reading flag 3 PPU clocks after set shouldn't suppress NMI
7) Reading flag 4 PPU clocks after set shouldn't suppress NMI

Next, about $2002 writes, the NMI is suppressed if disabled by bit 7 and the VBlank flag is still set. Plus, the NMI is requested if bit 7 is set and the current PPU cycle isn't zero.
zeroone wrote:
Does Nintendulator pass that particular test?

Yes.
Attachment:
nmi_nintendulator.png
nmi_nintendulator.png [ 6.53 KiB | Viewed 6720 times ]
Re: NMI timing
by on (#223565)
Thanks, Zepper!

I don't think this has to do with NMI suppression. This is testing the number of instructions between NMI trigger and the NMI instruction. Seems that shifting back NMI polling a couple ppu cycles fixes this, and I pass that whole test suite now. I also played through stage 2 in Battletoads without problems.

Now on to tackle IRQs which are currently very inaccurate.
Re: NMI timing
by on (#223587)
JonteP wrote:
Seems that shifting back NMI polling a couple ppu cycles fixes this, and I pass that whole test suite now.

NMI is requested at the end of the dummy scanline. Do you do the same?
Re: NMI timing
by on (#223636)
According to my log above, NMI is triggered at scanline 241 (first vblank scanline) dot 1. Is that wrong?
Re: NMI timing
by on (#223684)
JonteP wrote:
According to my log above, NMI is triggered at scanline 241 (first vblank scanline) dot 1. Is that wrong?

You know about the CPU-PPU alignment, right? Well, during my tests, I had to request NMI 1 PPU cycle earlier. In other words, at line 240 cycle 341. Technically, it would be the same, but I clock the PPU before the CPU cycle.
Re: NMI timing
by on (#223930)
Zepper wrote:
JonteP wrote:
According to my log above, NMI is triggered at scanline 241 (first vblank scanline) dot 1. Is that wrong?

You know about the CPU-PPU alignment, right? Well, during my tests, I had to request NMI 1 PPU cycle earlier. In other words, at line 240 cycle 341. Technically, it would be the same, but I clock the PPU before the CPU cycle.


Yes, if you mean that they are not running in parallel, I'm aware of it. And I'm inclined to think that this is what I am compensating for.

Anyway, thanks for helping out!