scanline sprite detection

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
scanline sprite detection
by on (#47502)
I was curious about in the sprite ram one should use y - 1 instead of y.
Could be that this way sprite detection on scanline is easier with NES logic?
Not(~) this value to get -y, and so:

Code:
uint8_t sprite_ram[0xFF], *py, sprite_height, scanline;
int count;

sprite_height = reg2000 & 0x20 ? 16 : 8;
for (py = sprite_ram, count = 0 ; py < sprite_ram + 0xFF ; py += 4)
   if (scanline + ~*py < sprite_height) {
      /* sprite is in scanline */
      if (++count >= 8)
         break;
   }

However this not work for *py = 0xEF-0xFE, where sprite should be hidden.
(so I think a check should be made for this).
Does exists a more straightforward method?
Bye,
tano

by on (#47505)
The sprite Y coord is off by 1 because the NES is looking for sprites to be drawn on the next scanline. IE: when sprites are found to be in-range when scanline=5, then they won't be visible until scanline 6. IE:

Code:
if( (unsigned)( scanline - *py ) < sprite_height )
{
  // sprite to be drawn on scanline+1
}


If you're looking for sprites on this scanline instead of looking ahead to the next scanline, then you can simply subtract 1 from your scanline:

Code:
int prevline = scanline - 1;

for( ... )
{
  if( (unsigned)( prevline - *py ) < sprite_height )
  {
    // to be drawn this scanline
  }
}

by on (#47517)
It has sense.
But why sprite with the y - 1 attribute setted to 0xef - 0xfe are not
just clipped instead of being hidden?

by on (#47548)
Because the Y coord is not signed, it's unsigned. And the in-range checker is large enough so that the subtraction does not cause it to wrap.

Or because $F0 and up are below the bottom of the screen. Take your pick.

note:
it's not really 0xEF-0xFE,.. it's more like 0xF0-0xFF. 0xEF may not be [i]visible[i], but those sprites will still be found to be "in range" and the sprite overflow flag might trip as a result.

by on (#47572)
No, I am not talking about your code :-)
I was asking why NES behavior was not just clipping instead of
hide (if anybody could have suppositions about that)
Thanks,
tano

by on (#47576)
tanoatnd wrote:
No, I am not talking about your code :-)


Neither was I. The NES probably does something exactly like that in hardware.

Quote:
I was asking why NES behavior was not just clipping instead of
hide


Well what do you mean by "clipping"? I'm assuming you mean that it would treat a value of $FE as "-2" so you can scroll sprites off the top of the screen.

The problem with that would be that there's no way to hide sprites if that were the case. The only way to prevent a sprite from being visible on the NES is to move it off the bottom of the screen.

by on (#47577)
That is the answer...
Sprite Y coordinates are unsigned, and have 1 added onto them.
Any Y coordinate value >= 239 (Y coorinate >= 240) is completely offscreen. The values do not wrap, you can't put a sprite at Y=255 and get it to appear one pixel higher than the minimum position.

by on (#47578)
/me reiterates that 239 is only "offscreen" in the sense that the sprites are not rendered, but is "onscreen" in the sense that they're detected and their tiles are fetched.

by on (#47586)
Thanks

by on (#47587)
Ok, let's summarize (as I would be six years old):

* scanlines goes from 0 to 239
* in sprite attributes, the first byte is the y coordinate minus 1, so
its value is between 0xff (sprite on top of the screen) and 0xee (only
first line of the sprite is showed, in last scanline, the rest does not appear)
* if the attribute value is 0xef, it does not appear on the screen, however it is counted
when computing the eight sprites per scanline limit
* if the attribute value is 0xf0-0xfe, nothing is done

Right?

by on (#47588)
If the Y attribute is $FF, nothing is done either. It is impossible for a sprite to occupy the top scanline.

by on (#47589)
Maybe Nintendo designed it to not need to wrap to the top just because their NTSC TVs were clipping the topmost scanlines.

by on (#47592)
tepples already pointed out your error, but here it is clarified a bit more:

Quote:
* scanlines goes from 0 to 239
* in sprite attributes, the first byte is the y coordinate minus 1, so
its value is between 0 (sprite on scanline 1) and 0xee (only
first line of the sprite is showed, in last scanline, the rest does not appear)
* if the attribute value is 0xef, it does not appear on the screen, however it is counted when computing the eight sprites per scanline limit
* if the attribute value is 0xf0-0xff, nothing is done

by on (#47595)
- Visible scanlines are 0~239. There are the -1 and 240 scanlines. If you count by taking the VBlank:

- 0~19 is the VBlank period.
- 20 is a dummy scanline, but "works" like all the others, plus sprites are evaluated for the next scanline, among other small things.
- 21~260 is the visible field.
- 261 is the last one (PPU "resting").

Additionally, I would avoid the "attribute value", as "Y position" is more correct, since the only attribute I know is the "attribute color", ORed with the pixel color.

by on (#47618)
Fx3 wrote:
- 20 is a dummy scanline, but "works" like all the others, plus sprites are evaluated for the next scanline, among other small things.

I've read before that sprites are evaluated during the pre-render scanline, but that doesn't make sense since no sprites are ever displayed on the first visible scanline.

If sprite evaluation does indeed start on the pre-render scanline, why on earth didn't Nintendo check for sprites one scanline ahead, so that the "Y - 1" deal didn't exist? It's stupid that we can't place sprites at the top of the screen if the pre-render scanline does in fact evaluate sprites.

by on (#47624)
Fx3 wrote:
- Visible scanlines are 0~239.
[snip]
- 21~260 is the visible field.


This is exactly why I disapprove this numbering system. It's totally confusing. If you say "scanline 20" do you mean the pre-render scanline? Or the 21st rendered scanline? Not to mention this totally screws with PAL scanlines, since the same scanline would be "scanline 70".

IMO the only scanlines that should be numbered are the rendered ones. IE: 0-239. No confusion, no NTSC/PAL issues. Or if you insist on numbering the other two oddball ones:

- scanline -1 = pre-render
- scanlines 0-239 = rendered
- scanline 240 = idle

</unrelated personal opinion and tangent rant>

tokumaru wrote:
If sprite evaluation does indeed start on the pre-render scanline, why on earth didn't Nintendo check for sprites one scanline ahead, so that the "Y - 1" deal didn't exist? It's stupid that we can't place sprites at the top of the screen if the pre-render scanline does in fact evaluate sprites.


Beats me. Hindsight is always 20/20 I suppose.

I'm still straching my head why they tied useless IRQ functionality to the APU Frame sequencer and not to Sprite 0 hit, which would have made infinitly more sense.

by on (#47625)
Disch wrote:
- scanline -1 = pre-render
- scanlines 0-239 = rendered
- scanline 240 = idle

I agree, and I've been using the same system myself.

Quote:
I'm still straching my head why they tied useless IRQ functionality to the APU Frame sequencer and not to Sprite 0 hit

Probably patents. Remember that until the mid-1990s, the concept of a video game itself was patented.

by on (#47627)
Maybe it's so you can use a 2A03 for music without a PPU.

by on (#47640)
Quote:
Additionally, I would avoid the "attribute value", as "Y position" is more correct, since the only attribute I know is the "attribute color", ORed with the pixel color.

You are right.



Gosh! I do not think write a NES emulator was a so confusing task.
Probably I should start my emulation career from my washing machine.

Let's start, program 1 is for wool and soft tissues.. :-)