OAMDATA $2003 corruption clarification?

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

Post Reply
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

OAMDATA $2003 corruption clarification?

Post by rainwarrior »

The current wiki description for OAMADDR / $2003 seems incomplete or inconsistent, and I'd like to try and clarify it. A few questions:


1. Writes to $2003 "reliably corrupt OAM" on 2C02G.

Do we have a better description of this reliable corruption? Does it overwrite specific bytes? Does it overwrite with specific values? Does it copy an 8 byte line from the current OAM address to the target one, similar to the other well-defined behaviour of leaving $2003 set to something not-zero before rendering?

The suggested workaround is simply writing all 256 bytes, which I feel should really more explicitly suggest using OAMDMA? The timing of decay makes a naive attempt to write all 256 bytes through $2004 prone to intermittent failure.

2. OAMADDR is set to 0 during each of ticks 257–320 (the sprite tile loading interval) of the pre-render and visible scanlines.

I feel we should explicitly clarify that this means that OAMADDR will always be 0 after the conclusion of a rendered frame?

3. The value of OAMADDR when sprite evaluation starts at tick 65 of the visible scanlines will determine where in OAM sprite evaluation starts, and hence which sprite gets treated as sprite 0. The first OAM entry to be checked during sprite evaluation is the one starting at OAM[OAMADDR]. If OAMADDR is unaligned and does not point to the y position (first byte) of an OAM entry, then whatever it points to (tile index, attribute, or x coordinate) will be reinterpreted as a y position, and the following bytes will be similarly reinterpreted. No more sprites will be found once the end of OAM is reached, effectively hiding any sprites before OAM[OAMADDR].

This almost seems to imply a contradiction with point 2. If OAMADDR is being set to 0, how and when does this case come up? Do we have to set OAMADDR in the early portion of the scanline (0-64) to make this happen?

Or is this an incorrect description of the known behaviour 4 below?

4. It is also the case that if OAMADDR is not less than eight when rendering starts, the eight bytes starting at OAMADDR & 0xF8 are copied to the first eight bytes of OAM; it seems likely that this is related.

So, is this a full explanation of the behaviour that 3 attempts to describe, or is there really something special about setting OAMADDR mid-scanline that it's trying to get across?

Is this also a full explanation of the corruption of 1 too? Is it simply a more general behaviour that all writes to OAMADDR, including the implicit one at tick 257 of a scanline, will copy the 8 bytes at the current address line to the target address line? Or is the corruption of 1 something else?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: OAMDATA $2003 corruption clarification?

Post by lidnariq »

rainwarrior wrote: Sun Jan 29, 2023 3:05 pm 1. Writes to $2003 "reliably corrupt OAM" on 2C02G.

Do we have a better description of this reliable corruption? Does it overwrite specific bytes? Does it overwrite with specific values? Does it copy an 8 byte line from the current OAM address to the target one, similar to the other well-defined behaviour of leaving $2003 set to something not-zero before rendering?
At least one of the corruptions on some of the CPU-PPU subpixel alignments is smearing the a subset of the row specified by contents of the data bus before the CPU drives the the data bus - usually $20 from $2003, meaning sprite pair 8 and 9, to whatever value is written.

There are other effects too, visible in my test ROM here.
I feel we should explicitly clarify that this means that OAMADDR will always be 0 after the conclusion of a rendered frame?
At the conclusion a frame that is not stopped prematurely, yes.
3. The value of OAMADDR when sprite evaluation starts at tick 65 of the visible scanlines will determine where in OAM sprite evaluation starts, and hence which sprite gets treated as sprite 0. The first OAM entry to be checked during sprite evaluation is the one starting at OAM[OAMADDR]. If OAMADDR is unaligned and does not point to the y position (first byte) of an OAM entry, then whatever it points to (tile index, attribute, or x coordinate) will be reinterpreted as a y position, and the following bytes will be similarly reinterpreted. No more sprites will be found once the end of OAM is reached, effectively hiding any sprites before OAM[OAMADDR].

This almost seems to imply a contradiction with point 2. If OAMADDR is being set to 0, how and when does this case come up? Do we have to set OAMADDR in the early portion of the scanline (0-64) to make this happen?
This is describing the effect within each sprite evaluation on a scanline. If rendering is off, and you turn it on at a specific X coordinate, this describes the behavior on that scanline.
Is this also a full explanation of the corruption of 1 too? Is it simply a more general behaviour that all writes to OAMADDR, including the implicit one at tick 257 of a scanline, will copy the 8 bytes at the current address line to the target address line? Or is the corruption of 1 something else?
#s 1 and 4 appear to be related due to incorrect handling by the OAM row/column selectors relative to the precharge (left half dot) sequencer, but I don't think they're otherwise related.
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: OAMDATA $2003 corruption clarification?

Post by rainwarrior »

Okay, so that clarifies that 3 was something completely independent. I've tried to revise the descriptions to make this clear.

So, the open question I had about this that was causing me to review the wiki page, was Fiskbit suggesting you could do partial OAM updates through $2004 by compensating for the $2003 issues.

From the description you gave of the $2003 write corruption, it sounds like there is no particular behaviour one can rely on. Is that correct?

However, with rendering safely returning OAMADDR to 0, it seems that this is just enough to be able to do a partial update without ever setting OAMADDR and an OAM that is pre-prepared with a DMA ahead of time.

So, to use $2004 for partial updates:
  • Sprite 0 and 1 will be overwritten by the pair of sprites wherever you update ends. This is really the entire issue to address. The issues with $2003 are avoided by starting from 0 as delivered by the previous frame's render.
  • First frame NMI: use DMA to fill sprite 0,1 and all sprites past the end of your partial update range with offscreen values.
  • Subsequent NMIs:
    • Write 8 bytes (sprite 0, 1) to $2004 which will automatically be overwritten when rendering begins.
    • Write your remaining sprites.
    • Write offscreen sprites to overwrite any previous non-empty sprites, end on an even sprite index (multiple of 8 bytes) that already contains offscreen sprite values.
    • Never write to $2003!
Is that valid?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: OAMDATA $2003 corruption clarification?

Post by lidnariq »

rainwarrior wrote: Sun Jan 29, 2023 4:02 pm From the description you gave of the $2003 write corruption, it sounds like there is no particular behaviour one can rely on. Is that correct?
At the very least, I don't know of one, and nothing was obvious when I looked earlier.
However, with rendering safely returning OAMADDR to 0, it seems that this is just enough to be able to do a partial update without ever setting OAMADDR and an OAM that is pre-prepared with a DMA ahead of time.
[...]
end on an even sprite index (multiple of 8 bytes) that already contains offscreen sprite values.
Yes, but it's slightly better - I believe it to be the case that the three LSbits of OAMADDR are safely reset to 0 without side effect when rendering starts naturally, so it should be possible to end with uploading 5 bytes to move the last two entries in OAM off-screen and thus that 0,1 are off-screen.

At the very I know for certain that one can upload the very first 7 bytes freely without a problem.
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: OAMDATA $2003 corruption clarification?

Post by rainwarrior »

lidnariq wrote: Sun Jan 29, 2023 5:04 pmYes, but it's slightly better - I believe it to be the case that the three LSbits of OAMADDR are safely reset to 0 without side effect when rendering starts naturally, so it should be possible to end with uploading 5 bytes to move the last two entries in OAM off-screen and thus that 0,1 are off-screen.
Does "safely reset to 0 without side effect" mean that the copy/overwrite behaviour does not happen if OAMADDR is not a multiple of 8? Or do you just mean that the low 3 bits are effectively ignored and there there's no additional/different behaviour to account for with them for the purpose of the overwrite? I'd assume the latter, but I can't entirely tell if that's what you meant from the wording.

I guess the savings here are just that you can save 3 writes when blanking the last sprite, in the case where you had more sprites in the previous frame and have to blank those.

Or more minimally you could write 4 bytes to do just 1 sprite. 5-7 writes if you only need to partially update a 2nd one?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: OAMDATA $2003 corruption clarification?

Post by lidnariq »

rainwarrior wrote: Sun Jan 29, 2023 5:21 pm Or do you just mean that the low 3 bits are effectively ignored and there there's no additional/different behaviour to account for with them for the purpose of the overwrite? I'd assume the latter, but I can't entirely tell if that's what you meant from the wording.
Yes, I meant the latter.
I guess the savings here are just that you can save 3 writes when blanking the last sprite, in the case where you had more sprites in the previous frame and have to blank those.
Well, more than just that - you can't actually fully update the last pair of sprites in this way, because once you make the final 8 writes OAMADDR will now point to the next row of RAM.
Or more minimally you could write 4 bytes to do just 1 sprite. 5-7 writes if you only need to partially update a 2nd one?
Exactly!
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: OAMDATA $2003 corruption clarification?

Post by rainwarrior »

lidnariq wrote: Sun Jan 29, 2023 5:48 pm
I guess the savings here are just that you can save 3 writes when blanking the last sprite, in the case where you had more sprites in the previous frame and have to blank those.
Well, more than just that - you can't actually fully update the last pair of sprites in this way, because once you make the final 8 writes OAMADDR will now point to the next row of RAM.
My thought was maybe you have a frame that uses sprites 2,3,4,5,6,7 and the next frame you only want to use 2,3. You have to blank 4,5,6,7 this frame, but with this condition you can stop after the first byte of 7, since the 6,7 blank is just as good as the pre-existing 8,9 blank.

Or if a doubled sprite is benign and you have an odd number of them you can similarly skip 3 bytes.
Post Reply