On this page
  1. Parameters
  2. Related
  3. Games
  4. References
  5. Sources

The PPU shows up to 64 sprites1 and finds each scanline’s sprites by searching OAM in order, keeping at most 8 sprites per scanline2. Sprites late in OAM order lose on a crowded line2. A game that changes the order each frame spreads the loss across sprites, so each one blinks instead of one vanishing.

  • Alternating orderMega Man 2 (1988). Crowded sprites take turns, so they flicker instead of vanishing.3
  • Rotating slotsSuper Mario Bros. (1985). When too many sprites share a line, a different one drops out each frame, so they flicker in turn.4

#Parameters

Super Mario Bros. leaves the first OAM slots, sprite 0 among them, in place and shuffles only the objects above them4. Mega Man 2 flips the whole list every other frame3.

Games

GameYearHowNote
Mega Man 21988Alternating ordercrowded sprites take turns, so they flicker instead of vanishing
Super Mario Bros.1985Rotating slotswhen too many sprites share a line, a different one drops out each frame, so they flicker in turn
Every claim about this mechanic, by game (5)

References

  1. ^The PPU's object attribute memory (OAM) holds a display list of up to 64 sprites, 4 bytes each, and sprite 0 is the one at OAM bytes $00-$03.WikiNESdev Wiki, “PPU OAM”
    How we know
    contains a display list of up to 64 sprites, where each sprite's information occupies 4 bytes.
    lead · archived copy ↗
    Sprite 0 is the sprite controlled by OAM addresses $00-$03
    Sprite 0 hits · archived copy ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-21.

    In the claim ledger

  2. ^ abOn each scanline the PPU copies the sprites in range, searching OAM in order, into a secondary OAM that holds 8, so a scanline shows at most 8 sprites and the later ones in OAM order drop out.WikiNESdev Wiki, “PPU OAM”
    How we know
    the PPU contains 32 bytes (enough for 8 sprites) of secondary OAM memory … a linear search of the entire primary OAM is carried out to find sprites that are within y range for the next scanline
    Internal operation · archived copy ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-21.

    In the claim ledger

  3. ^ abOn even frames _sprites writes Mega Man's object slots (0 to $0F), then the enemies' ($10 to $1F), then the energy meters; on odd frames it writes the meters first and the slots in reverse, $1F down to $10, then $0F down to 0, so objects that lose the 8-per-scanline race lose it only every other frame.Codelsmmega/mm2revised
    How we know
    12@time_stopper_flag_not_set:
    13	LDA z:znmi_frame
    14	AND #%00000001
    15	BNE @invert
    ⋮
    21@loop_1:
    22	JSR _draw_megaman_with_animation
    ⋮
    26	CMP #$10
    27	BNE @loop_1
    ⋮
    29@loop_2:
    30	JSR _draw_enemies_with_animation
    ⋮
    34	CMP #$20
    35	BNE @loop_2
    ⋮
    
    home/sprites.asm lines 12–38 at 94f0bf3 · upstream ↗
    43@invert:
    44	JSR _draw_bar
    ⋮
    47	LDA #$1F
    48	STA z:zsprites_pointer
    ⋮
    50@loop_3:
    51	JSR _draw_enemies_with_animation
    52	BCS @max_2
    53	DEC z:zsprites_pointer
    ⋮
    58@loop_4:
    59	JSR _draw_megaman_with_animation
    60	BCS @max_2
    61	DEC z:zsprites_pointer
    ⋮
    
    home/sprites.asm lines 43–62 at 94f0bf3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Revised after a check by a second AI model, 2026-10-21.

    In the claim ledger

  4. ^ abEvery unpaused frame SpriteShuffler adds one of three amounts, $58, $48, or $38 in turn, to each object's OAM offset at or above $28, wrapping within $28-$FF, so the objects' sprites change OAM slots each frame and a ninth sprite on a line drops out on different frames.CodeXkeeper0/smb1
    How we know
    165	LDA GamePauseStatus                          ; if in pause mode, do not bother with sprites at all
    ⋮
    168	JSR MoveSpritesOffscreen
    169	JSR SpriteShuffler
    
    src/prg.asm lines 164–169 at 1cc3c92 · upstream ↗
    240SpriteShuffler:
    ⋮
    242	LDA #$28                                     ; load preset value which will put it at
    243	STA $00                                      ; sprite #10
    244	LDX #$0e                                     ; start at the end of OAM data offsets
    245ShuffleLoop:
    246	LDA SprDataOffset,x                          ; check for offset value against
    247	CMP $00                                      ; the preset value
    248	BCC NextSprOffset                            ; if less, skip this part
    249	LDY SprShuffleAmtOffset                      ; get current offset to preset value we want to add
    250	CLC
    251	ADC SprShuffleAmt,y                          ; get shuffle amount, add to current sprite offset
    252	BCC StrSprOffset                             ; if not exceeded $ff, skip second add
    ⋮
    
    src/prg.asm lines 240–266 at 1cc3c92 · upstream ↗
    2225	LDA #$38                                     ; load sprite shuffle amounts to be used later
    2226	STA SprShuffleAmt+2
    2227	LDA #$48
    2228	STA SprShuffleAmt+1
    2229	LDA #$58
    2230	STA SprShuffleAmt
    
    src/prg.asm lines 2225–2230 at 1cc3c92 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-21.

    Checked by a second AI model, 2026-10-21.

    In the claim ledger

Sources

Code

Secondary