From ec1b0d5e16f1e85d7760ef74c0aee82e95690bb2 Mon Sep 17 00:00:00 2001 From: Souryo Date: Thu, 26 Jun 2014 16:41:07 -0400 Subject: [PATCH] MMC3 IRQ fixes - All tests pass (Revision A) --- Core/APU.cpp | 10 +++-- Core/MMC3.h | 14 ++++--- Core/PPU.cpp | 105 ++++++++++++++++++++++++++++++--------------------- Core/PPU.h | 10 +++++ 4 files changed, 87 insertions(+), 52 deletions(-) diff --git a/Core/APU.cpp b/Core/APU.cpp index 14758014..bac0c678 100644 --- a/Core/APU.cpp +++ b/Core/APU.cpp @@ -44,7 +44,7 @@ uint8_t APU::ReadRAM(uint16_t addr) switch(addr) { case 0x4015: CPU::ClearIRQSource(IRQSource::FrameCounter); - return _apu.read_status(_currentClock); + return _apu.read_status(_currentClock + 4); } return 0; @@ -52,7 +52,11 @@ uint8_t APU::ReadRAM(uint16_t addr) void APU::WriteRAM(uint16_t addr, uint8_t value) { - _apu.write_register(_currentClock, addr, value); + _apu.write_register(_currentClock + 4, addr, value); + if(addr == 0x4017 && (value & 0x40) == 0x40) { + //Disable frame interrupts + CPU::ClearIRQSource(IRQSource::FrameCounter); + } } bool APU::Exec(uint32_t executedCycles) @@ -63,7 +67,7 @@ bool APU::Exec(uint32_t executedCycles) _apu.end_frame(_currentClock); _buf.end_frame(_currentClock); - _currentClock -= 29780; + _currentClock = 0; if(APU::Instance->_apu.earliest_irq() == Nes_Apu::irq_waiting) { CPU::SetIRQSource(IRQSource::FrameCounter); diff --git a/Core/MMC3.h b/Core/MMC3.h index 24c4d7a9..e632d8b8 100644 --- a/Core/MMC3.h +++ b/Core/MMC3.h @@ -53,7 +53,7 @@ class MMC3 : public BaseMapper _irqReload = false; _irqEnabled = false; _lastCycle = 0xFFFF; - _cyclesDown = 0; + _cyclesDown = 0xFFFF; _wramEnabled = false; _wramWriteProtected = false; @@ -186,7 +186,6 @@ class MMC3 : public BaseMapper case MMC3Registers::RegE000: _irqEnabled = false; - //Remove SetIRQ flag if it was added to cpu? CPU::ClearIRQSource(IRQSource::External); break; @@ -198,26 +197,29 @@ class MMC3 : public BaseMapper virtual void NotifyVRAMAddressChange(uint16_t addr) { - uint16_t cycle = PPU::GetCurrentCycle(); + uint32_t cycle = PPU::GetFrameCycle(); + if((addr & 0x1000) == 0) { if(_cyclesDown == 0) { _cyclesDown = 1; } else { if(_lastCycle > cycle) { - _cyclesDown += (340 - _lastCycle + cycle); + //We changed frames + _cyclesDown += (89342 - _lastCycle) + cycle; } else { - _cyclesDown += (_lastCycle - cycle); + _cyclesDown += (cycle - _lastCycle); } } } else if(addr & 0x1000) { if(_cyclesDown > 8) { - //std::cout << "Cycle: " << PPU::GetCurrentCycle() << " - Cycles down: " << _cyclesDown << " - Going up? " << ((addr & 0x1000) ? "true" : "false") << std::endl; uint32_t count = _irqCounter; if(_irqCounter == 0 || _irqReload) { _irqCounter = _irqReloadValue; } else { _irqCounter--; } + + //MMC3 Revision A behavior if((count > 0 || _irqReload) && _irqCounter == 0 && _irqEnabled) { CPU::SetIRQSource(IRQSource::External); } diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 2a00f25c..50e90a1c 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -60,6 +60,10 @@ void PPU::UpdateVideoRamAddr() { if(_scanline >= 239 || !IsRenderingEnabled()) { _state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1; + + //Trigger memory read when setting the vram address - needed by MMC3 IRQ counter + //"Should be clocked when A12 changes to 1 via $2007 read/write" + _memoryManager->ReadVRAM(_state.VideoRamAddr); } else { //"During rendering (on the pre-render line and the visible lines 0-239, provided either background or sprite rendering is enabled), " //it will update v in an odd way, triggering a coarse X increment and a Y increment simultaneously" @@ -127,6 +131,7 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value) _state.VideoRamAddr = _state.TmpVideoRamAddr; //Trigger memory read when setting the vram address - needed by MMC3 IRQ counter + //"4) Should be clocked when A12 changes to 1 via $2006 write" _memoryManager->ReadVRAM(_state.VideoRamAddr); } else { _state.TmpVideoRamAddr = (_state.TmpVideoRamAddr & ~0xFF00) | ((value & 0x3F) << 8); @@ -291,62 +296,66 @@ uint16_t PPU::GetAttributeAddr() void PPU::LoadTileInfo() { - _previousTile = _currentTile; - _currentTile = _nextTile; + if(_flags.BackgroundEnabled) { + _previousTile = _currentTile; + _currentTile = _nextTile; - uint16_t tileIndex = _memoryManager->ReadVRAM(GetNameTableAddr()); - uint16_t tileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr; - - uint16_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02); - _nextTile.PaletteOffset = ((_memoryManager->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2; - _nextTile.LowByte = _memoryManager->ReadVRAM(tileAddr); - _nextTile.HighByte = _memoryManager->ReadVRAM(tileAddr + 8); + uint16_t tileIndex = _memoryManager->ReadVRAM(GetNameTableAddr()); + uint16_t tileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr; + + uint16_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02); + _nextTile.PaletteOffset = ((_memoryManager->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2; + _nextTile.LowByte = _memoryManager->ReadVRAM(tileAddr); + _nextTile.HighByte = _memoryManager->ReadVRAM(tileAddr + 8); + } } void PPU::LoadSpriteTileInfo(uint8_t spriteIndex) { - uint32_t spriteAddr = spriteIndex * 4; - uint8_t spriteY = _secondarySpriteRAM[spriteAddr]; - uint8_t tileIndex = _secondarySpriteRAM[spriteAddr+1]; - uint8_t attributes = _secondarySpriteRAM[spriteAddr+2]; - uint8_t spriteX = _secondarySpriteRAM[spriteAddr+3]; - bool backgroundPriority = (attributes & 0x20) == 0x20; - bool horizontalMirror = (attributes & 0x40) == 0x40; - bool verticalMirror = (attributes & 0x80) == 0x80; + if(_flags.SpritesEnabled) { + uint32_t spriteAddr = spriteIndex * 4; + uint8_t spriteY = _secondarySpriteRAM[spriteAddr]; + uint8_t tileIndex = _secondarySpriteRAM[spriteAddr + 1]; + uint8_t attributes = _secondarySpriteRAM[spriteAddr + 2]; + uint8_t spriteX = _secondarySpriteRAM[spriteAddr + 3]; + bool backgroundPriority = (attributes & 0x20) == 0x20; + bool horizontalMirror = (attributes & 0x40) == 0x40; + bool verticalMirror = (attributes & 0x80) == 0x80; - uint16_t tileAddr; - uint8_t lineOffset; - if(verticalMirror) { - lineOffset = (_flags.LargeSprites ? 15 : 7) - (_scanline - spriteY); - } else { - lineOffset = _scanline - spriteY; - } + uint16_t tileAddr; + uint8_t lineOffset; + if(verticalMirror) { + lineOffset = (_flags.LargeSprites ? 15 : 7) - (_scanline - spriteY); + } else { + lineOffset = _scanline - spriteY; + } - if(_flags.LargeSprites) { - tileAddr = (((tileIndex & 0x01) ? 0x1000 : 0x0000) | ((tileIndex & ~0x01) << 4)) + (lineOffset >= 8 ? lineOffset + 8 : lineOffset); - } else { - tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset; - } - - if(spriteIndex < _spriteCount && spriteY < 240) { - _spriteX[spriteIndex] = spriteX; - _spriteTiles[spriteIndex].BackgroundPriority = backgroundPriority; - _spriteTiles[spriteIndex].HorizontalMirror = horizontalMirror; - _spriteTiles[spriteIndex].PaletteOffset = (attributes & 0x03) << 2; - _spriteTiles[spriteIndex].LowByte = _memoryManager->ReadVRAM(tileAddr); - _spriteTiles[spriteIndex].HighByte = _memoryManager->ReadVRAM(tileAddr + 8); - } else { - //Fetches to sprite 0xFF for remaining sprites/hidden - used by MMC3 IRQ counter - lineOffset = 0; - tileIndex = 0xFF; if(_flags.LargeSprites) { tileAddr = (((tileIndex & 0x01) ? 0x1000 : 0x0000) | ((tileIndex & ~0x01) << 4)) + (lineOffset >= 8 ? lineOffset + 8 : lineOffset); } else { tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset; } - _memoryManager->ReadVRAM(tileAddr); - _memoryManager->ReadVRAM(tileAddr + 8); + if(spriteIndex < _spriteCount && spriteY < 240) { + _spriteX[spriteIndex] = spriteX; + _spriteTiles[spriteIndex].BackgroundPriority = backgroundPriority; + _spriteTiles[spriteIndex].HorizontalMirror = horizontalMirror; + _spriteTiles[spriteIndex].PaletteOffset = (attributes & 0x03) << 2; + _spriteTiles[spriteIndex].LowByte = _memoryManager->ReadVRAM(tileAddr); + _spriteTiles[spriteIndex].HighByte = _memoryManager->ReadVRAM(tileAddr + 8); + } else { + //Fetches to sprite 0xFF for remaining sprites/hidden - used by MMC3 IRQ counter + lineOffset = 0; + tileIndex = 0xFF; + if(_flags.LargeSprites) { + tileAddr = (((tileIndex & 0x01) ? 0x1000 : 0x0000) | ((tileIndex & ~0x01) << 4)) + (lineOffset >= 8 ? lineOffset + 8 : lineOffset); + } else { + tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset; + } + + _memoryManager->ReadVRAM(tileAddr); + _memoryManager->ReadVRAM(tileAddr + 8); + } } } @@ -468,6 +477,10 @@ void PPU::ProcessPrerenderScanline() _statusFlags.SpriteOverflow = false; _statusFlags.Sprite0Hit = false; _statusFlags.VerticalBlank = false; + } + + if((_cycle - 1) % 8 == 0 && _cycle < 250) { + LoadTileInfo(); } else if(_cycle >= 280 && _cycle <= 304) { if(IsRenderingEnabled()) { //copy vertical scrolling value from t @@ -483,6 +496,12 @@ void PPU::ProcessPrerenderScanline() InitializeShiftRegisters(); } } + + if(_cycle >= 261 && (_cycle - 261) % 8 == 0 && _cycle <= 320) { + //Unused sprite tile fetches, but vital for MMC3 IRQ counter + uint32_t spriteIndex = (_cycle - 261) / 8; + LoadSpriteTileInfo(spriteIndex); + } } void PPU::ProcessVisibleScanline() diff --git a/Core/PPU.h b/Core/PPU.h index 2649ab26..f4ae8c90 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -195,8 +195,18 @@ class PPU : public IMemoryHandler, public Snapshotable return _frameCount; } + static uint32_t GetFrameCycle() + { + return ((PPU::Instance->_scanline + 1) * 341) + PPU::Instance->_cycle; + } + static uint32_t GetCurrentCycle() { return PPU::Instance->_cycle; } + + static uint32_t GetCurrentScanline() + { + return PPU::Instance->_scanline; + } };