MMC3 IRQ fixes - All tests pass (Revision A)

This commit is contained in:
Souryo 2014-06-26 16:41:07 -04:00
parent d9fd9c4736
commit ec1b0d5e16
4 changed files with 87 additions and 52 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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()

View File

@ -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;
}
};