This is a continuation of a previous thread where efficient implementation of the "more than 8 sprites on a scanline encountered" flag, bit 5 of $2002 when read. Here's an elaboration of the idea I sketched out. Basically you keep a cache of how many sprites are on each scanline, and recalculate it whenever you access it after it might have become invalid. I'm going to try this in my emulator and report on the performance impact (as compared to not implementing the flag at all).
Code:
int scanlines [256 + 16]; // extra lines allow elimination of range checking
bool scanlines_valid;
void calc_scanlines()
{
scanlines_valid = true;
memset( scanlines, 0, sizeof scanlines ); // clear counts
for ( int i = 0; i < 64; i++ )
{
int top = sprite_ram [i * 4] + 1;
for ( int line = top; line < height; line++ )
scanlines [top + line]++;
}
}
void write_2000( int data )
{
if ( (w2000 ^ data) & 0x20 )
scanlines_valid = false; // sprite height changed
...
w2000 = data;
}
void write_2001( int data )
{
if ( (w2001 ^ data) & 0x10 )
scanlines_valid = false; // sprite visibility changed
...
w2001 = data;
}
// (same for 0x4014)
void write_2004( int data )
{
if ( (w2003 & 3) == 0 )
scanlines_valid = false; // might have modified vertical position
...
w2003 = data;
}
int read_2002( long timestamp )
{
if ( !r2002 & 0x20 )
{
// max sprites flag not yet set
if ( !scanlines_valid )
calc_scanlines();
if ( scanlines [timestamp / 341] > 8 )
run_ppu_until( time ); // may set max sprites flag
}
...
return r2002;
}
bool scanlines_valid;
void calc_scanlines()
{
scanlines_valid = true;
memset( scanlines, 0, sizeof scanlines ); // clear counts
for ( int i = 0; i < 64; i++ )
{
int top = sprite_ram [i * 4] + 1;
for ( int line = top; line < height; line++ )
scanlines [top + line]++;
}
}
void write_2000( int data )
{
if ( (w2000 ^ data) & 0x20 )
scanlines_valid = false; // sprite height changed
...
w2000 = data;
}
void write_2001( int data )
{
if ( (w2001 ^ data) & 0x10 )
scanlines_valid = false; // sprite visibility changed
...
w2001 = data;
}
// (same for 0x4014)
void write_2004( int data )
{
if ( (w2003 & 3) == 0 )
scanlines_valid = false; // might have modified vertical position
...
w2003 = data;
}
int read_2002( long timestamp )
{
if ( !r2002 & 0x20 )
{
// max sprites flag not yet set
if ( !scanlines_valid )
calc_scanlines();
if ( scanlines [timestamp / 341] > 8 )
run_ppu_until( time ); // may set max sprites flag
}
...
return r2002;
}