ppu flickering

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
ppu flickering
by on (#55267)
- I'd like some advice about fixing screen flickering in Battletoads, level 1. Plus, the same occurs with games that triggers DMC IRQs, like Kid Dracula.

by on (#55272)
Check your timing (nmi timing/'delay', sprite/apu dma cycles, additional cycles for branch taken/page crossing, etc.). That said, my scanline-based ppu has the same issue, to a lesser degree (level 2 isn't as bad), my cycle-based ppu is fine. If you're performing scanline-based emulation, you might not be able to get it to work without hacks.

DMC IRQs don't matter for Battletoads (my emulator doesn't even support them).

by on (#55275)
- I use a pixel-by-pixel render. My emu pass OK for all those test suites.

by on (#55276)
Looks like Rad Racer has some glitches as well. Check your PPU timing (reset horizontal portion of vram address at cycle 257, etc.)

by on (#55324)
- Looks like it's related with sprite zero strikes.

by on (#55361)
Yes, Battletoads is very sensitive to sprite 0 hit timing... why, exactly, or in relation to what other event, I don't know. What I do know is that, in order to get it to work, I had to fix some timing bugs that I had (unfortunately, I don't remember exactly what it was. It may have been the horizontal counter update at cycle 257, which is why I checked Rad Racer, which is sensitive to this. It may be NMI timing as well.)

Has anyone looked at the code to see exactly what's going on here?

by on (#55364)
- Well, for the stage 2 (vertical tunnel), it's a LDA #$40, BIT $2002, BEQ $863E. It hangs. I can "fix" by forcing a $40 into $2002. I don't know about the 1st stage though.

EDIT: when you say "cycle 257", is the 257th cycle? Do you count 0...255,256,>257< or 0...255,>256< ?

by on (#55366)
Zepper wrote:
0...255,256,>257<

this

other timings: vertical counter update on cycle 251, and vram is updated with the vram latch/temp/whatever you call it, on cycle 304 of the pre-render scanline if drawing is enabled.

by on (#55414)
- Quote from wiki:
Quote:
Sprite data is delayed by one scanline; you must subtract 1 from the sprite's Y coordinate before writing it here. Hide a sprite by writing any values in $EF-$FF here.


- To evaluate what sprites are in-range, I do:
Code:
(unsigned int)(line - 21 - oam_buffer) <= (unsigned int)ppu->is_8x16)

- first visible line is 21 here; line 20 is the dummy scanline, or -1.
- ppu->is_8x16 can be 7 or 15.
- oam_buffer is the current sprite value, Y.


- For $EF, the result is zero, so it's in-range... but something is wrong here. What?

by on (#55428)
since you said it's related to the sprite zero hit and asked about sprite eval code, maybe i guess check the rendering code that the sprites get rendered one scanline after, rather than rendering them by adding one to it once you grab it from the previous scanline. Though I don't really know if that is the case, but it was one of the symptoms I had that I forgot to check when I was doing an emu. Could also be the case of forgetting to clear sprite zero somewhere.

by on (#55430)
If a sprite's Y coordinate in OAM is 239, its top visible pixel is below the visible area. The PPU fetches its OAM entries during line 260*, fetches its pattern slivers in the following hblank, and never actually displays these pattern slivers because line 261 is the post-render line.

* Assuming the visible scanlines are 21 through 260

by on (#55433)
tepples wrote:
If a sprite's Y coordinate in OAM is 239, its top visible pixel is below the visible area. The PPU fetches its OAM entries during line 260*, fetches its pattern slivers in the following hblank, and never actually displays these pattern slivers because line 261 is the post-render line.

* Assuming the visible scanlines are 21 through 260


- Ah. That demo s0.nes writes $80 as Y, then a lot of $EF, making the sprites to be copied into secondary OAM, plus "triggering" the weird evaluation scheme. So, it's correct after all.

EDIT: my sprite zero strike code was broken. Things are sweet now, it has been fixed. An internal flag was being incorrectly set.