If you need only two sprites, both of which are fixed, what is an efficient way to do this? I don't know entirely about how the PPU OAM DRAM works, so I don't know what needs to be refreshed and how. I want to not waste a lot of RAM and ROM space in order to do this.
I'd still use DMA, just because, why not? You'll probably need more sprites elsewhere eventually.
In my first NES game, Lan Master, I only needed four sprites for the cursor and decided to handle them with direct OAM writes, without using DMA. It worked well in emulators, but turned out to be glitchy on the hardware. So I had to change it to DMA.
If you're really tight for RAM to the point where you can't waste a single 256-byte page, you could try wasting less than 64 nibbles. Fill a page with $FF and don't use the first byte of each 4-byte word. For example, if you need only four sprites, you can use $0200-$020F for these sprites, and then you can use $0211-$0213, $0215-$0217, $0219-$021B, ..., $02FD-$02FF for anything. You can even use the low nibble of the Y coordinate bytes ($0210, $0214, etc.) as long as the value remains within $F0-$FF to keep the unused sprite off-screen. If I remember correctly, Hot Seat Harry by Memblers uses this technique in page 0 to keep the code tiny.
What might be causing the glitchiness is that updates to the sprite address via $2003 aren't synchronized to the PPU at all, and so might change the address at a bad time. The address bump after writing $2004 passes through some clock logic, which probably makes it glitch-free.
If that's all there is to it, then maybe making sure you only use $2004 writes to update sprites would be enough to get it working safely. The sprite address is automatically set to zero during dots 257-320 on each scanline when rendering is enabled, so you wouldn't even need to write $2003 once if you start at the beginning of OAM.
You'd still need to write the sprite data each frame to prevent values from fading though. Not sure if the fading could mess with the never-updated bytes of OAM in some way when using this approach...
Edit: Hmm, I guess you'd only need to update the sprite data to keep it fresh if not rendering. Otherwise sprite evaluation should take care of it...
ulfalizer wrote:
Not sure if the fading could mess with the never-updated bytes of OAM in some way when using this approach...
And that is my most important question of it. And no, I know how I need it; I don't need more sprites. (Well, maybe I do, and maybe I made other mistakes too, but for the purpose of this discussion let's assume that what I wrote in my first message is correct; it will help someone, at least, or will help me with another program, at least.) We simply need two sprites whose positions, tile numbers, palette index, never change. (The PPU palette might itself change though, the CHR bank used might change, and sprites might sometimes be turned off, but the actual data in the OAM never needs to change (I am aware it needs to be written to every frame). Note that I don't have any hardware to test it with, and I am not expecting any emulators to be 100% accurate to test it either, so that is why I am asking on here.)
Quote:
Hmm, I guess you'd only need to update the sprite data to keep it fresh if not rendering. Otherwise sprite evaluation should take care of it...
It seems to me that might possibly be true, but have you tested this?
zzo38 wrote:
ulfalizer wrote:
Hmm, I guess you'd only need to update the sprite data to keep it fresh if not rendering. Otherwise sprite evaluation should take care of it...
It seems to me that might possibly be true, but have you tested this?
Nope. Depends on whether the VBlank period is long enough for values to fade I guess. If it's long enough, then that might mess with partially updating OAM, depending on what what value the cells that aren't updated fade "to".
A few major problems:
* on the 2C02, writes to OAM_ADDR destroy data in the OAM, so if you use OAM_ADDR, you must subsequently update the entirety to avoid glitches.
* If (OAM_ADDR & 0xF7) != 0 before rendering starts, then sprites 0 and 1 are replaced with the data for the two sprites starting at int(OAM_ADDR÷8)×2.
* If rendering is ever disabled for much longer than 20 scanlines, data decays out of OAM. (It'll become some pattern that's an artifact of temperature)
So to agree with ulfalizer, I think that you could get away with updating the first 7 bytes of OAM (but not the second sprite's X coordinate) because OAM_ADDR is reset to 0 at the end of rendering.
ulfalizer wrote:
depending on what what value the cells that aren't updated fade "to".
Is there any consistency in what they will fade to? Do they fade to all bits set, or all bits clear, or something like that, assuming they are also initialized with those values when the program starts?
It'll be a function of the exact temperature, voltage on the +5V line, what adjacent bits had previously been set to (all of "same word line", "physically adjacent", and "same bit line") if you aren't waiting for complete steady-state decay, and lots of other analog effects. When my NES is cold, it seems to mostly decay to a steady-state of almost all OAM addresses containing 0x0F, but there are at least 8 bytes of exceptions. When my NES is warm I know it does something different.
If you update the 2 sprites during vblank, it should be fine at that point.
The difficult part is the initialization of all 64 sprites. It's hard to do that without a DMA because it decays so quickly while rendering is off (the window of stability is only very slightly longer than vblank). If you're just filling with $FF or something you might be able to do it fast enough, but it's best to only use the direct OAM write during vblank.
One suggestion might be a rolling fill. You could blank 8 sprites per frame in each DMA (+ the 2 that you always use), so that any time rendering is on it will get initialized within a short period of time.