PPU emulation

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
PPU emulation
by on (#15325)
Just have to ask some things about accurate PPU emulation...

1. Ive read that the NES draws 3 * (the number of cycles it took for the last instruction), is this correct?

2. When it comes to emulation should I always handle one CPU instruction and then draw 3 * (the number of cycles it took for the last instruction) or should I draw one scanline directly when the number have exceeded 341 pixels. If Ive to do the first method how do I keep track of the Lost Sprites Flag, cause things can change each X drawn pixels and when is it reset?

3. Do VBlank occur (bit set in reg $2002, and NMI called) at the 262th scanline for NTSC?

4. When is sprite 0 Hit flag reset, each frame?

thanks!

by on (#15326)
1: For NTSC, yes, the PPU draws 1 pixel every 1/3rd CPU cycle (factor 3.2 for PAL).

2: That's up to you. A pixel renderer will be slow and accurate. A scanline renderer will be fast and inaccurate (regarding sprite 0 and sprite overflow timing).

3: At the end of the 242th scanline (consider the 1st being the pre-render scanline).

4/2: PPU status register is reset at the start of each frame (its vblank bit also resets when reading it). **thanks for the correction Quietust**

have fun

by on (#15327)
hap wrote:
4/2: PPU status register is reset when you read it, and at the start of each frame.


That only applies to the VBLANK flag (in bit 7) - the sprite 0 hit (bit 6) and sprite overflow (bit 5) flags are only reset at the start of each frame (specifically, at the first cycle of the pre-render scanline)

by on (#15328)
Is VBlank flag cleared as well in the start of each frame?
I also have some annoying bug in the PPU that makes Super Mario Bros don't give correct output, Maybe its possible for you to see what the problem is...

Image

by on (#15329)
yes
Re: PPU emulation
by on (#15336)
n6 wrote:
1. Ive read that the NES draws 3 * (the number of cycles it took for the last instruction), is this correct?


As has been mentioned, this is correct for NTSC (1 CPU cycle = 3 PPU cycles). However, if you plan on having PAL support in your emu, you cannot rely on that fixed ratio. On PAL, 1 CPU cycle = 3.2 PPU cycles

by on (#15342)
Regarding the screenshot, you might have downloaded an incorrupt dump of Super Mario Bros. Plus, it will lock up if it fails to detect the sprite 0 hit (sprite 0 is located at the coin in the statusbar).

by on (#15371)
hap wrote:
2: That's up to you. A pixel renderer will be slow and accurate. A scanline renderer will be fast and inaccurate (regarding sprite 0 and sprite overflow timing).

An adaptive renderer will be fast and accurate. It draws "interesting" scanlines using the pixel renderer and the rest using the scanline renderer. "Interesting" scanlines include at least those containing sprite 0 and those containing at least 8 sprites.

by on (#15374)
What tepples said, in terms of painting: Paint the large areas quickly with a wide roller, and paint the intricate areas with a small, precise brush. There's no reason to limit yourself to one or the other, which would result in a quick but sloppy or a precise but slow job. How do you know when you can use the wide roller? Hindsight is always 20/20, so don't paint until absolutely necessary, at which point you can look back and determine which brush is best.

by on (#15427)
n6: "Is VBlank flag cleared as well in the start of each frame?
I also have some annoying bug in the PPU that makes Super Mario Bros don't give correct output, Maybe its possible for you to see what the problem is...
"

I had a similar problem in a PPU engine I wrote a while back. The problem turned out to be that I was updating the VRAM address when PPU rendering was turned off via $2001. The VRAM address should NOT be auto-updated by the PPU during this time, only when background and/or sprite rendering is active.

by on (#15574)
Hm is this correct?
pseudo code for Write to VRAM:
Code:
if (!Vblank && Control2 & (SPR_ENABLE | BG_ENABLE))
 return

Write to the current address


if (Control2 & (SPR_ENABLE | BG_ENABLE))
{
 VramAddr += 32 or 1 depending on BIT(2) in Control1
}