I know that reading any data from the PPU via $2007 (usually needed to read attribute tables for scrooling or pattern tables to have data on the CHR socket), the first data is a glitch and should be discarded. Now, I've a few questions :
- Does this dummy read increment the PPU pointer ? If so, I should substract one from the PPU adress before reading.
- Does $2007 reading increment the PPU pointer at all ? I know any write to $2007 will increase the PPU pointer by 1 or 32, but what about reading ?
Thanks
Yes, $2007 reads increment the VRAM address.
No, you shouldn't subtract one from the VRAM address on a $2007 read. Instead, a read should 1. return the current contents of the 1-byte read buffer, 2. copy the contents of the memory address into the 1-byte buffer, and finally 3. increment the VRAM address.
Ah, so you'll read the previous data into this buffer, i.e. what the PPU itself did read during the previous frame (in VBlank).
Thank you.
Since the first read since power-up is the one that's garbage, wouldn't it just be easier to read $2007 in the startup routine, then proceed with reading $2007 like normal?
Nope, you have to do a garbage read of $2007 any time you change the VRAM address (except by the auto-inc). The garbage read is actually reading what was previously in the buffer, and filling it with the byte at the specified VRAM address for the next read.
Hmm...that would explain why, not only does super mario bros render the title screen perfectly, but does so every single time the title screen routine gets run regardless of whether it's come from powerdown or not.
Thank goodness this thread was here when I needed it!
You'll find that a lot of CNROM games store much of their data in CHR ROM and read it out into main RAM with the PPU turned off. See Milon's Secret Castle.
Using the term "garbage read" is misleading since it implies that the buffer is somehow filled regularly with garbage, which is not the case. $2007 reads are buffered, which is best thought of as delaying things rather than returning garbage (except when reading palette RAM where there is no "delay", though the buffer is still invisibly refilled as if there were no palette RAM).
blargg wrote:
except when reading palette RAM where there is no "delay"
Really? So reading the palette returns valid values right on the first read? Now that's confusing...
tokumaru wrote:
blargg wrote:
except when reading palette RAM where there is no "delay"
Really? So reading the palette returns valid values right on the first read? Now that's confusing...
Not really, when you consider how it works. For VRAM accesses, it takes the PPU just barely too long to read the data and return it in time for the CPU, so it needs to delay by 1 read cycle. However, the palette is entirely contained within the PPU itself, so it doesn't need to spend any time loading it and can thus return it immediately.
blargg wrote:
Using the term "garbage read" is misleading
I've read the term "priming read" in similar contexts.
Quietust wrote:
However, the palette is entirely contained within the PPU itself, so it doesn't need to spend any time loading it and can thus return it immediately.
OK. So, external to the PPU are the 2KB of memory used for the name tables and attribute tables, and pattern table RAM possibly present in the cart. Carts can have extra name table and attribute memory also, right? So, all of these have a delay, and the only thing that's actually within the PPU is palette memory?
Yeah, I guess it's not complicated, but from the point of view of the programmer, $2006 and $2007 are used to access the same memory space, and seems weird that parts of it behave differently. Well, live and learn! =)
tokumaru wrote:
So, all of these have a delay, and the only thing that's actually within the PPU is palette memory?
And OAM. Both could be the same DRAM.
kyuusaku wrote:
And OAM. Both could be the same DRAM.
Oh, yeah. I didn't say anything about OAM because it isn't accessible through $2006/$2007. But since you brought it up, can it be read back? You know, most people don't even use $2003/$2004 anymore, but could these be used to read from OAM?
Of course you can read from OAM! How else could I have reverse-engineered the PPU's sprite fetch behaviour?
The only caveat is that reading $2004 does not increment the address pointer ($2003) - only writing does that.
I see. I don't see a lot of use for that, but it's still nice to know.
Quietust wrote:
reading $2004 does not increment the address pointer ($2003) - only writing does that.
That's probably what allowed you to use $2004 for reverse-engineering the sprite fetch behaviour, right? I mean, if each one of your reads would increment the address, you'd pretty much screw the fetching process.
tokumaru wrote:
I see. I don't see a lot of use for that, but it's still nice to know.
Quietust wrote:
reading $2004 does not increment the address pointer ($2003) - only writing does that.
That's probably what allowed you to use $2004 for reverse-engineering the sprite fetch behaviour, right? I mean, if each one of your reads would increment the address, you'd pretty much screw the fetching process.
Not necessarily - the PPU uses a separate counter to address sprite memory during rendering, since $2003 affects how that counter is initialized on
every scanline.