NES PPU update sprite evaluation

This commit is contained in:
alyosha-tas 2017-03-06 20:27:37 -05:00 committed by GitHub
parent 535bfa4ab4
commit b453745404
2 changed files with 170 additions and 140 deletions

View File

@ -450,6 +450,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{ {
ret = OAM[reg_2003]; ret = OAM[reg_2003];
} }
ppu_open_bus = ret; ppu_open_bus = ret;
ppu_open_bus_decay(1); ppu_open_bus_decay(1);
return ret; return ret;

View File

@ -22,15 +22,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
public short[] xbuf = new short[256 * 240]; public short[] xbuf = new short[256 * 240];
// values here are used in sprite evaluation // values here are used in sprite evaluation
public int spr_true_count;
public bool sprite_eval_write; public bool sprite_eval_write;
public byte read_value; public byte read_value;
public int soam_index; public int soam_index;
public int soam_index_prev; public int soam_index_prev;
public int soam_m_index; public int soam_m_index;
public int oam_index; public int oam_index;
public int read_value_aux; public byte read_value_aux;
public int soam_m_index_aux; public int soam_m_index_aux;
public int oam_index_aux; public int oam_index_aux;
public int soam_index_aux;
public bool is_even_cycle; public bool is_even_cycle;
public bool sprite_zero_in_range = false; public bool sprite_zero_in_range = false;
public bool sprite_zero_go = false; public bool sprite_zero_go = false;
@ -127,11 +129,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
case 6: case 6:
ppu_addr_temp |= 8; ppu_addr_temp |= 8;
bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true); bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true);
runppu(1);
if (reg_2001.PPUON) if (reg_2001.PPUON)
{ {
ppu_was_on = true; ppu_was_on = true;
} }
runppu(1);
break; break;
case 7: case 7:
race_2006 = false; race_2006 = false;
@ -199,6 +202,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ppur.status.sl = sl; ppur.status.sl = sl;
spr_true_count = 0;
soam_index = 0; soam_index = 0;
soam_m_index = 0; soam_m_index = 0;
soam_m_index_aux = 0; soam_m_index_aux = 0;
@ -253,7 +257,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
soam_index++; soam_index++;
} }
if (ppur.status.cycle == 64) if (ppur.status.cycle == 64)
{
soam_index = 0; soam_index = 0;
}
// otherwise, scan through OAM and test if sprites are in range // otherwise, scan through OAM and test if sprites are in range
// if they are, they get copied to the secondary OAM // if they are, they get copied to the secondary OAM
@ -261,70 +267,102 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{ {
if (oam_index == 64) if (oam_index == 64)
{ {
oam_index = 0;
sprite_eval_write = false; sprite_eval_write = false;
} }
if (is_even_cycle && oam_index<64) if (is_even_cycle && oam_index<64)
{ {
if ((oam_index * 4 + soam_m_index) < 256)
read_value = OAM[oam_index * 4 + soam_m_index]; read_value = OAM[oam_index * 4 + soam_m_index];
else
if (oam_index_aux > 63) read_value = OAM[oam_index * 4 + soam_m_index - 256];
oam_index_aux = 63; }
else if (!sprite_eval_write)
read_value_aux = OAM[oam_index_aux * 4 + soam_m_index_aux]; {
// if we don't write sprites anymore, just scan through the oam
read_value = soam[0];
oam_index++;
} }
else if (sprite_eval_write) else if (sprite_eval_write)
{ {
if (soam_index >= 8)
{
// this code mirrors sprite overflow bug behaviour
// see http://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation
if (yp >= read_value && yp < read_value + spriteHeight && reg_2001.PPUON)
{
Reg2002_objoverflow = true;
}
else
{
soam_m_index++;
oam_index++;
if (soam_m_index == 4)
soam_m_index = 0;
}
}
//look for sprites //look for sprites
soam[soam_index * 4] = OAM[oam_index_aux * 4]; if (spr_true_count==0 && soam_index<8)
if (yp >= read_value_aux && yp < read_value_aux + spriteHeight && soam_m_index_aux == 0)
{ {
//a flag gets set if sprite zero is in range soam[soam_index * 4] = read_value;
if (oam_index_aux == 0)
sprite_zero_in_range = true;
soam_m_index_aux++;
}
else if (soam_m_index_aux > 0 && soam_m_index_aux < 4)
{
soam[soam_index * 4 + soam_m_index_aux] = OAM[oam_index_aux * 4 + soam_m_index_aux];
soam_m_index_aux++;
if (soam_m_index_aux == 4)
{
oam_index_aux++;
soam_index++;
soam_m_index_aux = 0;
}
}
else
{
oam_index_aux++;
} }
if (soam_index < 8) if (soam_index < 8)
{ {
soam_m_index = soam_m_index_aux; if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0)
oam_index = oam_index_aux; {
//a flag gets set if sprite zero is in range
if (oam_index == 0)
sprite_zero_in_range = true;
spr_true_count++;
soam_m_index++;
}
else if (spr_true_count > 0 && spr_true_count < 4)
{
soam[soam_index * 4 + soam_m_index] = read_value;
soam_m_index++;
spr_true_count++;
if (spr_true_count == 4)
{
oam_index++;
soam_index++;
if (soam_index == 8)
oam_index_aux = oam_index;
soam_m_index = 0;
spr_true_count = 0;
}
}
else
{
oam_index++;
}
}
else if (soam_index>=8)
{
if (yp >= read_value && yp < read_value + spriteHeight && reg_2001.PPUON)
{
Reg2002_objoverflow = true;
}
if (yp >= read_value && yp < read_value + spriteHeight && spr_true_count == 0)
{
spr_true_count++;
soam_m_index++;
}
else if (spr_true_count > 0 && spr_true_count < 4)
{
soam_m_index++;
spr_true_count++;
if (spr_true_count == 4)
{
oam_index++;
soam_index++;
soam_m_index = 0;
spr_true_count = 0;
}
}
else
{
oam_index++;
if (soam_index==8)
{
soam_m_index++; // glitchy increment
soam_m_index &= 3;
}
}
read_value = soam[0]; //writes change to reads
} }
} }
@ -422,11 +460,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
} }
} //rasterpos in sprite range } //rasterpos in sprite range
} //oamcount loop } //oamcount loop
/*
if (reg_2001.color_disable)
pixelcolor &= 0x30;
xbuf[target] = PaletteAdjustPixel(pixelcolor);
*/
pipeline(pixelcolor, target, xt*32+xp); pipeline(pixelcolor, target, xt*32+xp);
target++; target++;
@ -438,20 +472,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
Read_bgdata(ref bgdata[xt + 2]); Read_bgdata(ref bgdata[xt + 2]);
// normally only 8 sprites are allowed, but with a particular setting we can have more then that // normally only 8 sprites are allowed, but with a particular setting we can have more then that
// this extra bit is here because the actual loop doesn't have enough time to scann all sprites if there are more then 8 // this extra bit takes care of it quickly
soam_index_aux = 8;
if (nes.Settings.AllowMoreThanEightSprites) if (nes.Settings.AllowMoreThanEightSprites)
{ {
while (oam_index_aux < 64) while (oam_index_aux < 64)
{ {
//look for sprites //look for sprites
soam[soam_index * 4] = OAM[oam_index_aux * 4]; soam[soam_index_aux * 4] = OAM[oam_index_aux * 4];
read_value_aux = OAM[oam_index_aux * 4]; read_value_aux = OAM[oam_index_aux * 4];
if (yp >= read_value_aux && yp < read_value_aux + spriteHeight) if (yp >= read_value_aux && yp < read_value_aux + spriteHeight)
{ {
soam[soam_index * 4 + 1] = OAM[oam_index_aux * 4 + 1]; soam[soam_index_aux * 4 + 1] = OAM[oam_index_aux * 4 + 1];
soam[soam_index * 4 + 2] = OAM[oam_index_aux * 4 + 2]; soam[soam_index_aux * 4 + 2] = OAM[oam_index_aux * 4 + 2];
soam[soam_index * 4 + 3] = OAM[oam_index_aux * 4 + 3]; soam[soam_index_aux * 4 + 3] = OAM[oam_index_aux * 4 + 3];
soam_index++; soam_index_aux++;
oam_index_aux++; oam_index_aux++;
} }
else else
@ -461,7 +497,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
} }
} }
soam_index_prev = soam_index; soam_index_prev = soam_index_aux;
if (soam_index_prev > 8 && !nes.Settings.AllowMoreThanEightSprites) if (soam_index_prev > 8 && !nes.Settings.AllowMoreThanEightSprites)
soam_index_prev = 8; soam_index_prev = 8;
@ -470,25 +506,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
spriteHeight = reg_2000.obj_size_16 ? 16 : 8; spriteHeight = reg_2000.obj_size_16 ? 16 : 8;
// if there are less then 8 evaluated sprites, we still process 8 sprites
int bound;
if (soam_index_prev > 8)
{
bound = soam_index_prev;
}
else
{
bound = 8;
}
for (int s = 0; s < 8; s++) for (int s = 0; s < 8; s++)
{ {
//if this is a real sprite sprite, then it is not above the 8 sprite limit.
//this is how we support the no 8 sprite limit feature.
//not that at some point we may need a virtual CALL_PPUREAD which just peeks and doesnt increment any counters
//this could be handy for the debugging tools also
bool realSprite = (s < 8);
bool junksprite = (!reg_2001.PPUON); bool junksprite = (!reg_2001.PPUON);
t_oam[s].oam_y = soam[s * 4]; t_oam[s].oam_y = soam[s * 4];
@ -528,39 +547,51 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{ {
if (sl == 0 && ppur.status.cycle == 304) if (sl == 0 && ppur.status.cycle == 304)
{ {
runppu(1);
read_value = t_oam[s].oam_y; read_value = t_oam[s].oam_y;
if (reg_2001.PPUON) ppur.install_latches();
runppu(1); runppu(1);
if (reg_2001.PPUON) ppur.install_latches();
read_value = t_oam[s].oam_ind; read_value = t_oam[s].oam_ind;
runppu(1);
garbage_todo = 0; garbage_todo = 0;
} }
if ((sl != 0) && ppur.status.cycle == 256) if ((sl != 0) && ppur.status.cycle == 256)
{ {
read_value = t_oam[s].oam_y;
runppu(1); runppu(1);
if (target<=61441 && target > 0 && s==0) if (target<=61441 && target > 0 && s==0)
{ {
pipeline(0, target,256); pipeline(0, target,256);
target++; target++;
} }
read_value = t_oam[s].oam_y;
//at 257: 3d world runner is ugly if we do this at 256 //at 257: 3d world runner is ugly if we do this at 256
if (reg_2001.PPUON) ppur.install_h_latches(); if (reg_2001.PPUON) ppur.install_h_latches();
read_value = t_oam[s].oam_ind;
runppu(1); runppu(1);
if (target <= 61441 && target > 0 && s==0) if (target <= 61441 && target > 0 && s==0)
{ {
pipeline(0, target, 257); // last pipeline call option 1 of 2 pipeline(0, target, 257); // last pipeline call option 1 of 2
} }
read_value = t_oam[s].oam_ind;
garbage_todo = 0; garbage_todo = 0;
} }
} }
if (realSprite)
{
for (int i = 0; i < garbage_todo; i++) for (int i = 0; i < garbage_todo; i++)
{ {
if (i==0)
read_value = t_oam[s].oam_y;
else
read_value = t_oam[s].oam_ind;
runppu(1); runppu(1);
if (i == 0) if (i == 0)
@ -570,7 +601,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
pipeline(0, target,256); pipeline(0, target,256);
target++; target++;
} }
read_value = t_oam[s].oam_y;
} }
else else
{ {
@ -578,49 +608,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{ {
pipeline(0, target, 257); // last pipeline call option 2 of 2 pipeline(0, target, 257); // last pipeline call option 2 of 2
} }
read_value = t_oam[s].oam_ind;
} }
} }
}
ppubus_read(ppur.get_atread(), true, true); //at or nt? ppubus_read(ppur.get_atread(), true, true); //at or nt?
if (realSprite)
{
runppu(1);
read_value = t_oam[s].oam_attr; read_value = t_oam[s].oam_attr;
runppu(1); runppu(1);
read_value = t_oam[s].oam_x;
}
// TODO - fake sprites should not come through ppubus_read but rather peek it read_value = t_oam[s].oam_x;
// (at least, they should not probe it with AddressPPU. maybe the difference between peek and read is not necessary) runppu(1);
// if the PPU is off, we don't put anything on the bus
if (junksprite) if (junksprite)
{
if (realSprite)
{ {
ppubus_read(patternAddress, true, false); ppubus_read(patternAddress, true, false);
ppubus_read(patternAddress, true, false); ppubus_read(patternAddress, true, false);
runppu(kFetchTime * 2); runppu(kFetchTime * 2);
} }
}
else else
{ {
int addr = patternAddress; int addr = patternAddress;
t_oam[s].patterns_0 = ppubus_read(addr, true, true); t_oam[s].patterns_0 = ppubus_read(addr, true, true);
if (realSprite)
{
runppu(kFetchTime);
read_value = t_oam[s].oam_x; read_value = t_oam[s].oam_x;
} runppu(kFetchTime);
addr += 8; addr += 8;
t_oam[s].patterns_1 = ppubus_read(addr, true, true); t_oam[s].patterns_1 = ppubus_read(addr, true, true);
if (realSprite)
{
runppu(kFetchTime);
read_value = t_oam[s].oam_x; read_value = t_oam[s].oam_x;
} runppu(kFetchTime);
// hflip // hflip
if ((t_oam[s].oam_attr & 0x40) == 0) if ((t_oam[s].oam_attr & 0x40) == 0)
@ -629,14 +645,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1]; t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1];
} }
// if the sprites attribute is 0xFF, then this indicates a non-existent sprite
// I think the logic here is that bits 2-4 in OAM are disabled, but soam is initialized with 0xFF
// so the only way a sprite could have an 0xFF attribute is if it is not in the scope of the scanline
if (t_oam[s].oam_attr==0xFF)
{
t_oam[s].patterns_0 = 0;
t_oam[s].patterns_1 = 0;
}
} }
} // sprite pattern fetch loop } // sprite pattern fetch loop
//now do the same for extra sprites, but without any cycles run //now do the same for extra sprites, but without any cycles run
if (bound>8) if (soam_index_aux>8)
{ {
for (int s = 8; s < bound; s++) for (int s = 8; s < soam_index_aux; s++)
{ {
bool junksprite = (!reg_2001.PPUON); bool junksprite = (!reg_2001.PPUON);
@ -668,9 +693,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
//so we just need the line offset for the second pattern //so we just need the line offset for the second pattern
patternAddress += line & 7; patternAddress += line & 7;
//garbage nametable fetches + scroll resets
int garbage_todo = 2;
ppubus_read(ppur.get_ntread(), true, false); ppubus_read(ppur.get_ntread(), true, false);
ppubus_read(ppur.get_atread(), true, false); //at or nt? ppubus_read(ppur.get_atread(), true, false); //at or nt?
@ -688,6 +710,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1]; t_oam[s].patterns_1 = BitReverse.Byte8[t_oam[s].patterns_1];
} }
// if the sprites attribute is 0xFF, then this indicates a non-existent sprite
// I think the logic here is that bits 2-4 in OAM are disabled, but soam is initialized with 0xFF
// so the only way a sprite could have an 0xFF attribute is if it is not in the scope of the scanline
if (t_oam[s].oam_attr == 0xFF)
{
t_oam[s].patterns_0 = 0;
t_oam[s].patterns_1 = 0;
}
} // sprite pattern fetch loop } // sprite pattern fetch loop
} }
@ -700,7 +731,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
Read_bgdata(ref bgdata[xt]); Read_bgdata(ref bgdata[xt]);
} }
// this sequence is tuned to pass 10-even_odd_timing.nes // this sequence is tuned to pass 10-even_odd_timing.nes
runppu(kFetchTime); runppu(kFetchTime);
bool evenOddDestiny = (reg_2001.show_bg || reg_2001.show_obj); bool evenOddDestiny = (reg_2001.show_bg || reg_2001.show_obj);
@ -725,7 +755,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
} //FrameAdvance } //FrameAdvance
void FrameAdvance_ppudead() void FrameAdvance_ppudead()
{ {
//not quite emulating all the NES power up behavior //not quite emulating all the NES power up behavior