It seems like rapid changes to OAM + a scanline mask cutting off the bottom of the screen = Trouble with OAM. Does this sound familiar to anyone? Any advice?
EDIT: SOLVED: Make sure your PPU_MASK background shutoff is in a very specific piece of hBlank.
If the former, that's a known bug and requires very specific timing within the scanline to do so safely.
Nope. Somari is perfect. It's actually where i got the idea from! I play this version:
Turning off backgrounds & sprites with PPU_MASK. I am also writing 0's into PPU_ADDR; i actually saw Somari do it. Anyway, i figured it was some hardware quirk. Details good sir?
I am already rewriting the palette because that drops out sometimes too.
Turning rendering off in PPUMASK ($2001) before the PPU has finished evaluating sprites for that line (x=192 for lines with no sprites, x=240 for lines with at least one sprite) can corrupt OAM, leading to sprite flicker.So you have to set your MMC3 IRQ a scanline early and busy-wait there before you disable rendering.
Note that Fiskbit's test in the first link above implies a different window, but it's always "not earlier than dot N and not after dot 255"
If you're turning rendering off mid-frame and back on during vblank so that it starts at the normal time, then performing a rendering-disable write at approximately 62-253 should also be safe.
There are 3 regions of the scanline on common PPU revisions where disabling rendering puts the PPU into a bad state where it will corrupt one row of OAM the next time rendering becomes enabled. This means that even if you perform OAM DMA between disabling rendering and enabling it again, the data will become corrupted because the corruption occurs at the next start of rendering. These regions cover almost the entirety of the scanline, leaving just a small window at the end of hblank where it's always safe. Having rendering-disable take effect (which is delayed a few dots from when the write is done) in the range of approximately dots 1-64, 65-256, and 257-320 will trigger this behavior.
The exact behavior depends on the range. The 1-64 and 257-320 ranges appear to set one OAM row (8 bytes) equal to row 0. Which row it is depends on when in the range rendering was disabled, though the selection behavior is not the same for both ranges. This corruption appears guaranteed to happen if you disable within these ranges. Mesen supports this behavior as best we understand with the Options -> Emulation -> Advanced -> "Enable PPU OAM row corruption emulation" option, but it's implemented based mostly on the results from one test arrangement, so it may not be completely accurate. The end of the 318-340 safe range estimate assumes that the first 2 dots of the 1-64 range will "corrupt" row 0 by setting it to itself.
The 65-256 range is weirder. This range will only corrupt OAM if you enable rendering again mid-frame, and will only do so on some CPU/PPU alignments; the other alignments are completely safe. The exact behavior is not understood and thus not emulated. It can result in sprites appearing in unexpected places.
This corruption behavior was known by some NES developers and some games do indeed go out of their way to land there. For example, Micro Machines (on rev G+ PPUs) lands within the end-of-scanline safe region, and Bio Force Ape usually does (though is sometimes a little late, which should result in some sprite flicker on the map screen). Others, like Isolated Warrior, don't.
Wow, so, there are like "potholes" inside of hBlank that when you shut off rendering through PPU_MASK at a certain time it corrupts specific and predictable pieces of OAM. Furthermore, recopying OAM to attempt to correct it does not work. Wow... so this explains alot of what i've been seeing. ...In brighter news, i'm glad my NES isn't possessed by demons.
Thank you for writing this! What bizarre behavior... i'm happy to have a nice testable grasp on it.
I should be able to play with it from here. Thanks for the links / info!
Hm, that's confusing. How did we previously come up with the the 192-240 range?Fiskbit wrote: ↑Sat Mar 06, 2021 12:24 amThe 65-256 range is weirder. This range will only corrupt OAM if you enable rendering again mid-frame, and will only do so on some CPU/PPU alignments; the other alignments are completely safe. The exact behavior is not understood and thus not emulated. It can result in sprites appearing in unexpected places.
Here's tepples's reasoning, but: viewtopic.php?p=140414#p140414
It's pretty wild watching different parts of OAM get corrupted as you move the PPU shutoff around with the test. On my machine, the OAM corruption seems to be sequential through the table, but, i'm sure it would take one of you hardware wizards to nail it down.
Gotcha. That seems to be the ticket, but, also worth testing whatever scanline you shoot for; hopefully on affected hardware. Thanks again for all of your help!
Here is a simple code snippet that hits a nice cutoff:
Code: Select all
; * * *MMC3 Simple Scanline Blanker * * ; * * Now with Less OAM Corruption! * * IRQ: PHA ; (other things besides spinning in a loop) ; Wait for the golden window LDA #$11 SEC - ADC #$FE BNE - ; Turn off rendering STA PPU_ADDR STA PPU_ADDR STA PPU_MASK ; Acknowledge Interrupt STA $E000 PLA RTI
I unfortunately don't have a great idea of what's actually going on under the hood and am basing all of this on test results. Kitrinx was looking into this some, but I'm not sure that research has resulted in any useful results yet.
My understanding of the 65-256 range is really limited and it's possible there are portions of this that are safe or safe if sprites aren't present on the line. I hope someone is able to figure out what's going on from the transistor map, since I don't think testing will be sufficient for this.
Check original Somari, improvement vol.2 doesn't have this bug.