PPU: Fixed overflow bug emulation & $2004 read behavior (fixed read2004 test)

This commit is contained in:
Souryo 2017-03-10 21:29:48 -05:00
parent 7bfe260c27
commit bed6976570
4 changed files with 46 additions and 12 deletions

View file

@ -45,9 +45,7 @@ public:
{
_nextIrqCycle = 29828;
//"After reset or power-up, APU acts as if $4017 were written with $00 from 9 to 12 clocks before first instruction begins."
//Because of the 3-4 sequence reset delay, 9-12 clocks turns into 6-7
_previousCycle = 6;
_previousCycle = 0;
//"After reset: APU mode in $4017 was unchanged", so we need to keep whatever value _stepMode has for soft resets
if(!softReset) {
@ -55,11 +53,16 @@ public:
}
_currentStep = 0;
//"After reset or power-up, APU acts as if $4017 were written with $00 from 9 to 12 clocks before first instruction begins."
//This is emulated in the CPU::Reset function
//Reset acts as if $00 was written to $4017
_newValue = 0;
_writeDelayCounter = 3;
_inhibitIRQ = false;
_blockFrameCounterTick = 0;
_newValue = -1;
_writeDelayCounter = -1;
}
void StreamState(bool saving) override

View file

@ -103,6 +103,14 @@ void CPU::Reset(bool softReset)
_runIrq = false;
}
//The CPU takes some cycles before starting its execution after a reset/power up
for(int i = 0; i < 12; i++) {
PPU::ExecStatic();
}
for(int i = 0; i < 10; i++) {
APU::ExecStatic();
}
}
void CPU::Exec()

View file

@ -86,6 +86,8 @@ void PPU::Reset()
_frameCount = 1;
_memoryReadBuffer = 0;
_overflowBugCounter = 0;
UpdateMinimumDrawCycles();
}
@ -213,14 +215,16 @@ uint8_t PPU::ReadRAM(uint16_t addr)
case PPURegisters::SpriteData:
if(!EmulationSettings::CheckFlag(EmulationFlags::DisablePpu2004Reads)) {
if(_scanline <= 239 && IsRenderingEnabled() && (_cycle >= 257 || _cycle <= 64)) {
if(_scanline <= 239 && IsRenderingEnabled()) {
//While the screen is begin drawn
if(_cycle >= 257 && _cycle <= 320) {
//Set OAM copy buffer to its proper value. This is done here for performance.
//If we're doing sprite rendering, set OAM copy buffer to its proper value. This is done here for performance.
//It's faster to only do this here when it's needed, rather than splitting LoadSpriteTileInfo() into an 8-step process
uint8_t step = ((_cycle - 257) % 8) > 3 ? 3 : ((_cycle - 257) % 8);
_secondaryOAMAddr = (_cycle - 257) / 8 * 4 + step;
_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr];
}
//Return the value that PPU is currently using for sprite evaluation/rendering
returnValue = _oamCopybuffer;
} else {
returnValue = _spriteRAM[_state.SpriteRamAddr];
@ -813,6 +817,7 @@ void PPU::CopyOAMData()
_spriteInRange = false;
_secondaryOAMAddr = 0;
_overflowSpriteAddr = 0;
_overflowBugCounter = 0;
_oamCopyDone = false;
_spriteAddrH = (_state.SpriteRamAddr >> 2) & 0x3F;
@ -828,6 +833,10 @@ void PPU::CopyOAMData()
} else {
if(_oamCopyDone) {
_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
if(_secondaryOAMAddr >= 0x20) {
//"As seen above, a side effect of the OAM write disable signal is to turn writes to the secondary OAM into reads from it."
_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr & 0x1F];
}
} else {
if(!_spriteInRange && _scanline >= _oamCopybuffer && _scanline < _oamCopybuffer + (_flags.LargeSprites ? 16 : 8)) {
_spriteInRange = true;
@ -862,6 +871,9 @@ void PPU::CopyOAMData()
}
}
} else {
//"As seen above, a side effect of the OAM write disable signal is to turn writes to the secondary OAM into reads from it."
_oamCopybuffer = _secondarySpriteRAM[_secondaryOAMAddr & 0x1F];
//8 sprites have been found, check next sprite for overflow + emulate PPU bug
if(_overflowSpriteAddr == 0) {
//Used to remove sprite limit
@ -871,13 +883,22 @@ void PPU::CopyOAMData()
if(_spriteInRange) {
//Sprite is visible, consider this to be an overflow
_statusFlags.SpriteOverflow = true;
_spriteAddrL = (_spriteAddrL + 1) & 0x03;
_spriteAddrL = (_spriteAddrL + 1);
if(_spriteAddrL == 4) {
_spriteInRange = false;
_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
_oamCopyDone = true;
_spriteAddrL = 0;
}
if(_overflowBugCounter == 0) {
_overflowBugCounter = 3;
} else if(_overflowBugCounter > 0) {
_overflowBugCounter--;
if(_overflowBugCounter == 0) {
//"After it finishes "fetching" this sprite(and setting the overflow flag), it realigns back at the beginning of this line and then continues here on the next sprite"
_oamCopyDone = true;
_spriteAddrL = 0;
}
}
} else {
//Sprite isn't on this scanline, trigger sprite evaluation bug - increment both H & L at the same time
_spriteAddrH = (_spriteAddrH + 1) & 0x3F;
@ -889,6 +910,7 @@ void PPU::CopyOAMData()
}
}
}
_state.SpriteRamAddr = (_spriteAddrL & 0x03) | (_spriteAddrH << 2);
}
}
}
@ -1032,7 +1054,7 @@ void PPU::StreamState(bool saving)
_nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount,
_secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, unusedSpriteDmaAddr,
unusedSpriteDmaCounter, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, _skipScrollingIncrement, paletteRam, spriteRam, secondarySpriteRam,
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug);
openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter);
for(int i = 0; i < 64; i++) {
Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority);

View file

@ -76,6 +76,7 @@ class PPU : public IMemoryHandler, public Snapshotable
uint8_t _spriteAddrH;
uint8_t _spriteAddrL;
bool _oamCopyDone;
uint8_t _overflowBugCounter;
bool _renderingEnabled;
bool _prevRenderingEnabled;