What happens when disabling PPU in Hblank?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: What happens when disabling PPU in Hblank?

Post by dougeff »

Also, the rendering Y scroll is incremented on the second write to 2006...(if you write to the screen during rendering)

http://wiki.nesdev.com/w/index.php/PPU_ ... 8w_is_1.29

...shifting the screen up 1 pixel below the place where the second 2006 write occurred.
nesdoug.com -- blog/tutorial on programming for the NES
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

Sour wrote:In the code, the 2nd write to $2006 immediately changes VRAMAddr, and then writes to $2007 change the palette if VRAMAddr is still >= $3F00.
I did actually mention this above, but I didn't call it "VRAMAddr". In Visual2C02, it looks like it's explicitly the physical address on the address bus that controls whether reads come from and writes go to the palette. And although the second write to $2006 does immediately update "loopy_v", that value isn't what appears on the address bus during rendering.
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: What happens when disabling PPU in Hblank?

Post by Banshaku »

@sour

I didn't meant to imply that one emulator may do it better than the other but more that the behavior was different and had artifact, like some people mentioned, when switching to a nametable with a different color. Which meant "maybe" nintendulator could have been closer to the nes hardware but unless that I test my own code on hardware myself, it was just speculation. Now from looking at the code, there seems to be no special processing and the behavior may be not appropriate too.

I guess this specific scenario only affect people that creates new games and need to do special effect (albeit the time to do it in hblank is quite short) so it's no priority to get it to work like the hardware right away. Still, the timed code part got half of it right so it was already a good start.

I'm curious what happens on real hardware when you forget to close the PPU (I guess nothing is written and garbage occurs?). Still, emulators today improved so much compared to 10 years ago that the case that we need to absolutely test on hardware to confirming the real behavior are becoming smaller, which I'm quite grateful of such a time saver ;)
Sour
Posts: 890
Joined: Sun Feb 07, 2016 6:16 pm

Re: What happens when disabling PPU in Hblank?

Post by Sour »

lidnariq wrote:I did actually mention this above, but I didn't call it "VRAMAddr". In Visual2C02, it looks like it's explicitly the physical address on the address bus that controls whether reads come from and writes go to the palette. And although the second write to $2006 does immediately update "loopy_v", that value isn't what appears on the address bus during rendering.
I was actually just referring to the variable name in Nintendulator's code here. A bit unrelated, but I'm curious how the bus actually works: during rendering (scanline -1 to 239), the PPU puts the address it needs to fetch the data on the bus, and any register writes will directly set the bus to that address (but the PPU's next fetch will be affected by the new value)? When scanline 240 begins (or when rendering is turned off), the bus returns to the current value of "VRAMAddr" and keeps that value until the prerender line? (unless changed by register writes)

So, because the bus' value is controlled by the PPU during rendering, and the PPU never fetches anything in the $3000+ range, it's impossible to write to the palette during this time. Whereas writes to $0000-$2FFF via $2007 are possible, but will (almost?) never write to the correct address, because writing to $2006 during rendering will update VRAMAddr, but will not have an immediate impact on the bus' current value (but it will have an impact on the bus' value the next time the PPU reads VRAMAddr to fetch a byte for rendering purposes)?

I'm sure I'm missing some details & oversimplifying, but is this more or less how it works? If so, I can probably make a few changes to my PPU code to prevent stuff like writes during HBlank from working properly without too much trouble.
Banshaku wrote:I didn't meant to imply that one emulator may do it better than the other
No worries (and to be fair, Nintendulator does go more in-depth than Mesen when it comes to emulating the PPU's internal state), I'm just genuinely interested in making this more robust in Mesen if possible, especially since it would likely have no impact on performance.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

Sour wrote:during rendering (scanline -1 to 239), the PPU puts the address it needs to fetch the data on the bus
Yes.
, and any register writes will directly set the bus to that address (but the PPU's next fetch will be affected by the new value)?
Typo? I assume you mean "will not directly set"
When scanline 240 begins (or when rendering is turned off), the bus returns to the current value of "VRAMAddr" and keeps that value until the prerender line? (unless changed by register writes)
I believe that's accurate.
So, because the bus' value is controlled by the PPU during rendering, and the PPU never fetches anything in the $3000+ range, it's impossible to write to the palette during this time. Whereas writes to $0000-$2FFF via $2007 are possible, but will (almost?) never write to the correct address, because writing to $2006 during rendering will update VRAMAddr, but will not have an immediate impact on the bus' current value (but it will have an impact on the bus' value the next time the PPU reads VRAMAddr to fetch a byte for rendering purposes)?
That also sounds accurate.



Quickly tracking down the PPU A8 signal in Visual2C02...

* It's synchronized on left half dots (t13798)

There's a five-way analog multiplexer behind this, selecting between
* Vcc during attribute fetches = node 1162 (+hpos_eq_0-255_or_320-335_and_hpos_mod_8_eq_2_or_3_and_rendering)
* vramaddr_+v8 during node 2047 (must be used for both nametable fetches and the inactive portion)
* node10730 during node 1275 (node 10730 is just a floating node... node 1275 is part of the "CPU writes to $2007" handler)
* node 9110 during node 1963 = pattern table fetch
** Node 9110 ultimately comes from another multiplexer,
** ultimately spr_d4, after nodes 1870, 328 (++hpos_eq_256_to_319_and_rendering), and a right half dot
** ultimately _db4, after a right half dot, and nodes 1870 and 1910 and another (the same) right half dot

In contrast, PPU A13 (still synchronized on left half dots) only has a two-way analog multiplexer, selecting between
* Ground during node 1963 (pattern table fetch)
* node 2042 during node 2047 or attribute fetches (1162)
** node 2042 is ultimately OR(vramaddr_+v13 , rendering_1)

PPU A12 is the one that does all the dirty work, because it has to be able to operate in the most different ways.
bkg_pat_out, spr_pat_out, spr_d0 (8x16 mode), NOR(vramaddr_/v12, rendering_1)



oh. ... huh, the timing glitches we saw on [$2000] & 3 or writes to $2005 and $2006 should also happen to [$2000] & $38, but only for a single sliver.
Sour
Posts: 890
Joined: Sun Feb 07, 2016 6:16 pm

Re: What happens when disabling PPU in Hblank?

Post by Sour »

lidnariq wrote:Typo? I assume you mean "will not directly set"
Whoops, yea, I meant "will not" there.

So, I've been playing around with Visual NES a bit. It looks like the bus' address reverts to the current value of VRAMAddr on scanline 240, cycle 1. And then on the prerender scanline (-1) at cycle 1, it goes back to being whatever address the CPU is using for rendering (this is probably just the result of the PPU fetching the first NT byte)

I used a ROM that enables BG rendering only and keeps running this loop:

Code: Select all

-:
  STX $2007
  INX
  JMP -
Here's what the memory looks like after running it for a good 15+ full frames:
VramTesting.png
lidnariq wrote:oh. ... huh, the timing glitches we saw on [$2000] & 3 or writes to $2005 and $2006 should also happen to [$2000] & $38, but only for a single sliver.
Still haven't gotten around to implementing that one (or fully reading the thread, either), hoping to get to that relatively soon, though.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

Sour wrote:Still haven't gotten around to implementing that one (or fully reading the thread, either), hoping to get to that relatively soon, though.
Well, the shoot-through glitches only happen on certain alignments, so I'm not entirely certain when it's appropriate to emulate.
Sour
Posts: 890
Joined: Sun Feb 07, 2016 6:16 pm

Re: What happens when disabling PPU in Hblank?

Post by Sour »

Did some more testing and noticed that, in Visual NES, the code ended up writing proper values to CHR RAM starting at around $185 during vblank. I get something similar in Mesen (but it starts around $1A5 instead). I tried adding/tweaking the logic to suppress the V/H increments on write during rendering (e.g don't do them if the PPU is also doing them for its regular rendering on this cycle, like Nintendulator does), and while that does change the exact addresses that get written to, it usually ends up writing around $2185, instead (presumably because it did a few too many or too little vertical increments).

Either way, I think this is probably good enough - the real idea here is just preventing devs from being able to write to the palette/chr/nametables during HBlank without disabling rendering. The exact pattern created by the small test I wrote most likely varies based on CPU/PPU alignment and the like, anyway.
lidnariq wrote:Well, the shoot-through glitches only happen on certain alignments, so I'm not entirely certain when it's appropriate to emulate.
That was actually one of my questions. I guess in that case it would make more sense for this to be an option, e.g "Emulate CPU-PPU alignment-specific glitches" or the like (and disabled by default, because nobody wants to see scroll glitches while playing Zelda!)
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

Sour wrote:Did some more testing and noticed that, in Visual NES, the code ended up writing proper values to CHR RAM starting at around $185 during vblank. I get something similar in Mesen (but it starts around $1A5 instead). I tried adding/tweaking the logic to suppress the V/H increments on write during rendering (e.g don't do them if the PPU is also doing them for its regular rendering on this cycle, like Nintendulator does), and while that does change the exact addresses that get written to, it usually ends up writing around $2185, instead (presumably because it did a few too many or too little vertical increments).
I suspect it will smear the write across multiple bytes, for analog reasons.

The $2007-triggered rd cadence is aligned to pixels: ALE, idle, /RD, each for one aligned whole dot. However, it looks like $2007-triggered writes are not aligned. ALE is a left and right half dot; idle is just one left half dot; /WR is asserted for a right half dot but with the wrong value on the data bus, and then /WR is asserted for just a left half dot more with the correct value on the bus. (* note: this should be verified on hardware)

This should collide with the normal rendering cadence (left half dot: ALE. right half dot: idle. one aligned whole dot: /RD).

Internally to the PPU, if /RD is asserted, the multiplexed bus is high impedance, regardless of what the earlier multiplexers say should be presented.
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: What happens when disabling PPU in Hblank?

Post by Banshaku »

I may be getting closer to find the source of a bug that I had for a long time and it "may" be related to doing things in hblank. My question would be, it is possible that if I write to 2005 inside a MMC3 IRQ but not in hblank, weird things could occurs?

For example (it's not the complete code), if I comment out this part of code:

Code: Select all

	bit PPU_STATUS
	lda #0							; location of scroll
	sta PPU_SCROLL
which is used to reset position of scroll X in second NT (nt was selected just before), now my palette refresh code inside NMI is working fine by waiting for NMI. I put this part of the code only, palette refresh doesn't work anymore.

It's an odd bug but I'm getting closer to the source of the error. Maybe there is something else triggering the bug but now, at the least, I know where inside IRQ that makes it fails. Before, I only knew something related to IRQ was causing it but not what.

edit:

Found my bug, at last: changing the scroll affect the latch, which means when it try to write to the palette, you need to reset the latch with bit PPU_STATUS. I didn't realize it was affecting the palette too but it just make sense because you change the address and it may end up in the wrong location. I feel ashamed to make such a simple mistake for a long time :lol:
Fiskbit
Posts: 890
Joined: Sat Nov 18, 2017 9:15 pm

Re: What happens when disabling PPU in Hblank?

Post by Fiskbit »

Sour wrote:I guess in that case it would make more sense for this to be an option, e.g "Emulate CPU-PPU alignment-specific glitches" or the like (and disabled by default, because nobody wants to see scroll glitches while playing Zelda!)
To my knowledge, the scroll glitching in Zelda (tested with split_scroll_test_v2) occurs regardless of alignment, though it looks like one of the alignments caused my writes to appear to take effect one dot early. The single-scanline issues seen in the ppu_xxxx_glitch tests are the ones that are only present on some alignments.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

Fiskbit wrote:To my knowledge, the scroll glitching in Zelda (tested with split_scroll_test_v2) occurs regardless of alignment
Right. The shoot-through glitches are the ones that only happen on some alignments if the CPU write starts during the right half of dot 257.

The Zelda glitches are bus conflicts due to colliding increment and reload operations, and occur
1- to all 15 bits of both vramaddr_v and _t if the CPU's second write to $2006 finishes during dot 256.
2- to all six coarse horizontal position bits in both if the CPU's second write finishes during dot X where X%8 = 0 and (X≤256 or X=328 or X=336)
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: What happens when disabling PPU in Hblank?

Post by lidnariq »

lidnariq wrote: Sun Jan 27, 2019 9:54 pm I suspect it will smear [a] write [to $2007 during rendering] across multiple bytes, for analog reasons.
Thought I'd sit down and make a diagram for this, just to enumerate the possible cases.

Because the PPU will always align the state machines to pixels, we only really need to consider four possibilities:

#1) Read with ALE periods aligned:

Code: Select all

             /--- fetch 1 ----\ /-- fetch 2 -\
read cadence ALE idle RD   RD   ALE idle RD RD
read state   ALE ALE  idle idle RD  RD   -  -
Fetch1 will work normally. Fetch2 will end up seeing the result of an analog feedback loop going through the relevant external PPU memory and address latch. This value will also be what is read from $2007 later on. The analog feedback loop will only operate for one half pixel.

#2) Read with ALE periods skew:

Code: Select all

             /--- fetch 1 ---\ /--- fetch 2 --\
read cadence ALE idle RD   RD   ALE idle RD  RD
read state   -   -    ALE ALE  idle idle RD  RD
Fetch1 will see the result of the analog feedback loop for one entire pixel. Fetch2 will work normally, and the read from $2007 will just get that value.

#3) Write with ALE periods aligned:

Code: Select all

             /--- fetch 1 ---\ /-- fetch 2 -\
read cadence ALE idle RD   RD  ALE idle RD RD
write state  ALE ALE  idle WR  WR  -    -   -
During Fetch1, the external memory will see both RD and WR asserted at the same time. The PPU sets its data bus to high-impedance during this time. Most RAMs will treat this as a write of the value that was already at that address - if the RAM was fast enough to drive the data bus while RD was asserted before WR was asserted. A ROM won't care. During fetch2, it will attempt to write the lower 8 bits of the address to itself, but the real-world delays through the 74'373 may cause a forbidden state - changing address lines after /WR has been asserted - and any combination of those addresses may be changed as a result.

#4) Write with ALE periods skew:

Code: Select all

             /--- fetch 1 ---\ /-- fetch 2 --\
read cadence ALE idle RD  RD   ALE  idle RD RD
write state  -   -    ALE ALE  idle WR   WR -
Fetch1 will see the same feedback as possibility #2. Fetch2 will try to write the lower 8 bits of the address to the address, but it might be too fast to be valid.

The address bus multiplexer always picks the fetch cadence address in preference to the value to be written. This is at least partially due to t8782.

Each column above is a "half pixel", or the period that either of pclk0 or pclk1 are high. In Visua2c02, each state will show up 4 times.
Post Reply