diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp index cc65f54d..959412b7 100644 --- a/Core/BaseMapper.cpp +++ b/Core/BaseMapper.cpp @@ -686,6 +686,11 @@ void BaseMapper::SetNametable(uint8_t index, uint8_t nametableIndex) _nametableIndexes[index] = nametableIndex; SetPpuMemoryMapping(0x2000 + index * 0x400, 0x2000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex)); + + //Mirror $2000-$2FFF to $3000-$3FFF, while keeping a distinction between the addresses + //Previously, $3000-$3FFF was being "redirected" to $2000-$2FFF to avoid MMC3 IRQ issues (which is incorrect) + //More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145 + SetPpuMemoryMapping(0x3000 + index * 0x400, 0x3000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex)); } void BaseMapper::SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index) @@ -790,16 +795,6 @@ void BaseMapper::WritePrgRam(uint16_t addr, uint8_t value) } } -void BaseMapper::ProcessVramAccess(uint16_t &addr) -{ - addr &= 0x3FFF; - if(addr >= 0x3000) { - //Need to mirror 0x3000 writes to 0x2000, this appears to be how hardware behaves - //Required for proper MMC3 IRQ timing in Burai Fighter - addr -= 0x1000; - } -} - void BaseMapper::NotifyVRAMAddressChange(uint16_t addr) { //This is called when the VRAM addr on the PPU memory bus changes @@ -818,7 +813,7 @@ uint8_t BaseMapper::InternalReadVRAM(uint16_t addr) uint8_t BaseMapper::DebugReadVRAM(uint16_t addr, bool disableSideEffects) { - ProcessVramAccess(addr); + addr &= 0x3FFF; if(!disableSideEffects) { NotifyVRAMAddressChange(addr); } @@ -832,7 +827,7 @@ uint8_t BaseMapper::MapperReadVRAM(uint16_t addr, MemoryOperationType operationT void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects) { - ProcessVramAccess(addr); + addr &= 0x3FFF; if(disableSideEffects) { if(_chrPages[addr >> 8]) { //Always allow writes when side-effects are disabled @@ -848,9 +843,7 @@ void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEf void BaseMapper::WriteVRAM(uint16_t addr, uint8_t value) { - ProcessVramAccess(addr); Debugger::ProcessVramWriteOperation(addr, value); - NotifyVRAMAddressChange(addr); if(_chrPageAccessType[addr >> 8] & MemoryAccessType::Write) { _chrPages[addr >> 8][(uint8_t)addr] = value; diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index 164ec9b2..9870bee2 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -185,9 +185,6 @@ public: __forceinline uint8_t ReadVRAM(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead) { - ProcessVramAccess(addr); - NotifyVRAMAddressChange(addr); - uint8_t value = MapperReadVRAM(addr, type); Debugger::ProcessVramReadOperation(type, addr, value); return value; @@ -196,7 +193,6 @@ public: void DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects = true); void WriteVRAM(uint16_t addr, uint8_t value); - __forceinline void ProcessVramAccess(uint16_t &addr); uint8_t DebugReadVRAM(uint16_t addr, bool disableSideEffects = true); static void InitializeRam(void* data, uint32_t length); diff --git a/Core/JyCompany.h b/Core/JyCompany.h index abb9a37a..c84d4cc7 100644 --- a/Core/JyCompany.h +++ b/Core/JyCompany.h @@ -228,6 +228,7 @@ protected: for(int i = 0; i < 4; i++) { if(_disableNtRam || (_ntLowRegs[i] & 0x80) != (_ntRamSelectBit & 0x80)) { SetPpuMemoryMapping(0x2000 + 0x400 * i, 0x23FF + 0x400 * i, _ntLowRegs[i] | (_ntHighRegs[i] << 8), ChrMemoryType::ChrRom); + SetPpuMemoryMapping(0x3000 + 0x400 * i, 0x33FF + 0x400 * i, _ntLowRegs[i] | (_ntHighRegs[i] << 8), ChrMemoryType::ChrRom); } else { SetNametable(i, _ntLowRegs[i] & 0x01); } diff --git a/Core/PPU.cpp b/Core/PPU.cpp index e273480b..922e7041 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -60,6 +60,7 @@ void PPU::Reset() _currentTile = {}; _nextTile = {}; + _ppuBusAddress = 0; _intensifyColorBits = 0; _paletteRamMask = 0x3F; _lastUpdatedPixel = -1; @@ -170,11 +171,11 @@ void PPU::SetState(PPUDebugState &state) void PPU::UpdateVideoRamAddr() { if(_scanline >= 240 || !IsRenderingEnabled()) { - _state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1; + _state.VideoRamAddr = (_state.VideoRamAddr + (_flags.VerticalWrite ? 32 : 1)) & 0x7FFF; //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" - _mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read); + SetBusAddress(_state.VideoRamAddr & 0x3FFF); } 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" @@ -268,7 +269,7 @@ uint8_t PPU::ReadRAM(uint16_t addr) openBusMask = 0xFF; } else { returnValue = _memoryReadBuffer; - _memoryReadBuffer = _mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read); + _memoryReadBuffer = ReadVram(_ppuBusAddress & 0x3FFF, MemoryOperationType::Read); if((_state.VideoRamAddr & 0x3FFF) >= 0x3F00 && !EmulationSettings::CheckFlag(EmulationFlags::DisablePaletteRead)) { returnValue = ReadPaletteRAM(_state.VideoRamAddr) | (_openBus & 0xC0); @@ -360,7 +361,7 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value) WritePaletteRAM(_state.VideoRamAddr, value); Debugger::ProcessVramWriteOperation(_state.VideoRamAddr & 0x3FFF, value); } else { - _mapper->WriteVRAM(_state.VideoRamAddr, value); + _mapper->WriteVRAM(_ppuBusAddress & 0x3FFF, value); } UpdateVideoRamAddr(); break; @@ -541,6 +542,24 @@ uint16_t PPU::GetAttributeAddr() return 0x23C0 | (_state.VideoRamAddr & 0x0C00) | ((_state.VideoRamAddr >> 4) & 0x38) | ((_state.VideoRamAddr >> 2) & 0x07); } +void PPU::SetBusAddress(uint16_t addr) +{ + _ppuBusAddress = addr; + _mapper->NotifyVRAMAddressChange(addr); +} + +uint8_t PPU::ReadVram(uint16_t addr, MemoryOperationType type) +{ + SetBusAddress(addr); + return _mapper->ReadVRAM(addr, type); +} + +void PPU::WriteVram(uint16_t addr, uint8_t value) +{ + SetBusAddress(addr); + _mapper->WriteVRAM(addr, value); +} + void PPU::LoadTileInfo() { if(IsRenderingEnabled()) { @@ -552,7 +571,7 @@ void PPU::LoadTileInfo() _state.LowBitShift |= _nextTile.LowByte; _state.HighBitShift |= _nextTile.HighByte; - uint8_t tileIndex = _mapper->ReadVRAM(GetNameTableAddr()); + uint8_t tileIndex = ReadVram(GetNameTableAddr()); _nextTile.TileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr; _nextTile.OffsetY = _state.VideoRamAddr >> 12; break; @@ -560,17 +579,17 @@ void PPU::LoadTileInfo() case 2: { uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02); - _nextTile.PaletteOffset = ((_mapper->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2; + _nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2; break; } case 3: - _nextTile.LowByte = _mapper->ReadVRAM(_nextTile.TileAddr); + _nextTile.LowByte = ReadVram(_nextTile.TileAddr); _nextTile.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(_nextTile.TileAddr); break; case 5: - _nextTile.HighByte = _mapper->ReadVRAM(_nextTile.TileAddr + 8); + _nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8); break; } } @@ -609,8 +628,8 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin info.HighByte = _mapper->DebugReadVRAM(tileAddr + 8); } else { fetchLastSprite = false; - info.LowByte = _mapper->ReadVRAM(tileAddr); - info.HighByte = _mapper->ReadVRAM(tileAddr + 8); + info.LowByte = ReadVram(tileAddr); + info.HighByte = ReadVram(tileAddr + 8); } info.TileAddr = tileAddr; info.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(tileAddr); @@ -635,8 +654,8 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin tileAddr = ((tileIndex << 4) | _flags.SpritePatternAddr) + lineOffset; } - _mapper->ReadVRAM(tileAddr); - _mapper->ReadVRAM(tileAddr + 8); + ReadVram(tileAddr); + ReadVram(tileAddr + 8); } _spriteIndex++; @@ -841,10 +860,10 @@ void PPU::ProcessScanline() LoadSpriteTileInfo(); } else if((_cycle - 257) % 8 == 0) { //Garbage NT sprite fetch (257, 265, 273, etc.) - Required for proper MC-ACC IRQs (MMC3 clone) - _mapper->ReadVRAM(GetNameTableAddr()); + ReadVram(GetNameTableAddr()); } else if((_cycle - 259) % 8 == 0) { //Garbage AT sprite fetch - _mapper->ReadVRAM(GetAttributeAddr()); + ReadVram(GetAttributeAddr()); } if(_scanline == -1 && _cycle >= 280 && _cycle <= 304) { @@ -868,7 +887,7 @@ void PPU::ProcessScanline() } } else if(_cycle == 337 || _cycle == 339) { if(IsRenderingEnabled()) { - _mapper->ReadVRAM(GetNameTableAddr()); + ReadVram(GetNameTableAddr()); if(_scanline == -1 && _nesModel == NesModel::NTSC && _cycle == 339 && (_frameCount & 0x01)) { //This behavior is NTSC-specific - PAL frames are always the same number of cycles @@ -1158,9 +1177,13 @@ void PPU::UpdateState() if(_updateVramAddrDelay == 0) { _state.VideoRamAddr = _updateVramAddr; - //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" - _mapper->ReadVRAM(_state.VideoRamAddr, MemoryOperationType::Read); + if(_scanline >= 240 || !IsRenderingEnabled()) { + //Only set the VRAM address on the bus if the PPU is rendering + //More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145 + //Trigger bus address change when setting the vram address - needed by MMC3 IRQ counter + //"4) Should be clocked when A12 changes to 1 via $2006 write" + SetBusAddress(_state.VideoRamAddr & 0x3FFF); + } } else { _needStateUpdate = true; } @@ -1228,7 +1251,8 @@ 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, unusedSkipScrollIncrement, paletteRam, spriteRam, secondarySpriteRam, - openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, _needStateUpdate); + openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, + _needStateUpdate, _ppuBusAddress); 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); diff --git a/Core/PPU.h b/Core/PPU.h index 14d923b1..f3f58ba3 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -63,6 +63,7 @@ class PPU : public IMemoryHandler, public Snapshotable SpriteInfo *_lastSprite; //used by HD ppu + uint16_t _ppuBusAddress; TileInfo _currentTile; TileInfo _nextTile; TileInfo _previousTile; @@ -156,6 +157,10 @@ class PPU : public IMemoryHandler, public Snapshotable } } + __forceinline void SetBusAddress(uint16_t addr); + __forceinline uint8_t ReadVram(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead); + __forceinline void WriteVram(uint16_t addr, uint8_t value); + void StreamState(bool saving) override; public: diff --git a/Core/VRC6.h b/Core/VRC6.h index be95510f..e0cd05d9 100644 --- a/Core/VRC6.h +++ b/Core/VRC6.h @@ -54,6 +54,12 @@ protected: _audio.Clock(); } + void SetPpuMapping(uint8_t bank, uint8_t page) + { + SetPpuMemoryMapping(0x2000 + bank * 0x400, 0x23FF + bank * 0x400, page); + SetPpuMemoryMapping(0x3000 + bank * 0x400, 0x33FF + bank * 0x400, page); + } + void UpdatePpuBanking() { uint8_t mask = (_bankingMode & 0x20) ? 0xFE : 0xFF; @@ -99,34 +105,34 @@ protected: switch(_bankingMode & 0x2F) { case 0x20: case 0x27: - SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6] & 0xFE); - SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[6] & 0xFE) | 1); - SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7] & 0xFE); - SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1); + SetPpuMapping(0, _chrRegisters[6] & 0xFE); + SetPpuMapping(1, (_chrRegisters[6] & 0xFE) | 1); + SetPpuMapping(2, _chrRegisters[7] & 0xFE); + SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1); break; case 0x23: case 0x24: - SetPpuMemoryMapping(0x2000, 0x23FF, (_chrRegisters[6] & 0xFE)); - SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[7] & 0xFE)); - SetPpuMemoryMapping(0x2800, 0x2BFF, (_chrRegisters[6] & 0xFE) | 1); - SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1); + SetPpuMapping(0, (_chrRegisters[6] & 0xFE)); + SetPpuMapping(1, (_chrRegisters[7] & 0xFE)); + SetPpuMapping(2, (_chrRegisters[6] & 0xFE) | 1); + SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1); break; case 0x28: case 0x2F: - SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6] & 0xFE); - SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[6] & 0xFE); - SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7] & 0xFE); - SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7] & 0xFE); + SetPpuMapping(0, _chrRegisters[6] & 0xFE); + SetPpuMapping(1, _chrRegisters[6] & 0xFE); + SetPpuMapping(2, _chrRegisters[7] & 0xFE); + SetPpuMapping(3, _chrRegisters[7] & 0xFE); break; case 0x2B: case 0x2C: - SetPpuMemoryMapping(0x2000, 0x23FF, (_chrRegisters[6] & 0xFE) | 1); - SetPpuMemoryMapping(0x2400, 0x27FF, (_chrRegisters[7] & 0xFE) | 1); - SetPpuMemoryMapping(0x2800, 0x2BFF, (_chrRegisters[6] & 0xFE) | 1); - SetPpuMemoryMapping(0x2C00, 0x2FFF, (_chrRegisters[7] & 0xFE) | 1); + SetPpuMapping(0, (_chrRegisters[6] & 0xFE) | 1); + SetPpuMapping(1, (_chrRegisters[7] & 0xFE) | 1); + SetPpuMapping(2, (_chrRegisters[6] & 0xFE) | 1); + SetPpuMapping(3, (_chrRegisters[7] & 0xFE) | 1); break; default: @@ -134,27 +140,27 @@ protected: case 0: case 6: case 7: - SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6]); - SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[6]); - SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[7]); - SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]); + SetPpuMapping(0, _chrRegisters[6]); + SetPpuMapping(1, _chrRegisters[6]); + SetPpuMapping(2, _chrRegisters[7]); + SetPpuMapping(3, _chrRegisters[7]); break; case 1: case 5: - SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[4]); - SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[5]); - SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[6]); - SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]); + SetPpuMapping(0, _chrRegisters[4]); + SetPpuMapping(1, _chrRegisters[5]); + SetPpuMapping(2, _chrRegisters[6]); + SetPpuMapping(3, _chrRegisters[7]); break; case 2: case 3: case 4: - SetPpuMemoryMapping(0x2000, 0x23FF, _chrRegisters[6]); - SetPpuMemoryMapping(0x2400, 0x27FF, _chrRegisters[7]); - SetPpuMemoryMapping(0x2800, 0x2BFF, _chrRegisters[6]); - SetPpuMemoryMapping(0x2C00, 0x2FFF, _chrRegisters[7]); + SetPpuMapping(0, _chrRegisters[6]); + SetPpuMapping(1, _chrRegisters[7]); + SetPpuMapping(2, _chrRegisters[6]); + SetPpuMapping(3, _chrRegisters[7]); break; } break;