There was some discussion about manual $2004 writes being glitchy. I did more testing and it seems that it's only $2003 writes (SPRADDR) that are glitchy; merely changing SPRADDR often corrupts OAM. That's fine if you're about to update all of OAM, but it causes havoc when writing test code. I've found a reliable approach is to only use $2004 reads and writes for test code:
* Do initial $2003 write to set address.
* Write to OAM, e.g. 256 $2004 writes, either manually or via $4014
* Read OAM by reading $2004, then writing that back to $2004 to increment address, 256 times
Each step leaves the address at whatever it started as, avoiding the need for a $2003 write.
I have some code put a random value in $2003, fill OAM with random bytes, then read them back to verify. Masking off the missing bits in every third byte of a sprite, they are consistently matching on all four CPU-PPU alignments (just ran 250 iterations for each of the four alignments and they all passed).
So this might at least narrow down the OAM funkyness we've encountered in the past. I'm going to try some more tests to be sure $2004 doesn't have any weirdness in any alignment.
* Do initial $2003 write to set address.
* Write to OAM, e.g. 256 $2004 writes, either manually or via $4014
* Read OAM by reading $2004, then writing that back to $2004 to increment address, 256 times
Each step leaves the address at whatever it started as, avoiding the need for a $2003 write.
I have some code put a random value in $2003, fill OAM with random bytes, then read them back to verify. Masking off the missing bits in every third byte of a sprite, they are consistently matching on all four CPU-PPU alignments (just ran 250 iterations for each of the four alignments and they all passed).
So this might at least narrow down the OAM funkyness we've encountered in the past. I'm going to try some more tests to be sure $2004 doesn't have any weirdness in any alignment.