mirror of
https://github.com/libretro/Mesen.git
synced 2025-01-21 08:14:27 +00:00
MMC3 IRQ fixes - All tests pass (Revision A)
This commit is contained in:
parent
d9fd9c4736
commit
ec1b0d5e16
10
Core/APU.cpp
10
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);
|
||||
|
14
Core/MMC3.h
14
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);
|
||||
}
|
||||
|
105
Core/PPU.cpp
105
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()
|
||||
|
10
Core/PPU.h
10
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;
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user