I don't understand something. In the second level of Battletoads, when I move the main character to the bottom of the screen, it becomes "immortal" because enemies always attack below the main character, despite his location.
I've found, the problem is the time of rising the sprite overflow flag. When the flag is set true immediately, it fixes Battletoads and breaks the third test of ppu_sprite_overflow (timing). When the setting of the flag is delayed by 1 PPU cycle, it breaks Battletoads and fixes ppu_sprite_overflow.
Code:
switch (clks & 1)
{
case 1: oam_tmp = oam[oam_addr]; break;
case 0:
{
const bool in_range = (scanline - oam_tmp < (ctrl & CTRL_MASK_SPRITE_SIZE ? 16 : 8));
if (clks == 66) s0_next_scanline = in_range;
if (!scan_oam_addr_overflow && !oam_addr_overflow)
scan_oam[scan_oam_addr] = oam_tmp;
else
oam_tmp = scan_oam[scan_oam_addr];
if (oam_copy > 0)
{
--oam_copy;
if (!(++ oam_addr &= 0xFF)) oam_addr_overflow = true;
if (!(++scan_oam_addr &= 0x1F)) scan_oam_addr_overflow = sprite_overflow_detection = true;
}
else if (in_range && !scan_oam_addr_overflow && !oam_addr_overflow)
{
oam_copy = 3;
if (!(++ oam_addr &= 0xFF)) oam_addr_overflow = true;
if (!(++scan_oam_addr &= 0x1F)) scan_oam_addr_overflow = sprite_overflow_detection = true;
}
else if (sprite_overflow_detection)
{
if (in_range && !oam_addr_overflow) {sprite_overflow = true; sprite_overflow_detection = false;}
else
{
const u16 temp = ((oam_addr + 4) & ~3) | ((oam_addr + 1) & 3); oam_addr = temp & 255;
if (temp & 256) oam_addr_overflow = true;
}
}
else
{
const u16 temp = oam_addr + 4; oam_addr = temp & 0xFC;
if (temp & 256) oam_addr_overflow = true;
}
break;
}
}