PPU: Added option to emulate OAM row corruption when disabling rendering at certain points during rendering

This commit is contained in:
Sour 2020-04-30 18:40:39 -04:00
parent bf6d161e1a
commit 59fddb7008
17 changed files with 1008 additions and 904 deletions

View file

@ -23,6 +23,8 @@ enum EmulationFlags : uint64_t
PauseOnMovieEnd = 0x0100,
EnablePpuOamRowCorruption = 0x0200,
AllowBackgroundInput = 0x0400,
ReduceSoundInBackground = 0x0800,
MuteSoundInBackground = 0x1000,

View file

@ -30,6 +30,9 @@ PPU::PPU(shared_ptr<Console> console)
0x09, 0x01, 0x34, 0x03, 0x00, 0x04, 0x00, 0x14, 0x08, 0x3A, 0x00, 0x02, 0x00, 0x20, 0x2C, 0x08 };
memcpy(_paletteRAM, paletteRamBootValues, sizeof(_paletteRAM));
//This should (presumably) persist across resets
memset(_corruptOamRow, 0, sizeof(_corruptOamRow));
_console->InitializeRam(_spriteRAM, 0x100);
_console->InitializeRam(_secondarySpriteRAM, 0x20);
@ -546,6 +549,11 @@ void PPU::SetMaskRegister(uint8_t value)
if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) {
_needStateUpdate = true;
if(_renderingEnabled && _scanline < 240) {
//Rendering was just disabled by the write
SetOamCorruptionFlags();
}
}
UpdateMinimumDrawCycles();
@ -1222,6 +1230,49 @@ void PPU::DebugUpdateFrameBuffer(bool toGrayscale)
}
}
void PPU::SetOamCorruptionFlags()
{
if(!_settings->CheckFlag(EmulationFlags::EnablePpuOamRowCorruption)) {
return;
}
//Note: Still pending more research, but this currently matches a portion of the issues that have been observed
//When rendering is disabled in some sections of the screen, either:
// A- During Secondary OAM clear (first ~64 cycles)
// B- During OAM tile fetching (cycle ~256 to cycle ~320)
//then OAM memory gets corrupted the next time the PPU starts rendering again (usually at the start of the next frame)
//This usually causes the first "row" of OAM (the first 8 bytes) to get copied over another, causing some sprites to be missing
//and causing an extra set of the first 2 sprites to appear on the screen (not possible to see them except via any overflow they may cause)
if(_cycle >= 1 && _cycle < 65) {
//Every 2 dots causes the corruption to shift down 1 OAM row (8 bytes)
_corruptOamRow[(_cycle - 1) >> 1] = true;
} else if(_cycle >= 257 && _cycle < 321) {
//This section is in 8-dot segments.
//The first 3 dot increment the corrupted row by 1, and then the last 5 dots corrupt the next row for 5 dots.
uint8_t base = (_cycle - 257) >> 3;
uint8_t offset = std::min<uint8_t>(3, (_cycle - 257) & 0x07);
_corruptOamRow[base * 4 + offset] = true;
}
}
void PPU::ProcessOamCorruption()
{
if(!_settings->CheckFlag(EmulationFlags::EnablePpuOamRowCorruption)) {
return;
}
//Copy first OAM row over another row, as needed by corruption flags (can be over itself, which causes no actual harm)
for(int i = 0; i < 32; i++) {
if(_corruptOamRow[i]) {
if(i > 0) {
memcpy(_spriteRAM + i * 8, _spriteRAM, 8);
}
_corruptOamRow[i] = false;
}
}
}
void PPU::Exec()
{
if(_cycle > 339) {
@ -1233,6 +1284,10 @@ void PPU::Exec()
//Force prerender scanline sprite fetches to load the dummy $FF tiles (fixes shaking in Ninja Gaiden 3 stage 1 after beating boss)
_spriteCount = 0;
if(_renderingEnabled) {
ProcessOamCorruption();
}
UpdateMinimumDrawCycles();
}
@ -1297,6 +1352,9 @@ void PPU::UpdateState()
_prevRenderingEnabled = _renderingEnabled;
if(_renderingEnabled != (_flags.BackgroundEnabled | _flags.SpritesEnabled)) {
_renderingEnabled = _flags.BackgroundEnabled | _flags.SpritesEnabled;
if(_renderingEnabled) {
ProcessOamCorruption();
}
}
if(_prevRenderingEnabled != _renderingEnabled) {
_needStateUpdate = true;
@ -1436,6 +1494,8 @@ void PPU::StreamState(bool saving)
_oamDecayCycles[i] = _console->GetCpu()->GetCycleCount();
}
memset(_corruptOamRow, 0, sizeof(_corruptOamRow));
for(int i = 0; i < 257; i++) {
_hasSprite[i] = true;
}

View file

@ -105,6 +105,7 @@ class PPU : public IMemoryHandler, public Snapshotable
uint64_t _oamDecayCycles[0x40];
bool _enableOamDecay;
bool _corruptOamRow[32];
void UpdateStatusFlag();
@ -140,6 +141,9 @@ class PPU : public IMemoryHandler, public Snapshotable
__forceinline uint8_t ReadSpriteRam(uint8_t addr);
__forceinline void WriteSpriteRam(uint8_t addr, uint8_t value);
void SetOamCorruptionFlags();
void ProcessOamCorruption();
void UpdateMinimumDrawCycles();

View file

@ -25,6 +25,7 @@ namespace Mesen.GUI.Config
public bool RandomizeCpuPpuAlignment = false;
public bool EnablePpu2006ScrollGlitch = false;
public bool EnablePpu2000ScrollGlitch = false;
public bool EnablePpuOamRowCorruption = false;
public bool UseAlternativeMmc3Irq = false;
@ -65,6 +66,7 @@ namespace Mesen.GUI.Config
InteropEmu.SetFlag(EmulationFlags.RandomizeCpuPpuAlignment, emulationInfo.RandomizeCpuPpuAlignment);
InteropEmu.SetFlag(EmulationFlags.EnablePpu2000ScrollGlitch, emulationInfo.EnablePpu2000ScrollGlitch);
InteropEmu.SetFlag(EmulationFlags.EnablePpu2006ScrollGlitch, emulationInfo.EnablePpu2006ScrollGlitch);
InteropEmu.SetFlag(EmulationFlags.EnablePpuOamRowCorruption, emulationInfo.EnablePpuOamRowCorruption);
InteropEmu.SetPpuNmiConfig(emulationInfo.PpuExtraScanlinesBeforeNmi, emulationInfo.PpuExtraScanlinesAfterNmi);

View file

@ -328,6 +328,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on/reset CPU/PPU alignment</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Enable PPU $2006 scroll glitch emulation</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Enable PPU $2000/$2005/$2006 first-write scroll glitch emulation</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="tpgOverclocking">Forçament</Control>
<Control ID="grpOverclocking">Forçament de CPU</Control>

View file

@ -317,7 +317,7 @@
<Control ID="lblMiscSettings">Miscellaneous Settings</Control>
<Control ID="chkUseAlternativeMmc3Irq">Use alternative MMC3 IRQ behavior</Control>
<Control ID="chkAllowInvalidInput">Allow invalid input (e.g Down + Up or Left + Right at the same time)</Control>
<Control ID="chkEnableOamDecay">Enable OAM RAM decay</Control>
<Control ID="chkEnableOamDecay">Enable PPU OAM decay</Control>
<Control ID="chkDisablePpu2004Reads">Disable PPU $2004 reads (Famicom behavior)</Control>
<Control ID="chkDisableOamAddrBug">Disable PPU OAMADDR bug emulation</Control>
<Control ID="chkDisablePaletteRead">Disable PPU palette reads</Control>
@ -327,7 +327,8 @@
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on/reset CPU/PPU alignment</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Enable PPU $2006 scroll glitch emulation</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Enable PPU $2000/$2005/$2006 first-write scroll glitch emulation</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="lblRamPowerOnState">Default power on state for RAM:</Control>
<Control ID="tpgOverclocking">Overclocking</Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Aleatorizar el encendido/reinicio de la alineación CPU/PPU</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Habilitar el fallo de emulación de scroll PPU $2006</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Habilitar el fallo de emulación de scroll de primera escritura PPU $2000/$2005/$2006</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="tpgOverclocking">Overclocking</Control>
<Control ID="grpOverclocking">Overclocking de CPU</Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Démarrer le jeu avec un alignement CPU/PPU aléatoire</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Simuler le bug de scrolling lors de l'écriture à $2006</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Simuler le bug de scrolling lors de l'écriture à $2000/$2005/$2006</Control>
<Control ID="chkEnablePpuOamRowCorruption">Simuler le bug de corruption de la rangée OAM</Control>
<Control ID="lblRamPowerOnState">État initial de la mémoire au démarrage : </Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on/reset CPU/PPU alignment</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Enable PPU $2006 scroll glitch emulation</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Enable PPU $2000/$2005/$2006 first-write scroll glitch emulation</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="lblRamPowerOnState">Stato di accensione predefinito per la RAM:</Control>

View file

@ -326,6 +326,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">ランダムなCPU/PPUアラインメントでゲームを起動する</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">$2006に書き込む時に発生するスクロールバグを再現する</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">$2000・$2005・$2006に書き込む時に発生するスクロールバグを再現する</Control>
<Control ID="chkEnablePpuOamRowCorruption">OAM行のデータの汚染を再現する</Control>
<Control ID="lblRamPowerOnState">起動時のメモリの状態 : </Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Aleatorizar o ligar/reiniciar do alinhamento da CPU/PPU</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Ativar emulação do erro gráfico PPU $2006 na rolagem</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Ativar emulação do erro gráfico PPU $2000/$2005/$2006 na rolagem na primeira escrita</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="lblRamPowerOnState">Estado inicial da RAM durante o início:</Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Рандомизировать включение/перезагрузку выравнивания CPU/PPU</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Включить PPU $2006 scroll glitch emulation</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Включить PPU $2000/$2005/$2006 first-write scroll glitch emulation</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="tpgOverclocking">Разгон</Control>
<Control ID="grpOverclocking">Разгон CPU</Control>

View file

@ -327,6 +327,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">Randomize power-on/reset CPU/PPU alignment</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">Enable PPU $2006 scroll glitch emulation</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">Enable PPU $2000/$2005/$2006 first-write scroll glitch emulation</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="tpgOverclocking">Розгін</Control>
<Control ID="grpOverclocking">Розгін CPU</Control>

View file

@ -353,6 +353,7 @@
<Control ID="chkRandomizeCpuPpuAlignment">随机 CPU/PPU 开机对齐</Control>
<Control ID="chkEnablePpu2006ScrollGlitch">模拟 PPU $2006 卷轴故障</Control>
<Control ID="chkEnablePpu2000ScrollGlitch">模拟首次写入 PPU $2000/$2005/$2006 卷轴故障</Control>
<Control ID="chkEnablePpuOamRowCorruption">Enable PPU OAM row corruption emulation</Control>
<Control ID="lblRamPowerOnState">默认开机 RAM 状态:</Control>
<Control ID="tpgOverclocking">超频</Control>

File diff suppressed because it is too large Load diff

View file

@ -42,6 +42,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding("RandomizeCpuPpuAlignment", chkRandomizeCpuPpuAlignment);
AddBinding("EnablePpu2006ScrollGlitch", chkEnablePpu2006ScrollGlitch);
AddBinding("EnablePpu2000ScrollGlitch", chkEnablePpu2000ScrollGlitch);
AddBinding("EnablePpuOamRowCorruption", chkEnablePpuOamRowCorruption);
AddBinding("PpuExtraScanlinesBeforeNmi", nudExtraScanlinesBeforeNmi);
AddBinding("PpuExtraScanlinesAfterNmi", nudExtraScanlinesAfterNmi);

View file

@ -1619,6 +1619,8 @@ namespace Mesen.GUI
PauseOnMovieEnd = 0x0100,
EnablePpuOamRowCorruption = 0x0200,
AllowBackgroundInput = 0x0400,
ReduceSoundInBackground = 0x0800,
MuteSoundInBackground = 0x1000,