Page 1 of 1

Possible improvement to PPU Sprite Evaluation section

Posted: Mon Jun 28, 2021 9:45 pm
by jeff.r.day
I was reading https://wiki.nesdev.com/w/index.php/PPU ... evaluation and the section "Details" item 2 "Cycles 65-256: Sprite evaluation" seems like it could use improvement.

What I find confusing is it talks about what happens on Odd and Even cycles, but then immediately moves into a numbered list where the numbers don't represent cycles (they mostly seem to represent pairs of cycles, or multiple sets of read/write cycle pairs.)

I would like to propose a partial re-write of that sub-section. Here's my first pass at rewriting:
  • 2. Cycles 65-256: Sprite evaluation
    • On odd cycles, data is read from (primary) OAM
    • On even cycles, data is written to secondary OAM (unless secondary OAM is full, in which case it will read the value in secondary OAM instead)
    • Over these 191 read and write cycles until HBLANK, behavior can be explained as a state-machine which starts in "Copying" mode:
      • Copying: (Starts with n=0.) First 2 cycles: Read a sprite's Y-coordinate (OAM[n][0], writing it to the next open slot in secondary OAM (unless 8 sprites have been found, or all 64 sprites have already been evaluated, in which case the write is ignored.)
        • a) If Y-coordinate is out of range, continue next cycle with "Evaluation/Loop" (below.)
        • If in range, the next 6 cycles are spent reading and writing the remaining bytes of sprite data (OAM[n][1] thru OAM[n][3]) to copy them into secondary OAM. The cycle after the last write will continue with "Evaluation/Loop" (below.)
      • Evaluation/Loop: (2 cycles) Increment n, potentially disable writes to Secondary OAM, and change state:
        • a) If n has overflowed back to zero (all 64 sprites evaluated), disable writes to Secondary OAM and continue next cycle with Copying (Even though it won't copy anything further.)
        • b) If less than 8 sprites have been found, continue next cycle with Copying.
        • c) If exactly 8 sprites have been found, disable writes to Secondary OAM (because it is full.) This causes sprites in back to drop out. Continue next cycle with Overflow Logic (below.)
      • Overflow Logic: (Starts with m=0.) First cycle: Read OAM[n][m] and evaluate it as a Y-coordinate (whether or not it is.)
        • a) If the value is in-range, the next 7 cycles are spent as follows: First, set the overflow flag in $2002, then read the next 3 entries of OAM (incrementing 'm' after each byte and incrementing 'n' when 'm' overflows); if m = 3, increment n.
        • b) If the value is not in range, the next cycle increments n and m (without carry). (The m increment is a hardware bug - if only n was incremented, the overflow flag would be set whenever more than 8 sprites were present on the same scanline, as expected.)
          • If n overflows to 0, disable further writes to Secondary OAM and continue next cycle with Copying (which won't do anything further.)
          • If n did not overflow to 0, continue next cycle with first step of Overflow Logic, starting with the current values of n of m.
I am not 100% certain if I understand the way this work is distributed across the 191 cycles correctly, so this needs to be reviewed for accuracy. I'm hoping for comment on this and corresponding updates to the wiki to make the cycle behavior more clear once this has been reviewed and improved as necessary.

By the way, I'm new here. Hi, and thank you to everyone who has put these detailed resources together.