Super Cars relies on $2004 reads to time its code. As if this wasn't enough, it also uses the $B3 illegal opcode (seems like it's LAX ($xx),Y). Is this game the more insane licenced NES game ?
What especially interest me is how it reads $2004. As far I know, acessing $2007 during rendering is really bad because not only it screws the counters up, but also it can possibly make the PPU try to read and write at the same time and things like that.
However, reading $2004 seems to do nothing special : It doesn't increase any counter, so it's possible to read it during rendering without affecting anything badly, right ? At least Super Cars and Micro Machines do this.
What I'm more curious to know if how do we know what is read back. Super cars seems to load the first byte of the SPR-RAM buffer, and stuck in a loop until it differs from the value of $2004. After Sprite DMA, it make sense that you read the first OAM byte again since you wrote all 256 of them from 0 up to 255, so you read the first back. Adress in $2003 is changed during rendering, so reading $2004 will likely output a different byte, and then that's how Super Cars time its code to know when VBlank stopped. As far I know it's also possible to wait sprite-zero hit flag to be cleared for a similar effect.
I ask myself if it wouldn't be possible to abuse this $2004 trick to do much better tricks than this. More specifically, you would put a dummy sprite somewhere in the screen, and it doesn't have to be sprite zero, any number would do. By repeately reading $2004 and wait for it to match your dummy's sprite Y position, is it possible to reliably wait for that Y position in question ? I guess it should be possible.
That would allow simple cartridges to time their code without relying only on sprite zero hits anymore, so if that works then it's possible to do "pseudo-sprite zero hits" more than once per frame, with a totally transparent sprite and/or with no background enabled.
Also, if $2003 modifies itself a way so that you can also read OAM in non-multiples of 4 indexes, this would be much more tricky, as you could read a X pos, palette byte or tile byte as well, so you have to be sure that your "hit sprite" have a Y pos smaller than all sprites that potentially have a X Pos, palette byte or tile number that matches its Y pos. Sounds like a headache.
What especially interest me is how it reads $2004. As far I know, acessing $2007 during rendering is really bad because not only it screws the counters up, but also it can possibly make the PPU try to read and write at the same time and things like that.
However, reading $2004 seems to do nothing special : It doesn't increase any counter, so it's possible to read it during rendering without affecting anything badly, right ? At least Super Cars and Micro Machines do this.
What I'm more curious to know if how do we know what is read back. Super cars seems to load the first byte of the SPR-RAM buffer, and stuck in a loop until it differs from the value of $2004. After Sprite DMA, it make sense that you read the first OAM byte again since you wrote all 256 of them from 0 up to 255, so you read the first back. Adress in $2003 is changed during rendering, so reading $2004 will likely output a different byte, and then that's how Super Cars time its code to know when VBlank stopped. As far I know it's also possible to wait sprite-zero hit flag to be cleared for a similar effect.
I ask myself if it wouldn't be possible to abuse this $2004 trick to do much better tricks than this. More specifically, you would put a dummy sprite somewhere in the screen, and it doesn't have to be sprite zero, any number would do. By repeately reading $2004 and wait for it to match your dummy's sprite Y position, is it possible to reliably wait for that Y position in question ? I guess it should be possible.
That would allow simple cartridges to time their code without relying only on sprite zero hits anymore, so if that works then it's possible to do "pseudo-sprite zero hits" more than once per frame, with a totally transparent sprite and/or with no background enabled.
Also, if $2003 modifies itself a way so that you can also read OAM in non-multiples of 4 indexes, this would be much more tricky, as you could read a X pos, palette byte or tile byte as well, so you have to be sure that your "hit sprite" have a Y pos smaller than all sprites that potentially have a X Pos, palette byte or tile number that matches its Y pos. Sounds like a headache.