I've figured out roughly what goes on with the
VBlank in the PPU that causes the weird reading behavior.
Inside the PPU, there are two key players when it comes to signal timing:
- Read/write signals like /r2002 and /w2001.
- The _io_ce signal.
The read/write signals are generated directly by stuffing the
r/w line and the three address bus lines from the CPU into a decoder (around node 419 in Visual 2C02). The
r/w line and the address bus is set at the beginning of a read/write CPU cycle, so the read/write signals will have the corresponding values without delay as soon as the read or write starts (ignoring propagation delays - not sure if they're significant here).
The
_io_ce line comes from the address decoder (the chip marked "LS139" in
http://wiki.nesdev.com/w/images/f/f3/Neswires.jpg). The address decoder is set up so that the chip enable signals are generated during the second, high phase of M2 (it basically has an "AND M2" condition on all the outputs, though some are inverted). The
/DBE input to the PPU in the diagram corresponds to
_io_ce, and is also inverted, so that
_io_ce will be
low during the second, high phase of M2. M2 has a modified non-50% duty cycle (see
http://wiki.nesdev.com/w/index.php/CPU_ ... escription).
To summarize: For a read/write cycle from the CPU, the internal read/write signals in the PPU will get their values right away, while
_io_ce will go low during the second (high) phase of M2.
Inside the PPU, the same signal,
read_2002_output_vblank_flag, is used both to clear the VBlank flag and to hold the value of the read buffer (see the tutorial). While the read buffer is not held (closed off), it simply mirrors the VBlank flag. The relationship between the signals is
Code:
read_2002_output_vblank_flag = /r2002 NOR _io_ce
This means that
read_2002_output_vblank_flag goes high during the second phase of M2 during a read from $2002. Since this is the point where the value is held, we can make the following observation:
- The value returned by the read is the value the VBlank flag has when the high phase of M2 starts.
To figure out the rest, there's one more piece needed: The signal that sets the VBlank flag (node 5807 - let's call it "
set_vbl_flag") is high during the first PPU half-cycle (i.e., during
pclk0) of
vpos=241/
hpos=1. This means that if a read ends before the end of
set_vbl_flag, the VBlank flag will still get set and remain set. However, if a read ends after
set_vbl_flag (e.g. in a case where the high phase of M2 completely overlaps
set_vbl_flag), then the flag will be cleared during and after it is being set, suppressing the setting.
The NMI output is directly tied to the VBlank flag (it just has an additional "AND nmi_enabled" condition). Looking at the above, you can see how an NMI might be missed, as the high "clearing" phase of M2 might completely override
set_vbl_flag, and thus the NMI.
It's a bit confusing, but clicking around a bit helps. I also put some notes (mostly for myself) at
http://wiki.nesdev.com/w/index.php/User:Ulfalizer .
Sorry for not looking into the timing stuff you posted yet btw, but attacking the issue from two angles is probably a good idea at least.
Maybe
read_2002_output_vblank_flag should be renamed to something like "
read_2002_clear_vblank_flag_and_hold_value" by the way.