diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c2462a03c..ac4554d18 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -20,6 +20,10 @@ + + + + @@ -852,6 +856,7 @@ + NotUsing NotUsing diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 7c1781e20..7b615190a 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -2928,6 +2928,18 @@ NES + + NES\Mappers\Homebrew + + + NES\Mappers\Homebrew + + + NES\Mappers\Homebrew + + + NES\Mappers\Homebrew + @@ -3266,6 +3278,9 @@ NES + + NES\Mappers\Homebrew + diff --git a/Core/Debugger/DebugUtilities.h b/Core/Debugger/DebugUtilities.h index 1b42e6434..3a76e3189 100644 --- a/Core/Debugger/DebugUtilities.h +++ b/Core/Debugger/DebugUtilities.h @@ -101,6 +101,7 @@ class DebugUtilities case MemoryType::NesInternalRam: case MemoryType::NesMemory: case MemoryType::NesNametableRam: + case MemoryType::NesMapperRam: case MemoryType::NesPaletteRam: case MemoryType::NesPpuMemory: case MemoryType::NesPrgRom: diff --git a/Core/Debugger/PpuTools.h b/Core/Debugger/PpuTools.h index 0018fb8f0..d78c3d735 100644 --- a/Core/Debugger/PpuTools.h +++ b/Core/Debugger/PpuTools.h @@ -242,12 +242,12 @@ class PpuTools void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, const uint32_t* palette, uint32_t *outBuffer); - virtual DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) = 0; + virtual DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) = 0; virtual FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) = 0; - virtual DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) = 0; + virtual DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) = 0; - virtual DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) = 0; - virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) = 0; + virtual DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) = 0; + virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) = 0; int32_t GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y); void SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color); diff --git a/Core/GBA/Debugger/GbaPpuTools.cpp b/Core/GBA/Debugger/GbaPpuTools.cpp index d8403c9ea..296b20e09 100644 --- a/Core/GBA/Debugger/GbaPpuTools.cpp +++ b/Core/GBA/Debugger/GbaPpuTools.cpp @@ -15,7 +15,7 @@ void GbaPpuTools::SetMemoryAccessData(uint16_t scanline, uint8_t* data) memcpy(_memoryAccess + scanline * 308 * 4, data, 308 * 4); } -DebugTilemapInfo GbaPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo GbaPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { GbaPpuState& state = (GbaPpuState&)baseState; FrameInfo outputSize = GetTilemapSize(options, state); @@ -256,7 +256,7 @@ FrameInfo GbaPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& base return { 0, 0 }; } -DebugTilemapTileInfo GbaPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo GbaPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -397,7 +397,7 @@ void GbaPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& b } } -DebugSpritePreviewInfo GbaPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) +DebugSpritePreviewInfo GbaPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) { DebugSpritePreviewInfo info = {}; info.Height = 256; @@ -586,7 +586,7 @@ void GbaPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview } } -void GbaPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void GbaPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { GbaPpuState& state = (GbaPpuState&)baseState; for(int i = 0; i < 128; i++) { diff --git a/Core/GBA/Debugger/GbaPpuTools.h b/Core/GBA/Debugger/GbaPpuTools.h index 957477d42..a45352c3d 100644 --- a/Core/GBA/Debugger/GbaPpuTools.h +++ b/Core/GBA/Debugger/GbaPpuTools.h @@ -20,12 +20,12 @@ class GbaPpuTools final : public PpuTools void SetMemoryAccessData(uint16_t scanline, uint8_t* data); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/Gameboy/Debugger/GbPpuTools.cpp b/Core/Gameboy/Debugger/GbPpuTools.cpp index b9a6aad62..3dd5282d5 100644 --- a/Core/Gameboy/Debugger/GbPpuTools.cpp +++ b/Core/Gameboy/Debugger/GbPpuTools.cpp @@ -10,7 +10,7 @@ GbPpuTools::GbPpuTools(Debugger* debugger, Emulator *emu) : PpuTools(debugger, e { } -DebugTilemapInfo GbPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo GbPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { GbPpuState& state = (GbPpuState&)baseState; uint32_t offset = options.Layer == 1 ? 0x1C00 : 0x1800; @@ -108,7 +108,7 @@ FrameInfo GbPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& state return { 256, 256 }; } -DebugTilemapTileInfo GbPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo GbPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -158,7 +158,7 @@ DebugTilemapTileInfo GbPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint return result; } -DebugSpritePreviewInfo GbPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) +DebugSpritePreviewInfo GbPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) { DebugSpritePreviewInfo info = {}; info.Height = 256; @@ -241,7 +241,7 @@ void GbPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, } } -void GbPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void GbPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { GbPpuState& state = (GbPpuState&)baseState; for(int i = 0; i < 40; i++) { diff --git a/Core/Gameboy/Debugger/GbPpuTools.h b/Core/Gameboy/Debugger/GbPpuTools.h index bd0596caf..46d1219d5 100644 --- a/Core/Gameboy/Debugger/GbPpuTools.h +++ b/Core/Gameboy/Debugger/GbPpuTools.h @@ -16,12 +16,12 @@ class GbPpuTools final : public PpuTools public: GbPpuTools(Debugger* debugger, Emulator *emu); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/NES/BaseMapper.cpp b/Core/NES/BaseMapper.cpp index d73fe8e50..615132e8c 100644 --- a/Core/NES/BaseMapper.cpp +++ b/Core/NES/BaseMapper.cpp @@ -138,14 +138,24 @@ void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, PrgMe case PrgMemoryType::PrgRom: source = _prgRom; sourceSize = _prgSize; break; case PrgMemoryType::SaveRam: source = _saveRam; sourceSize = _saveRamSize; break; case PrgMemoryType::WorkRam: source = _workRam; sourceSize = _workRamSize; break; + case PrgMemoryType::MapperRam: source = _mapperRam; sourceSize = _mapperRamSize; break; } int firstSlot = startAddr >> 8; int slotCount = (endAddr - startAddr + 1) >> 8; for(int i = 0; i < slotCount; i++) { - _prgMemoryOffset[firstSlot + i] = sourceOffset + i * 0x100; - _prgMemoryType[firstSlot + i] = type; - _prgMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType; + if(sourceSize == 0) { + _prgPages[i] = nullptr; + _prgMemoryAccess[i] = MemoryAccessType::NoAccess; + } else { + while(sourceOffset >= sourceSize) { + sourceOffset -= sourceSize; + } + _prgPages[firstSlot + i] = source + sourceOffset; + _prgMemoryOffset[firstSlot + i] = sourceOffset + i * 0x100; + _prgMemoryType[firstSlot + i] = type; + _prgMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType; + } } SetCpuMemoryMapping(startAddr, endAddr, source, sourceOffset, sourceSize, accessType); @@ -233,6 +243,9 @@ void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint1 pageCount = _nametableCount; defaultAccessType |= MemoryAccessType::Write; break; + + default: + throw new std::runtime_error("Invalid parameter"); } if(pageCount == 0) { @@ -285,14 +298,27 @@ void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, ChrMe sourceMemory = _nametableRam; sourceSize = _ntRamSize; break; + + case ChrMemoryType::MapperRam: + sourceMemory = _mapperRam; + sourceSize = _mapperRamSize; + break; } int firstSlot = startAddr >> 8; int slotCount = (endAddr - startAddr + 1) >> 8; for(int i = 0; i < slotCount; i++) { - _chrMemoryOffset[firstSlot + i] = sourceOffset + i * 256; - _chrMemoryType[firstSlot + i] = type; - _chrMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType; + if(sourceSize == 0) { + _chrPages[i] = nullptr; + _chrMemoryAccess[i] = MemoryAccessType::NoAccess; + } else { + while(sourceOffset >= sourceSize) { + sourceOffset -= sourceSize; + } + _chrMemoryOffset[firstSlot + i] = sourceOffset + i * 256; + _chrMemoryType[firstSlot + i] = type; + _chrMemoryAccess[firstSlot + i] = (MemoryAccessType)accessType; + } } SetPpuMemoryMapping(startAddr, endAddr, sourceMemory, sourceOffset, sourceSize, accessType); @@ -534,6 +560,7 @@ void BaseMapper::Serialize(Serializer& s) SVArray(_chrRam, _chrRamSize); SVArray(_workRam, _workRamSize); SVArray(_saveRam, _saveRamSize); + SVArray(_mapperRam, _mapperRamSize); SVArray(_nametableRam, _ntRamSize); SVArray(_prgMemoryOffset, 0x100); @@ -647,8 +674,13 @@ void BaseMapper::Initialize(NesConsole* console, RomData& romData) _workRam = new uint8_t[_workRamSize]; _emu->RegisterMemory(MemoryType::NesWorkRam, _workRam, _workRamSize); + _mapperRamSize = GetMapperRamSize(); + _mapperRam = new uint8_t[_mapperRamSize]; + _emu->RegisterMemory(MemoryType::NesMapperRam, _mapperRam, _mapperRamSize); + _console->InitializeRam(_saveRam, _saveRamSize); _console->InitializeRam(_workRam, _workRamSize); + _console->InitializeRam(_mapperRam, _mapperRamSize); _nametableCount = GetNametableCount(); if(_nametableCount == 0) { @@ -728,6 +760,7 @@ BaseMapper::~BaseMapper() delete[] _prgRom; delete[] _saveRam; delete[] _workRam; + delete[] _mapperRam; delete[] _nametableRam; } @@ -941,7 +974,16 @@ void BaseMapper::DebugWriteVram(uint16_t addr, uint8_t value, bool disableSideEf void BaseMapper::WriteVram(uint16_t addr, uint8_t value) { _emu->ProcessPpuWrite(addr, value, MemoryType::NesPpuMemory); + MapperWriteVram(addr, value); +} +void BaseMapper::MapperWriteVram(uint16_t addr, uint8_t value) +{ + InternalWriteVram(addr, value); +} + +void BaseMapper::InternalWriteVram(uint16_t addr, uint8_t value) +{ if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Write) { _chrPages[addr >> 8][(uint8_t)addr] = value; } @@ -969,16 +1011,19 @@ AddressInfo BaseMapper::GetAbsoluteAddress(uint16_t relativeAddr) info.Address = relativeAddr & _internalRamMask; info.Type = MemoryType::NesInternalRam; } else { - uint8_t *prgAddr = _prgPages[relativeAddr >> 8] + (uint8_t)relativeAddr; - if(prgAddr >= _prgRom && prgAddr < _prgRom + _prgSize) { - info.Address = (uint32_t)(prgAddr - _prgRom); + uint8_t *addr = _prgPages[relativeAddr >> 8] + (uint8_t)relativeAddr; + if(addr >= _prgRom && addr < _prgRom + _prgSize) { + info.Address = (uint32_t)(addr - _prgRom); info.Type = MemoryType::NesPrgRom; - } else if(prgAddr >= _workRam && prgAddr < _workRam + _workRamSize) { - info.Address = (uint32_t)(prgAddr - _workRam); + } else if(addr >= _workRam && addr < _workRam + _workRamSize) { + info.Address = (uint32_t)(addr - _workRam); info.Type = MemoryType::NesWorkRam; - } else if(prgAddr >= _saveRam && prgAddr < _saveRam + _saveRamSize) { - info.Address = (uint32_t)(prgAddr - _saveRam); + } else if(addr >= _saveRam && addr < _saveRam + _saveRamSize) { + info.Address = (uint32_t)(addr - _saveRam); info.Type = MemoryType::NesSaveRam; + } else if(addr >= _mapperRam && addr < _mapperRam + _mapperRamSize) { + info.Address = (uint32_t)(addr - _mapperRam); + info.Type = MemoryType::NesMapperRam; } else { info.Address = -1; info.Type = MemoryType::None; @@ -1000,6 +1045,9 @@ void BaseMapper::GetPpuAbsoluteAddress(uint16_t relativeAddr, AddressInfo& info) } else if(addr >= _chrRam && addr < _chrRam + _chrRamSize) { info.Address = (uint32_t)(addr - _chrRam); info.Type = MemoryType::NesChrRam; + } else if(addr >= _mapperRam && addr < _mapperRam + _mapperRamSize) { + info.Address = (uint32_t)(addr - _mapperRam); + info.Type = MemoryType::NesMapperRam; } else if(addr >= _nametableRam && addr < _nametableRam + _nametableCount * BaseMapper::NametableSize) { info.Address = (uint32_t)(addr - _nametableRam); info.Type = MemoryType::NesNametableRam; @@ -1025,6 +1073,7 @@ AddressInfo BaseMapper::GetRelativeAddress(AddressInfo& addr) case MemoryType::NesPrgRom: ptrAddress = _prgRom; break; case MemoryType::NesWorkRam: ptrAddress = _workRam; break; case MemoryType::NesSaveRam: ptrAddress = _saveRam; break; + case MemoryType::NesMapperRam: ptrAddress = _mapperRam; break; case MemoryType::NesInternalRam: return { (int32_t)(addr.Address & _internalRamMask), MemoryType::NesMemory }; default: return { GetPpuRelativeAddress(addr), MemoryType::NesPpuMemory }; } @@ -1049,6 +1098,7 @@ int32_t BaseMapper::GetPpuRelativeAddress(AddressInfo& addr) case MemoryType::NesChrRom: ptrAddress = _chrRom; break; case MemoryType::NesChrRam: ptrAddress = _chrRam; break; case MemoryType::NesNametableRam: ptrAddress = _nametableRam; break; + case MemoryType::NesMapperRam: ptrAddress = _mapperRam; break; case MemoryType::NesPaletteRam: return 0x3F00 | (addr.Address & 0x1F); break; default: return -1; } @@ -1106,6 +1156,7 @@ CartridgeState BaseMapper::GetState() state.SaveRamPageSize = GetSaveRamPageSize(); vector entries = GetMapperStateEntries(); + assert(entries.size() <= 200); state.CustomEntryCount = (uint32_t)entries.size(); for(int i = 0; i < entries.size(); i++) { state.CustomEntries[i] = entries[i]; diff --git a/Core/NES/BaseMapper.h b/Core/NES/BaseMapper.h index afe08e701..6a16ff93a 100644 --- a/Core/NES/BaseMapper.h +++ b/Core/NES/BaseMapper.h @@ -81,6 +81,9 @@ class BaseMapper : public INesMemoryHandler, public ISerializable bool _hasChrBattery = false; int16_t _vramOpenBusValue = -1; + uint8_t* _mapperRam = nullptr; + uint32_t _mapperRamSize = 0; + virtual void InitMapper() = 0; virtual void InitMapper(RomData &romData); virtual uint16_t GetPrgPageSize() = 0; @@ -104,6 +107,8 @@ class BaseMapper : public INesMemoryHandler, public ISerializable virtual uint32_t GetWorkRamSize() { return 0x2000; } virtual uint32_t GetWorkRamPageSize() { return 0x2000; } + virtual uint32_t GetMapperRamSize() { return 0; } + virtual uint16_t RegisterStartAddress() { return 0x8000; } virtual uint16_t RegisterEndAddress() { return 0xFFFF; } virtual bool AllowRegisterRead() { return false; } @@ -140,7 +145,7 @@ class BaseMapper : public INesMemoryHandler, public ISerializable void RemovePpuMemoryMapping(uint16_t startAddr, uint16_t endAddr); bool HasBattery(); - void LoadBattery(); + virtual void LoadBattery(); string GetBatteryFilename(); uint32_t GetPrgPageCount(); @@ -168,6 +173,8 @@ class BaseMapper : public INesMemoryHandler, public ISerializable void SetMirroringType(MirroringType type); MirroringType GetMirroringType(); + void InternalWriteVram(uint16_t addr, uint8_t value); + __forceinline uint8_t InternalReadVram(uint16_t addr) { if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Read) { @@ -222,7 +229,8 @@ class BaseMapper : public INesMemoryHandler, public ISerializable void WritePrgRam(uint16_t addr, uint8_t value); virtual uint8_t MapperReadVram(uint16_t addr, MemoryOperationType operationType); - + virtual void MapperWriteVram(uint16_t addr, uint8_t value); + __forceinline uint8_t ReadVram(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead) { uint8_t value; diff --git a/Core/NES/Debugger/IExtModeMapperDebug.h b/Core/NES/Debugger/IExtModeMapperDebug.h new file mode 100644 index 000000000..d53339978 --- /dev/null +++ b/Core/NES/Debugger/IExtModeMapperDebug.h @@ -0,0 +1,63 @@ +#pragma once +#include "pch.h" + +struct NtExtConfig +{ + uint16_t SourceOffset; + bool AttrExtMode; + bool BgExtMode; + bool FillMode; +}; + +struct ExtModeConfig +{ + NtExtConfig Nametables[5]; + bool SpriteExtMode; + uint8_t ChrSource; + uint8_t WindowBank; + uint8_t WindowScrollX; + uint8_t WindowScrollY; + uint8_t BgExtBank; + uint8_t SpriteExtBank; + uint8_t SpriteExtData[64]; + uint8_t ExtRam[0x2000]; +}; + +class IExtModeMapperDebug +{ +public: + virtual ExtModeConfig GetExModeConfig() + { + return {}; + } + + virtual bool HasExtendedAttributes(ExtModeConfig& extCfg, uint8_t ntIndex) + { + return false; + } + + virtual bool HasExtendedBackground(ExtModeConfig& extCfg, uint8_t ntIndex) + { + return false; + } + + virtual bool HasExtendedSprites(ExtModeConfig& extCfg) + { + return false; + } + + virtual uint8_t GetExAttributePalette(ExtModeConfig& extCfg, uint8_t ntIndex, uint16_t ntAddr) + { + return 0; + } + + virtual uint8_t GetExBackgroundChrData(ExtModeConfig& extCfg, uint8_t ntIndex, uint16_t ntAddr, uint16_t chrAddr) + { + return 0; + } + + virtual uint8_t GetExSpriteChrData(ExtModeConfig& extCfg, uint8_t spriteIndex, uint16_t chrAddr) + { + return 0; + } +}; diff --git a/Core/NES/Debugger/NesPpuTools.cpp b/Core/NES/Debugger/NesPpuTools.cpp index 81e9249b5..da2ab9058 100644 --- a/Core/NES/Debugger/NesPpuTools.cpp +++ b/Core/NES/Debugger/NesPpuTools.cpp @@ -1,125 +1,136 @@ #include "pch.h" #include "NES/Debugger/NesPpuTools.h" -#include "NES/Mappers/Nintendo/MMC5.h" +#include "NES/Debugger/IExtModeMapperDebug.h" +#include "NES/BaseMapper.h" +#include "NES/BaseNesPpu.h" #include "NES/NesConsole.h" +#include "NES/NesConstants.h" #include "NES/NesTypes.h" #include "NES/NesDefaultVideoFilter.h" +#include "NES/Mappers/Homebrew/Rainbow.h" #include "Debugger/DebugTypes.h" #include "Debugger/MemoryDumper.h" #include "Debugger/MemoryAccessCounter.h" #include "Shared/SettingTypes.h" +static constexpr uint32_t grayscalePalette[4] = { 0xFF000000, 0xFF808080, 0xFFC0C0C0, 0xFFFFFFFF }; + NesPpuTools::NesPpuTools(Debugger* debugger, Emulator *emu, NesConsole* console) : PpuTools(debugger, emu) { _console = console; _mapper = console->GetMapper(); } -DebugTilemapInfo NesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +void NesPpuTools::GetPpuToolsState(BaseState& state) { - MMC5* mmc5 = dynamic_cast(_mapper); - NesPpuState& state = (NesPpuState&)baseState; - uint16_t bgAddr = state.Control.BackgroundPatternAddr; + NesPpuToolsState nesState = {}; + + IExtModeMapperDebug* exMode = dynamic_cast(_mapper); + if(exMode) { + nesState.ExtConfig = exMode->GetExModeConfig(); + } - for(int i = 0; i < 32; i+=4) { - palette[i] = palette[0]; + (NesPpuToolsState&)state = nesState; +} + +void NesPpuTools::DrawNametable(uint8_t* ntSource, uint32_t ntBaseAddr, uint8_t ntIndex, GetTilemapOptions options, NesPpuState& state, IExtModeMapperDebug* exMode, ExtModeConfig& extCfg, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer, uint32_t bufferWidth) +{ + uint16_t baseAttributeAddr = ntBaseAddr + 960; + + for(uint8_t row = 0; row < 30; row++) { + for(uint8_t column = 0; column < 32; column++) { + uint16_t ntOffset = (row << 5) + column; + uint16_t attributeAddress = baseAttributeAddr + ((row & 0xFC) << 1) + (column >> 2); + uint8_t tileIndex = ntSource[ntBaseAddr + ntOffset]; + uint8_t attribute = ntSource[attributeAddress]; + uint8_t shift = (column & 0x02) | ((row & 0x02) << 1); + + uint8_t paletteBaseAddr; + if(exMode && exMode->HasExtendedAttributes(extCfg, ntIndex)) { + paletteBaseAddr = exMode->GetExAttributePalette(extCfg, ntIndex, ntOffset) << 2; + } else { + paletteBaseAddr = ((attribute >> shift) & 0x03) << 2; + } + + uint16_t tileAddr = state.Control.BackgroundPatternAddr + (tileIndex << 4); + if(options.DisplayMode == TilemapDisplayMode::AttributeView) { + for(uint8_t y = 0; y < 8; y++) { + for(uint8_t x = 0; x < 8; x++) { + uint8_t color = ((x & 0x04) >> 2) + ((y & 0x04) >> 1); + outBuffer[(row*bufferWidth*8) + (column << 3) + (y*bufferWidth) + x] = palette[paletteBaseAddr + color]; + } + } + } else { + for(uint8_t y = 0; y < 8; y++) { + uint8_t lowByte, highByte; + if(exMode && exMode->HasExtendedBackground(extCfg, ntIndex)) { + lowByte = exMode->GetExBackgroundChrData(extCfg, ntIndex, ntOffset, tileAddr + y); + highByte = exMode->GetExBackgroundChrData(extCfg, ntIndex, ntOffset, tileAddr + y + 8); + } else { + lowByte = vram[tileAddr + y]; + highByte = vram[tileAddr + y + 8]; + } + + uint32_t offset = (row*bufferWidth*8) + (column << 3) + (y*bufferWidth); + for(uint8_t x = 0; x < 8; x++) { + uint8_t color = ((lowByte >> (7 - x)) & 0x01) | (((highByte >> (7 - x)) & 0x01) << 1); + if(options.DisplayMode == TilemapDisplayMode::Grayscale) { + outBuffer[offset + x] = grayscalePalette[color]; + } else { + outBuffer[offset + x] = palette[paletteBaseAddr + color]; + } + } + } + } + } } +} - AddressCounters* accessCounters = options.AccessCounters; - uint8_t* prevVram = options.CompareVram != nullptr ? options.CompareVram : vram; +DebugTilemapInfo NesPpuTools::GetWindowTilemap(GetTilemapOptions options, NesPpuState& state, IExtModeMapperDebug* exMode, ExtModeConfig& extCfg, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +{ + uint16_t baseAddr = extCfg.WindowBank * 0x400; + + DrawNametable(extCfg.ExtRam, baseAddr, 4, options, state, exMode, extCfg, vram, palette, outBuffer, 256); - uint64_t masterClock = options.MasterClock; - uint32_t clockRate = _console->GetMasterClockRate() / _console->GetFps(); + DebugTilemapInfo result = {}; + result.Bpp = 2; + result.Format = TileFormat::NesBpp2; + result.TileWidth = 8; + result.TileHeight = 8; + result.ColumnCount = 32; + result.RowCount = 30; + result.TilemapAddress = extCfg.WindowBank * 0x400; + result.TilesetAddress = state.Control.BackgroundPatternAddr; + result.ScrollWidth = NesConstants::ScreenWidth; + result.ScrollHeight = NesConstants::ScreenHeight; + result.ScrollX = extCfg.WindowScrollX; + result.ScrollY = extCfg.WindowScrollY; + return result; +} - auto isHighlighted = [&](uint16_t addr, TilemapHighlightMode mode) -> bool { - switch(mode) { - default: - case TilemapHighlightMode::None: return false; +DebugTilemapInfo NesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +{ + IExtModeMapperDebug* exMode = dynamic_cast(_mapper); + NesPpuState& state = (NesPpuState&)baseState; + ExtModeConfig& extCfg = ((NesPpuToolsState&)ppuToolsState).ExtConfig; - //Highlight if modified since the last update - case TilemapHighlightMode::Changes: return prevVram[addr] != vram[addr]; + for(int i = 0; i < 32; i+=4) { + palette[i] = palette[0]; + } - //Highlight if modified in the last frame - case TilemapHighlightMode::Writes: return accessCounters && masterClock - accessCounters[addr].WriteStamp < clockRate; - } - }; + if(options.Layer == 1 && exMode) { + return GetWindowTilemap(options, state, exMode, extCfg, vram, palette, outBuffer); + } - constexpr uint32_t grayscalePalette[4] = { 0xFF000000, 0xFF808080, 0xFFC0C0C0, 0xFFFFFFFF }; for(int nametableIndex = 0; nametableIndex < 4; nametableIndex++) { uint16_t baseAddr = 0x2000 + nametableIndex * 0x400; - uint16_t baseAttributeAddr = baseAddr + 960; uint32_t bufferOffset = ((nametableIndex & 0x01) ? 256 : 0) + ((nametableIndex & 0x02) ? 512 * 240 : 0); - for(uint8_t row = 0; row < 30; row++) { - for(uint8_t column = 0; column < 32; column++) { - uint16_t ntIndex = (row << 5) + column; - uint16_t attributeAddress = baseAttributeAddr + ((row & 0xFC) << 1) + (column >> 2); - uint8_t tileIndex = vram[baseAddr + ntIndex]; - uint8_t attribute = vram[attributeAddress]; - bool tileHighlighted = isHighlighted(baseAddr + ntIndex, options.TileHighlightMode); - bool attrHighlighted = isHighlighted(attributeAddress, options.AttributeHighlightMode); - uint8_t shift = (column & 0x02) | ((row & 0x02) << 1); - - uint8_t paletteBaseAddr; - if(mmc5 && mmc5->IsExtendedAttributes()) { - paletteBaseAddr = mmc5->GetExAttributeNtPalette(ntIndex) << 2; - } else { - paletteBaseAddr = ((attribute >> shift) & 0x03) << 2; - } - - uint16_t tileAddr = bgAddr + (tileIndex << 4); - if(options.DisplayMode == TilemapDisplayMode::AttributeView) { - for(uint8_t y = 0; y < 8; y++) { - for(uint8_t x = 0; x < 8; x++) { - uint8_t color = ((x & 0x04) >> 2) + ((y & 0x04) >> 1); - outBuffer[bufferOffset + (row << 12) + (column << 3) + (y << 9) + x] = palette[paletteBaseAddr + color]; - } - } - } else { - for(uint8_t y = 0; y < 8; y++) { - uint8_t lowByte, highByte; - if(mmc5 && mmc5->IsExtendedAttributes()) { - lowByte = mmc5->GetExAttributeTileData(ntIndex, tileAddr + y); - highByte = mmc5->GetExAttributeTileData(ntIndex, tileAddr + y + 8); - } else { - lowByte = vram[tileAddr + y]; - highByte = vram[tileAddr + y + 8]; - } - uint32_t offset = bufferOffset + (row << 12) + (column << 3) + (y << 9); - for(uint8_t x = 0; x < 8; x++) { - uint8_t color = ((lowByte >> (7 - x)) & 0x01) | (((highByte >> (7 - x)) & 0x01) << 1); - if(options.DisplayMode == TilemapDisplayMode::Grayscale) { - outBuffer[offset+x] = grayscalePalette[color]; - } else { - outBuffer[offset+x] = palette[paletteBaseAddr + color]; - } - - if(tileHighlighted) { - static constexpr uint32_t tileChangedColor = 0x80FF0000; - if(x == 0 || y == 0 || x == 7 || y == 7) { - outBuffer[offset + x] = 0xFF000000 | tileChangedColor; - } else { - BlendColors((uint8_t*)&outBuffer[offset + x], (uint8_t*)&tileChangedColor); - } - } - - if(attrHighlighted) { - static constexpr uint32_t attrChangedColor = 0x80FFFF00; - bool isEdge = ( - ((column & 3) == 0 && x == 0) || - ((row & 3) == 0 && y == 0) || - ((column & 3) == 3 && x == 7) || - ((row & 3) == 3 && y == 7) - ); - if(isEdge) { - outBuffer[offset + x] = 0xFF000000 | attrChangedColor; - } else { - BlendColors((uint8_t*)&outBuffer[offset + x], (uint8_t*)&attrChangedColor); - } - } - } - } - } + DrawNametable(vram, baseAddr, nametableIndex, options, state, exMode, extCfg, vram, palette, outBuffer+bufferOffset, 512); + + if(options.DisplayMode != TilemapDisplayMode::AttributeView) { + if(options.TileHighlightMode != TilemapHighlightMode::None || options.AttributeHighlightMode != TilemapHighlightMode::None) { + ApplyHighlights(options, nametableIndex, vram, outBuffer); } } } @@ -132,7 +143,7 @@ DebugTilemapInfo NesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& b result.ColumnCount = 64; result.RowCount = 60; result.TilemapAddress = 0x2000; - result.TilesetAddress = bgAddr; + result.TilesetAddress = state.Control.BackgroundPatternAddr; result.ScrollWidth = NesConstants::ScreenWidth; result.ScrollHeight = NesConstants::ScreenHeight; @@ -190,6 +201,74 @@ DebugTilemapInfo NesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& b return result; } +void NesPpuTools::ApplyHighlights(GetTilemapOptions options, uint8_t nametableIndex, uint8_t* vram, uint32_t* outBuffer) +{ + uint16_t baseAddr = 0x2000 + nametableIndex * 0x400; + uint16_t baseAttributeAddr = baseAddr + 960; + uint32_t bufferOffset = ((nametableIndex & 0x01) ? 256 : 0) + ((nametableIndex & 0x02) ? 512 * 240 : 0); + + AddressCounters* accessCounters = options.AccessCounters; + uint8_t* prevVram = options.CompareVram != nullptr ? options.CompareVram : vram; + + uint64_t masterClock = options.MasterClock; + uint32_t clockRate = _console->GetMasterClockRate() / _console->GetFps(); + + auto isHighlighted = [&](uint16_t addr, TilemapHighlightMode mode) -> bool { + switch(mode) { + default: + case TilemapHighlightMode::None: return false; + + //Highlight if modified since the last update + case TilemapHighlightMode::Changes: return prevVram[addr] != vram[addr]; + + //Highlight if modified in the last frame + case TilemapHighlightMode::Writes: return accessCounters && masterClock - accessCounters[addr].WriteStamp < clockRate; + } + }; + + for(uint8_t row = 0; row < 30; row++) { + for(uint8_t column = 0; column < 32; column++) { + uint16_t ntOffset = (row << 5) + column; + uint16_t attributeAddress = baseAttributeAddr + ((row & 0xFC) << 1) + (column >> 2); + bool tileHighlighted = isHighlighted(baseAddr + ntOffset, options.TileHighlightMode); + bool attrHighlighted = isHighlighted(attributeAddress, options.AttributeHighlightMode); + + if(!tileHighlighted && !attrHighlighted) { + break; + } + + for(uint8_t y = 0; y < 8; y++) { + uint32_t offset = bufferOffset + (row << 12) + (column << 3) + (y << 9); + for(uint8_t x = 0; x < 8; x++) { + if(tileHighlighted) { + static constexpr uint32_t tileChangedColor = 0x80FF0000; + if(x == 0 || y == 0 || x == 7 || y == 7) { + outBuffer[offset + x] = 0xFF000000 | tileChangedColor; + } else { + BlendColors((uint8_t*)&outBuffer[offset + x], (uint8_t*)&tileChangedColor); + } + } + + if(attrHighlighted) { + static constexpr uint32_t attrChangedColor = 0x80FFFF00; + bool isEdge = ( + ((column & 3) == 0 && x == 0) || + ((row & 3) == 0 && y == 0) || + ((column & 3) == 3 && x == 7) || + ((row & 3) == 3 && y == 7) + ); + if(isEdge) { + outBuffer[offset + x] = 0xFF000000 | attrChangedColor; + } else { + BlendColors((uint8_t*)&outBuffer[offset + x], (uint8_t*)&attrChangedColor); + } + } + } + } + } + } +} + void NesPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& baseState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer) { uint32_t bgColor = GetSpriteBackgroundColor(options.Background, palette, false); @@ -222,10 +301,15 @@ void NesPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& b FrameInfo NesPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& state) { - return { 512, 480 }; + if(options.Layer == 0) { + return { 512, 480 }; + } else if(options.Layer == 1 && dynamic_cast(_mapper)) { + return { 256, 240 }; + } + return { 0,0 }; } -DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -243,20 +327,21 @@ DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uin row -= 30; } - MMC5* mmc5 = dynamic_cast(_mapper); + IExtModeMapperDebug* exMode = dynamic_cast(_mapper); + ExtModeConfig& extCfg = ((NesPpuToolsState&)ppuToolsState).ExtConfig; NesPpuState& state = (NesPpuState&)baseState; uint16_t bgAddr = state.Control.BackgroundPatternAddr; uint16_t baseAddr = 0x2000 + nametableIndex * 0x400; uint16_t baseAttributeAddr = baseAddr + 960; - uint16_t ntIndex = (row << 5) + column; + uint16_t ntOffset = (row << 5) + column; uint16_t attributeAddress = baseAttributeAddr + ((row & 0xFC) << 1) + (column >> 2); uint8_t attribute = vram[attributeAddress]; uint8_t shift = (column & 0x02) | ((row & 0x02) << 1); uint8_t paletteBaseAddr; - if(mmc5 && mmc5->IsExtendedAttributes()) { - paletteBaseAddr = mmc5->GetExAttributeNtPalette(ntIndex) << 2; + if(exMode && exMode->HasExtendedAttributes(extCfg, nametableIndex)) { + paletteBaseAddr = exMode->GetExAttributePalette(extCfg, nametableIndex, ntOffset) << 2; } else { paletteBaseAddr = ((attribute >> shift) & 0x03) << 2; } @@ -265,7 +350,7 @@ DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uin result.Column = column; result.Width = 8; result.Height = 8; - result.TileMapAddress = baseAddr + ntIndex; + result.TileMapAddress = baseAddr + ntOffset; result.TileIndex = vram[result.TileMapAddress]; result.TileAddress = bgAddr + (result.TileIndex << 4); result.PaletteIndex = paletteBaseAddr >> 2; @@ -276,8 +361,11 @@ DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uin return result; } -void NesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint32_t i, GetSpritePreviewOptions& options, NesPpuState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette) +void NesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint32_t i, GetSpritePreviewOptions& options, NesPpuState& state, NesPpuToolsState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette) { + IExtModeMapperDebug* exMode = dynamic_cast(_mapper); + ExtModeConfig& extCfg = ppuToolsState.ExtConfig; + sprite.Bpp = 2; sprite.Format = TileFormat::NesBpp2; sprite.SpriteIndex = i; @@ -329,8 +417,17 @@ void NesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview } for(int x = 0; x < 8; x++) { - uint8_t shift = horizontalMirror ? (7 - x) : x; - uint8_t color = GetTilePixelColor(vram, 0x3FFF, pixelStart, shift); + uint8_t lowByte, highByte; + if(exMode && exMode->HasExtendedSprites(extCfg)) { + lowByte = exMode->GetExSpriteChrData(extCfg, i, pixelStart); + highByte = exMode->GetExSpriteChrData(extCfg, i, pixelStart + 8); + } else { + lowByte = vram[pixelStart]; + highByte = vram[pixelStart + 8]; + } + + uint8_t shift = horizontalMirror ? x : (7 - x); + uint8_t color = ((lowByte >> shift) & 0x01) | (((highByte >> shift) & 0x01) << 1); uint32_t outOffset = (y * 8) + x; if(color > 0) { @@ -342,18 +439,19 @@ void NesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview } } -void NesPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void NesPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { NesPpuState& state = (NesPpuState&)baseState; + NesPpuToolsState& nesToolsState = (NesPpuToolsState&)ppuToolsState; for(int i = 0; i < 64; i++) { outBuffer[i].Init(); - GetSpriteInfo(outBuffer[i], spritePreviews+i*_spritePreviewSize, i, options, state, vram, oamRam, palette); + GetSpriteInfo(outBuffer[i], spritePreviews+i*_spritePreviewSize, i, options, state, nesToolsState, vram, oamRam, palette); } GetSpritePreview(options, baseState, outBuffer, spritePreviews, palette, screenPreview); } -DebugSpritePreviewInfo NesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) +DebugSpritePreviewInfo NesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) { DebugSpritePreviewInfo info = {}; info.Height = 256; diff --git a/Core/NES/Debugger/NesPpuTools.h b/Core/NES/Debugger/NesPpuTools.h index fdb1ffc2b..d016f92db 100644 --- a/Core/NES/Debugger/NesPpuTools.h +++ b/Core/NES/Debugger/NesPpuTools.h @@ -1,6 +1,7 @@ #pragma once #include "pch.h" #include "Debugger/PpuTools.h" +#include "NES/Debugger/IExtModeMapperDebug.h" class Debugger; class Emulator; @@ -8,23 +9,35 @@ class BaseMapper; class NesConsole; struct NesPpuState; +struct NesPpuToolsState +{ + ExtModeConfig ExtConfig; +}; + class NesPpuTools final : public PpuTools { private: NesConsole* _console = nullptr; BaseMapper* _mapper = nullptr; - void GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint32_t spriteIndex, GetSpritePreviewOptions& options, NesPpuState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette); + void GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, uint32_t spriteIndex, GetSpritePreviewOptions& options, NesPpuState& state, NesPpuToolsState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette); void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t *outBuffer); + DebugTilemapInfo GetWindowTilemap(GetTilemapOptions options, NesPpuState& state, IExtModeMapperDebug* exMode, ExtModeConfig& extCfg, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer); + + void DrawNametable(uint8_t* ntSource, uint32_t ntBaseAddr, uint8_t ntIndex, GetTilemapOptions options, NesPpuState& state, IExtModeMapperDebug* exMode, ExtModeConfig& extCfg, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer, uint32_t bufferWidth); + void ApplyHighlights(GetTilemapOptions options, uint8_t nametableIndex, uint8_t* vram, uint32_t* outBuffer); + public: NesPpuTools(Debugger* debugger, Emulator *emu, NesConsole* console); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + void GetPpuToolsState(BaseState& state) override; + + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreview, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreview, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/NES/MapperFactory.cpp b/Core/NES/MapperFactory.cpp index 0c22b3582..91ffe3553 100644 --- a/Core/NES/MapperFactory.cpp +++ b/Core/NES/MapperFactory.cpp @@ -22,6 +22,7 @@ #include "NES/Mappers/Homebrew/Cheapocabra.h" #include "NES/Mappers/Homebrew/FaridSlrom.h" #include "NES/Mappers/Homebrew/FaridUnrom.h" +#include "NES/Mappers/Homebrew/Rainbow.h" #include "NES/Mappers/Homebrew/SealieComputing.h" #include "NES/Mappers/Homebrew/UnRom512.h" #include "NES/Mappers/Homebrew/UnlDripGame.h" @@ -320,6 +321,8 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 1: return new Nina01(); case 2: return new BnRom(); } + break; + case 35: return new Mapper35(); case 36: return new Txc22000(); case 37: return new MMC3_37(); @@ -614,6 +617,8 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) case 528: break; //831128C case 529: return new T230(); case 530: return new Ax5705(); + + case 682: return new Rainbow(); case UnifBoards::Ac08: return new Ac08(); //mapper 42? case UnifBoards::Cc21: return new Cc21(); diff --git a/Core/NES/Mappers/Homebrew/FlashS29.h b/Core/NES/Mappers/Homebrew/FlashS29.h new file mode 100644 index 000000000..92c90e1c5 --- /dev/null +++ b/Core/NES/Mappers/Homebrew/FlashS29.h @@ -0,0 +1,220 @@ +#pragma once +#include "pch.h" +#include "Utilities/ISerializable.h" +#include "Utilities/Serializer.h" +#include "Shared/MessageManager.h" + +class FlashS29 final : public ISerializable +{ +private: + enum class ChipMode + { + WaitingForCommand, + Write, + Erase + }; + + enum class ChipModel + { + S29AL008, + S29AL016, + S29JL032, + S29GL064S + }; + + ChipModel _model = {}; + + ChipMode _mode = ChipMode::WaitingForCommand; + uint8_t _cycle = 0; + bool _softwareId = false; + bool _unlockBypass = false; + + //ROM data and size + uint8_t* _data = nullptr; + uint32_t _size = 0; + + void ProcessUnlockBypassMode(uint8_t value) + { + if(_cycle == 0) { + if(value == 0xA0) { + //1st write of unlock bypass write + _mode = ChipMode::Write; + } else if(value == 0x90) { + //1st write of unlock bypass reset + _cycle++; + } else { + ResetState(); + } + } else if(_cycle == 1) { + if(value == 0x00) { + //2nd write of unlock bypass reset + _unlockBypass = false; + } + ResetState(); + } + } + +protected: + void Serialize(Serializer& s) + { + SV(_mode); + SV(_cycle); + SV(_softwareId); + SV(_unlockBypass); + } + +public: + FlashS29(uint8_t* data, uint32_t size) + { + _data = data; + _size = size; + + switch(_size) { + case 0x100000: _model = ChipModel::S29AL008; break; + case 0x200000: _model = ChipModel::S29AL016; break; + case 0x400000: _model = ChipModel::S29JL032; break; + case 0x800000: _model = ChipModel::S29GL064S; break; + } + } + + bool IsSoftwareIdMode() + { + return _softwareId; + } + + int16_t Read(uint32_t addr) + { + if(_softwareId) { + switch(addr & 0x1FF) { + case 0x00: return 0x01; + + case 0x02: { + switch(_model) { + case ChipModel::S29AL008: return 0x5B; + case ChipModel::S29AL016: return 0x49; + case ChipModel::S29JL032: return 0x7E; + case ChipModel::S29GL064S: return 0x7E; + default: return 0xFF; + } + } + case 0x1C: { + switch(_model) { + case ChipModel::S29JL032: return 0x0A; + case ChipModel::S29GL064S: return 0x10; + default: return 0xFF; + } + } + + case 0x1E: { + switch(_model) { + case ChipModel::S29JL032: return 0x00; + case ChipModel::S29GL064S: return 0x00; + default: return 0xFF; + } + } + + default: return 0xFF; + } + } + return -1; + } + + void ResetState() + { + _mode = ChipMode::WaitingForCommand; + _cycle = 0; + } + + void Write(uint32_t addr, uint8_t value) + { + uint16_t cmd = addr & 0xFFF; + if(_mode == ChipMode::WaitingForCommand) { + if(_unlockBypass) { + ProcessUnlockBypassMode(value); + return; + } + + if(_cycle == 0) { + if(cmd == 0xAAA && value == 0xAA) { + //1st write, $AAA = $AA + _cycle++; + } else if(value == 0xF0) { + //Software ID exit + ResetState(); + _softwareId = false; + } + } else if(_cycle == 1 && cmd == 0x555 && value == 0x55) { + //2nd write, $555 = $55 + _cycle++; + } else if(_cycle == 2 && cmd == 0xAAA) { + //3rd write, determines command type + _cycle++; + switch(value) { + case 0x20: ResetState(); _unlockBypass = true; break; + case 0x80: _mode = ChipMode::Erase; break; + case 0x90: ResetState(); _softwareId = true; break; + case 0xA0: _mode = ChipMode::Write; break; + case 0xF0: ResetState(); _softwareId = false; break; + } + } else { + _cycle = 0; + } + } else if(_mode == ChipMode::Write) { + //Write a single byte + if(addr < _size) { + _data[addr] &= value; + } + ResetState(); + } else if(_mode == ChipMode::Erase) { + if(_cycle == 3) { + //4th write for erase command, $AAA = $AA + if(cmd == 0xAAA && value == 0xAA) { + _cycle++; + } else { + ResetState(); + } + } else if(_cycle == 4) { + //5th write for erase command, $555 = $55 + if(cmd == 0x555 && value == 0x55) { + _cycle++; + } else { + ResetState(); + } + } else if(_cycle == 5) { + if(cmd == 0xAAA && value == 0x10) { + //Chip erase + memset(_data, 0xFF, _size); + } else if(value == 0x30) { + //Sector erase + uint32_t pageCount = _size / 0x10000; + uint32_t page = addr / 0x10000; + if(page == pageCount - 1) { + //Last sector is split into multiple smaller sectors + vector sectorSizes; + switch(_model) { + case ChipModel::S29AL008: sectorSizes = vector { 32, 8, 8, 16 }; break; + case ChipModel::S29AL016: sectorSizes = vector { 32, 8, 8, 16 }; break; + case ChipModel::S29JL032: sectorSizes = vector { 8, 8, 8, 8, 8, 8, 8, 8 }; break; + case ChipModel::S29GL064S: sectorSizes = vector { 8, 8, 8, 8, 8, 8, 8, 8 }; break; + } + + uint32_t offsetKb = (addr & 0xFFFF) / 1024; + uint32_t segOffset = 0; + uint32_t segSize = 0; + for(int i = 0; i < sectorSizes.size(); i++) { + if(segOffset + sectorSizes[i] > offsetKb) { + break; + } + segOffset += sectorSizes[i]; + segSize = sectorSizes[i]; + } + memset(_data + page * 0x10000 + segOffset * 1024, 0xFF, segSize * 1024); + } else { + memset(_data + page * 0x10000, 0xFF, 0x10000); + } + } + ResetState(); + } + } + } +}; \ No newline at end of file diff --git a/Core/NES/Mappers/Homebrew/Rainbow.cpp b/Core/NES/Mappers/Homebrew/Rainbow.cpp new file mode 100644 index 000000000..0dfb10816 --- /dev/null +++ b/Core/NES/Mappers/Homebrew/Rainbow.cpp @@ -0,0 +1,1130 @@ +#include "pch.h" +#include "NES/Mappers/Homebrew/Rainbow.h" +#include "NES/Mappers/Homebrew/RainbowAudio.h" +#include "NES/Mappers/Homebrew/RainbowAudio.h" +#include "NES/Mappers/Homebrew/FlashS29.h" +#include "NES/NesConsole.h" +#include "NES/NesCpu.h" +#include "NES/NesMemoryManager.h" +#include "NES/BaseNesPpu.h" +#include "Shared/BatteryManager.h" +#include "Utilities/BitUtilities.h" +#include "Utilities/HexUtilities.h" +#include "Utilities/Patches/IpsPatcher.h" + +Rainbow::Rainbow() +{ +} + +void Rainbow::InitMapper() +{ + _audio.reset(new RainbowAudio(_console)); + _prgFlash.reset(new FlashS29(_prgRom, _prgSize)); + _chrFlash.reset(new FlashS29(_chrRom, _chrRomSize)); + UpdateState(); + + AddRegisterRange(0x6000, 0xFFFF, MemoryOperation::Any); + + _orgPrgRom = vector(_prgRom, _prgRom + _prgSize); + _orgChrRom = vector(_chrRom, _chrRom + _chrRomSize); + ApplySaveData(); +} + +void Rainbow::ApplySaveData() +{ + if(_console->GetNesConfig().DisableFlashSaves) { + return; + } + + //Apply save data (saved as an IPS file), if found + vector ipsData = _emu->GetBatteryManager()->LoadBattery(".ips"); + if(!ipsData.empty()) { + vector patchedPrgRom; + if(IpsPatcher::PatchBuffer(ipsData, _orgPrgRom, patchedPrgRom)) { + memcpy(_prgRom, patchedPrgRom.data(), _prgSize); + } + } + + if(_chrRomSize > 0) { + ipsData = _emu->GetBatteryManager()->LoadBattery(".chr.ips"); + if(!ipsData.empty()) { + vector patchedChrRom; + if(IpsPatcher::PatchBuffer(ipsData, _orgChrRom, patchedChrRom)) { + memcpy(_chrRom, patchedChrRom.data(), _chrRomSize); + } + } + } +} + +void Rainbow::SaveBattery() +{ + if(_console->GetNesConfig().DisableFlashSaves) { + return; + } + + vector prgRom = vector(_prgRom, _prgRom + _prgSize); + vector ipsData = IpsPatcher::CreatePatch(_orgPrgRom, prgRom); + if(ipsData.size() > 8) { + _emu->GetBatteryManager()->SaveBattery(".ips", ipsData.data(), (uint32_t)ipsData.size()); + } + + if(_chrRomSize > 0) { + vector chrRom = vector(_chrRom, _chrRom + _chrRomSize); + ipsData = IpsPatcher::CreatePatch(_orgChrRom, chrRom); + if(ipsData.size() > 8) { + _emu->GetBatteryManager()->SaveBattery(".chr.ips", ipsData.data(), (uint32_t)ipsData.size()); + } + } +} + +void Rainbow::Reset(bool softReset) +{ + WriteRegister(0x4100, 0x00); + WriteRegister(0x4108, 0x00); + WriteRegister(0x4118, 0x00); + WriteRegister(0x4120, 0x00); + WriteRegister(0x4130, 0x00); + WriteRegister(0x4140, 0x00); + WriteRegister(0x4126, 0x00); + WriteRegister(0x4127, 0x00); + WriteRegister(0x4128, 0x01); + WriteRegister(0x4129, 0x01); + WriteRegister(0x412E, 0x00); + WriteRegister(0x412A, 0x00); + WriteRegister(0x412B, 0x00); + WriteRegister(0x412C, 0x00); + WriteRegister(0x412D, 0x00); + WriteRegister(0x412F, 0x80); + WriteRegister(0x4241, 0x07); + WriteRegister(0x4242, 0x1B); + WriteRegister(0x4152, 0x00); + WriteRegister(0x4153, 0x87); + WriteRegister(0x415A, 0x00); + WriteRegister(0x4190, 0x00); + WriteRegister(0x41A9, 0x03); + WriteRegister(0x41AA, 0x0F); +} + +void Rainbow::OnAfterResetPowerOn() +{ + _console->GetMemoryManager()->RegisterReadHandler(this, 0x4011, 0x4011); + _console->GetMemoryManager()->RegisterWriteHandler(this, 0x2000, 0x2000); + _console->GetMemoryManager()->RegisterWriteHandler(this, 0x2003, 0x2004); +} + +void Rainbow::WriteRam(uint16_t addr, uint8_t value) +{ + if(addr < 0x4000) { + switch(addr) { + case 0x2000: _largeSprites = value & 0x20; break; + case 0x2003: _oamAddr = value; break; + case 0x2004: + if((_oamAddr & 0x03) == 0) { + _oamPosY[_oamAddr >> 2] = value; + } + _oamAddr++; + break; + } + + _console->GetPpu()->WriteRam(addr, value); + } else { + BaseMapper::WriteRam(addr, value); + } +} + +uint8_t Rainbow::ReadRam(uint16_t addr) +{ + if(addr == 0x4011) { + if(_cpuIrqAckOn4011) { + AckCpuIrq(); + } + return _audio->GetLastOutput(); + } + return BaseMapper::ReadRam(addr); +} + +void Rainbow::GenerateOamClear() +{ + if(_oamCodeLocked) { + return; + } + _oamCodeLocked = true; + + int i = 6; + for(int spr = 0; spr < 64; spr++) { + _oamCode[i++] = 0xA9; //LDA #spr + _oamCode[i++] = spr * 4; + + if(spr == 0) { + _oamCode[i++] = 0xAA; //TAX + _oamCode[i++] = 0xCA; //DEX + } + + _oamCode[i++] = 0x8D; //STA $2003 + _oamCode[i++] = 0x03; + _oamCode[i++] = 0x20; + + _oamCode[i++] = 0x8E; //STX $2004 + _oamCode[i++] = 0x04; + _oamCode[i++] = 0x20; + } + _oamCode[i++] = 0x60; //RTS +} + +void Rainbow::GenerateExtUpdate() +{ + if(_oamCodeLocked) { + return; + } + _oamCodeLocked = true; + + int i = 2; + for(int spr = 0; spr < 64; spr++) { + _oamCode[i++] = 0xA9; //LDA #[fpga ram value] + _oamCode[i++] = _mapperRam[0x1800 + (_oamExtUpdatePage * 0x40) + spr]; + + _oamCode[i++] = 0x8D; //STA $42xx + _oamCode[i++] = spr; + _oamCode[i++] = 0x42; + } + _oamCode[i++] = 0x60; //RTS +} + +void Rainbow::GenerateOamSlowUpdate() +{ + if(_oamCodeLocked) { + return; + } + _oamCodeLocked = true; + + int i = 0; + _oamCode[i++] = 0xA9; //LDA #00 + _oamCode[i++] = 0x00; + + _oamCode[i++] = 0x8D; //STA $2003 + _oamCode[i++] = 0x03; + _oamCode[i++] = 0x20; + + for(int j = 0; j < 256; j++) { + _oamCode[i++] = 0xA9; //LDA #[fpga ram value] + _oamCode[i++] = _mapperRam[0x1800 + (_oamSlowUpdatePage * 0x100) + j]; + + _oamCode[i++] = 0x8D; //STA $2004 + _oamCode[i++] = 0x04; + _oamCode[i++] = 0x20; + } + _oamCode[i++] = 0x60; //RTS +} + +PrgMemoryType Rainbow::GetWorkRamType() +{ + return HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam; +} + +void Rainbow::SelectHighBank(uint16_t start, uint16_t size, uint8_t reg) +{ + PrgMemoryType memType = (_highBanks[reg] & 0x8000) ? GetWorkRamType() : PrgMemoryType::PrgRom; + MemoryAccessType accessType = (_highBanks[reg] & 0x8000) ? MemoryAccessType::ReadWrite : MemoryAccessType::Read; + SetCpuMemoryMapping(start, start + size - 1, memType, (_highBanks[reg] & 0x7FFF) * size, accessType); +} + +void Rainbow::SelectLowBank(uint16_t start, uint16_t size, uint8_t reg) +{ + switch((_lowBanks[reg] & 0xC000) >> 14) { + case 0: case 1: SetCpuMemoryMapping(start, start + size - 1, PrgMemoryType::PrgRom, (_lowBanks[reg] & 0x7FFF) * size, MemoryAccessType::Read); break; + case 2: SetCpuMemoryMapping(start, start + size - 1, GetWorkRamType(), (_lowBanks[reg] & 0x3FFF) * size, MemoryAccessType::ReadWrite); break; + case 3: SetCpuMemoryMapping(start, start + size - 1, PrgMemoryType::MapperRam, (_lowBanks[reg] & 0x3FFF) * size, MemoryAccessType::ReadWrite); break; + } +} + +void Rainbow::SelectChrBank(uint16_t start, uint16_t size, uint8_t reg) +{ + if(_chrSource >= 2) { + SetPpuMemoryMapping(0x0000, 0x0FFF, ChrMemoryType::MapperRam, 0, MemoryAccessType::ReadWrite); + SetPpuMemoryMapping(0x1000, 0x1FFF, ChrMemoryType::MapperRam, 0, MemoryAccessType::ReadWrite); + } else { + ChrMemoryType type = _chrSource == 0 ? ChrMemoryType::ChrRom : ChrMemoryType::ChrRam; + MemoryAccessType accessType = _chrSource == 0 ? MemoryAccessType::Read : MemoryAccessType::ReadWrite; + SetPpuMemoryMapping(start, start + size - 1, type, _chrBanks[reg] * size, accessType); + } +} + +void Rainbow::UpdateState() +{ + switch(_highMode) { + case 0: + SelectHighBank(0x8000, 0x8000, 0); + break; + + case 1: + SelectHighBank(0x8000, 0x4000, 0); + SelectHighBank(0xC000, 0x4000, 4); + break; + + case 2: + SelectHighBank(0x8000, 0x4000, 0); + SelectHighBank(0xC000, 0x2000, 4); + SelectHighBank(0xE000, 0x2000, 6); + break; + + case 3: + SelectHighBank(0x8000, 0x2000, 0); + SelectHighBank(0xA000, 0x2000, 2); + SelectHighBank(0xC000, 0x2000, 4); + SelectHighBank(0xE000, 0x2000, 6); + break; + + default: + for(int i = 0; i < 8; i++) { + SelectHighBank(0x8000 + i * 0x1000, 0x1000, i); + } + break; + } + + if(_lowMode) { + SelectLowBank(0x6000, 0x1000, 0); + SelectLowBank(0x7000, 0x1000, 1); + } else { + SelectLowBank(0x6000, 0x2000, 0); + } + + SetCpuMemoryMapping(0x5000, 0x5FFF, PrgMemoryType::MapperRam, _fpgaRamBank * 0x1000, MemoryAccessType::ReadWrite); + SetCpuMemoryMapping(0x4800, 0x4FFF, PrgMemoryType::MapperRam, 0x1800, MemoryAccessType::ReadWrite); + + uint8_t chrBankCount = (1 << _chrMode); + uint16_t chrBankSize = 0x2000 >> _chrMode; + for(int i = 0; i < chrBankCount; i++) { + SelectChrBank(0x0000 + i * chrBankSize, chrBankSize, i); + } + + for(int i = 0; i < 4; i++) { + NtControl& ctrl = _ntControl[i]; + uint16_t start = 0x2000 + i * 0x400; + uint16_t end = 0x23FF + i * 0x400; + switch(ctrl.Source) { + case 0: SetPpuMemoryMapping(start, end, ChrMemoryType::NametableRam, _ntBanks[i] * 0x400, MemoryAccessType::ReadWrite); break; + case 1: SetPpuMemoryMapping(start, end, ChrMemoryType::ChrRam, _ntBanks[i] * 0x400, MemoryAccessType::ReadWrite); break; + case 2: SetPpuMemoryMapping(start, end, ChrMemoryType::MapperRam, (_ntBanks[i] & 0x03) * 0x400, MemoryAccessType::ReadWrite); break; + case 3: SetPpuMemoryMapping(start, end, ChrMemoryType::ChrRom, _ntBanks[i] * 0x400, MemoryAccessType::Read); break; + } + } +} + +void Rainbow::ProcessCpuClock() +{ + BaseProcessCpuClock(); + + _jitterCounter++; + + _audio->Clock(); + + if(_cpuIrqCounter && --_cpuIrqCounter == 0) { + _cpuIrqCounter = _cpuIrqReloadValue; + _cpuIrqPending = true; + UpdateIrqStatus(); + } + + if(_ppuIdleCounter) { + _ppuIdleCounter--; + if(_ppuIdleCounter == 0) { + //"The "in-frame" flag is cleared when the PPU is no longer rendering. This is detected when 3 CPU cycles pass without a PPU read having occurred (PPU /RD has not been low during the last 3 M2 rises)." + _inFrame = false; + _inHBlank = false; + _scanlineCounter = -1; + _ntFetchCounter = 0; + _ntReadCounter = 0; + _oamAddr = 0; + } + } +} + +void Rainbow::UpdateInWindowFlag() +{ + bool yMatch; + bool xMatch; + + uint8_t scanline = _ntFetchCounter >= 41 ? _scanlineCounter + 1 : _scanlineCounter; + if(_windowY1 >= _windowY2) { + yMatch = scanline <= _windowY2 || scanline > _windowY1; + } else { + yMatch = scanline >= _windowY1 && scanline <= _windowY2; + } + + uint8_t column = (_ntFetchCounter + 1) % 42; + if(_windowX1 >= _windowX2) { + xMatch = column <= _windowX2 || column > _windowX1; + } else { + xMatch = column >= _windowX1 && column <= _windowX2; + } + + _inWindow = xMatch && yMatch; +} + +uint8_t Rainbow::MapperReadVram(uint16_t addr, MemoryOperationType memoryOperationType) +{ + if(_chrFlash->IsSoftwareIdMode()) { + AddressInfo absAddr = GetPpuAbsoluteAddress(addr); + if(absAddr.Address >= 0 && absAddr.Type == MemoryType::NesChrRom) { + return _chrFlash->Read(absAddr.Address); + } + } + + _ppuReadCounter++; + DetectScanlineStart(addr); + + if(_slIrqScanline == _scanlineCounter && _slIrqOffset == _ppuReadCounter) { + _slIrqPending = true; + UpdateIrqStatus(); + } + + _ppuIdleCounter = 3; + _lastPpuReadAddr = addr; + + if(!_inFrame) { + return InternalReadVram(addr); + } + + bool isNtFetch = addr >= 0x2000 && addr <= 0x2FFF; + if(isNtFetch) { + //Nametable or attribute fetch + bool isAttributeFetch = (addr & 0x3FF) >= 0x3C0; + if(!isAttributeFetch) { + //Nametable fetch, check if we're inside or outside the window + _ntFetchCounter++; + if(_ntFetchCounter == 33) { + _inHBlank = true; + } + _inWindow = false; + + if(_windowEnabled) { + UpdateInWindowFlag(); + } + } + + NtControl& ctrl = _inWindow ? _windowControl : _ntControl[(addr >> 10) & 0x03]; + + if(_inWindow) { + uint8_t scanline = _ntFetchCounter >= 41 ? _scanlineCounter + 1 : _scanlineCounter; + uint8_t windowScanline = (scanline + _windowScrollY) % 240; + uint8_t column = (_ntFetchCounter + 1) % 42; + if(!isAttributeFetch) { + addr = ((windowScanline / 8 * 32) + ((column + _windowScrollX) & 0x1F)); + } else { + addr = 0x3C0 + ((windowScanline >> 3) | (((column + _windowScrollX) & 0x1F) >> 2)); + } + } + + //Process BG/Attribute ext modes and fill mode + if(!isAttributeFetch) { + _overrideTileFetch = ctrl.BgExtMode; + bool hasExtMode = ctrl.AttrExtMode || ctrl.BgExtMode; + if(hasExtMode) { + _extData = _mapperRam[(ctrl.FpgaRamSrc * 0x400) + (addr & 0x3FF)]; + } + if(ctrl.FillMode) { + return _fillModeTileIndex; + } + } else { + if(ctrl.AttrExtMode) { + uint8_t attr = (_extData & 0xC0) >> 6; + return attr | (attr << 2) | (attr << 4) | (attr << 6); + } else if(ctrl.FillMode) { + uint8_t attr = _fillModeAttrIndex; + return attr | (attr << 2) | (attr << 4) | (attr << 6); + } + } + + if(_inWindow) { + //If in window (and fill mode is not enabled), return the window nametable/attribute data + return _mapperRam[_windowBank * 0x400 + addr]; + } + } else { + //Tile data fetches + bool isBgFetch = _ntFetchCounter < 33 || _ntFetchCounter >= 41; + if(_inWindow && isBgFetch) { + uint8_t scanline = _ntFetchCounter >= 41 ? _scanlineCounter + 1 : _scanlineCounter; + uint8_t windowScanline = (scanline + _windowScrollY) % 240; + if(_overrideTileFetch) { + uint32_t fetchAddr = (addr & 0xFF8) | (windowScanline & 0x07) | ((_extData & 0x3F) << 12) | (_bgExtModeOffset << 18); + return ReadChr(fetchAddr); + } else { + uint32_t fetchAddr = (addr & 0x1FF8) | (windowScanline & 0x07); + return ReadChr(fetchAddr); + } + } else if(_overrideTileFetch && isBgFetch) { + uint32_t fetchAddr = (addr & 0xFFF) | ((_extData & 0x3F) << 12) | (_bgExtModeOffset << 18); + return ReadChr(fetchAddr); + } else if(_spriteExtMode && !isBgFetch) { + uint8_t spriteIndex = _oamMappings[_ntFetchCounter - 33]; + uint32_t fetchAddr; + if(_largeSprites) { + fetchAddr = (_spriteExtBank << 21) | (_spriteExtData[spriteIndex] << 13) | (addr & 0x1FFF); + } else { + fetchAddr = (_spriteExtBank << 20) | (_spriteExtData[spriteIndex] << 12) | (addr & 0xFFF); + } + return ReadChr(fetchAddr); + } + } + + return InternalReadVram(addr); +} + +void Rainbow::MapperWriteVram(uint16_t addr, uint8_t value) +{ + if(addr < 0x2000) { + AddressInfo absAddr = GetPpuAbsoluteAddress(addr); + if(absAddr.Address >= 0 && absAddr.Type == MemoryType::NesChrRom) { + _chrFlash->Write(absAddr.Address, value); + return; + } + } + + InternalWriteVram(addr, value); +} + +uint8_t Rainbow::ReadChr(uint32_t addr) +{ + switch(_chrSource) { + default: + case 0: return _chrRomSize ? _chrRom[addr & (_chrRomSize - 1)] : 0; + case 1: return _chrRamSize ? _chrRam[addr & (_chrRamSize - 1)] : 0; + + case 2: + case 3: + return _mapperRam[addr & 0x1FFF]; + } +} + +void Rainbow::UpdateIrqStatus() +{ + bool active = (_cpuIrqEnabled && _cpuIrqPending) || (_slIrqEnabled && _slIrqPending); + if(active) { + if(!_console->GetCpu()->HasIrqSource(IRQSource::External)) { + _jitterCounter = 0; + } + _console->GetCpu()->SetIrqSource(IRQSource::External); + } else { + _console->GetCpu()->ClearIrqSource(IRQSource::External); + } +} + +void Rainbow::AckCpuIrq() +{ + _cpuIrqEnabled = _cpuIrqEnableAfterAck; + if(_cpuIrqEnabled) { + _cpuIrqCounter = _cpuIrqReloadValue; + } + _cpuIrqPending = false; + UpdateIrqStatus(); +} + +void Rainbow::ProcessSpriteEval() +{ + uint8_t spriteCount = 0; + int scanline = _scanlineCounter; + int height = (_largeSprites ? 16 : 8); + + memset(_oamMappings, 0, sizeof(_oamMappings)); + + for(int i = 0; i < 64; i++) { + uint8_t y = _oamPosY[i]; + if(scanline >= y && scanline < y + height) { + _oamMappings[spriteCount] = i; + spriteCount++; + + if(spriteCount >= 8) { + break; + } + } + } +} + +void Rainbow::DetectScanlineStart(uint16_t addr) +{ + if(addr >= 0x2000 && addr <= 0x2FFF) { + if(_lastPpuReadAddr == addr) { + //Count consecutive identical reads + _ntReadCounter++; + + if(_ntReadCounter >= 2) { + //After 3 identical NT reads, trigger IRQ when the following attribute byte is read + if(!_inFrame) { + _inFrame = true; + _scanlineCounter = 0; + } else { + _scanlineCounter++; + } + + ProcessSpriteEval(); + + _ntFetchCounter = 0; + _ppuReadCounter = 0; + _ntReadCounter = 0; + _inHBlank = false; + } + } else { + _ntReadCounter = 0; + } + } else { + _ntReadCounter = 0; + } +} + +uint8_t Rainbow::ReadRegister(uint16_t addr) +{ + switch(addr) { + case 0x4100: return _highMode | (_lowMode << 7); + case 0x4120: return _chrMode | ((uint8_t)_windowEnabled << 4) | ((uint8_t)_spriteExtMode << 5) | (_chrSource << 6); + + case 0x412A: case 0x412B: case 0x412C: case 0x412D: + return _ntControl[addr - 0x412A].ToByte(); + + case 0x412F: return _windowControl.ToByte(); + + case 0x4151: + _slIrqPending = false; + UpdateIrqStatus(); + return ( + ((uint8_t)_inHBlank << 7) | + ((uint8_t)_inFrame << 6) | + (uint8_t)_slIrqPending + ); + + case 0x4154: return _jitterCounter; + + case 0x415F: + { + uint8_t value = _mapperRam[_fpgaRamAddr]; + _fpgaRamAddr = (_fpgaRamAddr + _fpgaRamInc) & 0x1FFF; + return value; + } + + case 0x4160: return 0x20; + case 0x4161: + return ( + (uint8_t)_wifiIrqPending | + ((uint8_t)_cpuIrqPending << 6) | + ((uint8_t)_slIrqPending << 7) + ); + + case 0x4280: GenerateOamSlowUpdate(); break; + case 0x4282: GenerateExtUpdate(); break; + case 0x4286: GenerateOamClear(); break; + + case 0x4190: return (uint8_t)_espEnabled | ((uint8_t)_wifiIrqEnabled << 1); + case 0x4191: return ((uint8_t)_dataReady << 6) | ((uint8_t)_dataReceived << 7); + case 0x4192: return (uint8_t)_dataSent << 7; + + case 0xFFFA: + case 0xFFFB: + _inFrame = false; + _lastPpuReadAddr = 0; + _scanlineCounter = 0; + _slIrqPending = false; + UpdateIrqStatus(); + + if(_nmiVectorEnabled) { + return (addr & 0x01) ? _nmiVectorAddr : (_nmiVectorAddr >> 8); + } + return DebugReadRam(addr); + + case 0xFFFE: + case 0xFFFF: + if(_irqVectorEnabled) { + return (addr & 0x01) ? _irqVectorAddr : (_irqVectorAddr >> 8); + } + return DebugReadRam(addr); + } + + if(addr >= 0x4280 && addr < 0x4800) { + //Built-in OAM functions + if(addr >= 0x4286) { + _oamCodeLocked = false; + } + return _oamCode[addr - 0x4280]; + } + + if(addr >= 0x6000) { + if(_prgFlash->IsSoftwareIdMode()) { + AddressInfo absAddr = GetAbsoluteAddress(addr); + if(absAddr.Address >= 0 && absAddr.Type == MemoryType::NesPrgRom) { + return _prgFlash->Read(absAddr.Address); + } + } + return InternalReadRam(addr); + } else { + return _console->GetMemoryManager()->GetOpenBus(); + } +} + +void Rainbow::WriteRegister(uint16_t addr, uint8_t value) +{ + switch(addr) { + case 0x4100: + _highMode = value & 0x07; + _lowMode = (value & 0x80) >> 7; + UpdateState(); + break; + + case 0x4115: + _fpgaRamBank = value & 0x01; + UpdateState(); + break; + + case 0x4120: + _chrMode = value & 0x07; + _windowEnabled = value & 0x10; + _spriteExtMode = value & 0x20; + _chrSource = (value & 0xC0) >> 6; + UpdateState(); + break; + + case 0x4121: _bgExtModeOffset = value & 0x1F; break; + + case 0x4124: _fillModeTileIndex = value; break; + case 0x4125: _fillModeAttrIndex = value; break; + case 0x412E: _windowBank = value; break; + + case 0x412F: + _windowControl.BgExtMode = value & 0x01; + _windowControl.AttrExtMode = value & 0x02; + _windowControl.FpgaRamSrc = (value & 0x0C) >> 2; + _windowControl.FillMode = value & 0x20; + _windowControl.Source = (value & 0xC0) >> 6; //todo this is writeable/readable but ignored? + break; + + case 0x4150: _slIrqScanline = value; break; + case 0x4151: _slIrqEnabled = true; break; + + case 0x4152: + _slIrqEnabled = false; + _slIrqPending = false; + UpdateIrqStatus(); + break; + + case 0x4153: _slIrqOffset = std::clamp(value, 1, 170); break; + + case 0x4158: BitUtilities::SetBits<8>(_cpuIrqReloadValue, value); break; + case 0x4159: BitUtilities::SetBits<0>(_cpuIrqReloadValue, value); break; + case 0x415A: + _cpuIrqEnabled = value & 0x01; + _cpuIrqEnableAfterAck = value & 0x02; + _cpuIrqAckOn4011 = value & 0x04; + if(_cpuIrqEnabled) { + _cpuIrqCounter = _cpuIrqReloadValue; + } + UpdateIrqStatus(); + break; + + case 0x415B: AckCpuIrq(); break; + + case 0x415C: BitUtilities::SetBits<8>(_fpgaRamAddr, value & 0x1F); break; + case 0x415D: BitUtilities::SetBits<0>(_fpgaRamAddr, value); break; + case 0x415E: _fpgaRamInc = value; break; + case 0x415F: + _mapperRam[_fpgaRamAddr] = value; + _fpgaRamAddr = (_fpgaRamAddr + _fpgaRamInc) & 0x1FFF; + break; + + case 0x416B: + _nmiVectorEnabled = value & 0x01; + _irqVectorEnabled = value & 0x02; + break; + + case 0x416C: BitUtilities::SetBits<8>(_nmiVectorAddr, value); break; + case 0x416D: BitUtilities::SetBits<0>(_nmiVectorAddr, value); break; + case 0x416E: BitUtilities::SetBits<8>(_irqVectorAddr, value); break; + case 0x416F: BitUtilities::SetBits<0>(_irqVectorAddr, value); break; + + case 0x4170: _windowX1 = value & 0x1F; break; + case 0x4171: _windowX2 = value & 0x1F; break; + case 0x4172: _windowY1 = value; break; + case 0x4173: _windowY2 = value; break; + case 0x4174: _windowScrollX = value & 0x1F; break; + case 0x4175: _windowScrollY = value; break; + + case 0x4190: + _espEnabled = value & 0x01; + _wifiIrqEnabled = value & 0x02; + break; + + case 0x4191: _dataReceived = false; break; + case 0x4192: _dataSent = false; break; + case 0x4193: _recvDstAddr = value & 0x07; break; + case 0x4194: _sendSrcAddr = value & 0x07; break; + + case 0x4240: _spriteExtBank = value & 0x07; break; + case 0x4241: _oamSlowUpdatePage = value & 0x07; break; + case 0x4242: _oamExtUpdatePage = value & 0x1F; break; + } + + if(addr >= 0x4106 && addr <= 0x4107) { + BitUtilities::SetBits<8>(_lowBanks[addr - 0x4106], value); + UpdateState(); + } else if(addr >= 0x4116 && addr <= 0x4117) { + BitUtilities::SetBits<0>(_lowBanks[addr - 0x4116], value); + UpdateState(); + } if(addr >= 0x4108 && addr <= 0x410F) { + BitUtilities::SetBits<8>(_highBanks[addr - 0x4108], value); + UpdateState(); + } else if(addr >= 0x4118 && addr <= 0x411F) { + BitUtilities::SetBits<0>(_highBanks[addr - 0x4118], value); + UpdateState(); + } else if(addr >= 0x4130 && addr <= 0x413F) { + BitUtilities::SetBits<8>(_chrBanks[addr - 0x4130], value); + UpdateState(); + } else if(addr >= 0x4140 && addr <= 0x414F) { + BitUtilities::SetBits<0>(_chrBanks[addr - 0x4140], value); + UpdateState(); + } else if(addr >= 0x4126 && addr <= 0x4129) { + _ntBanks[addr - 0x4126] = value; + UpdateState(); + } else if(addr >= 0x412A && addr <= 0x412D) { + NtControl& ctrl = _ntControl[addr - 0x412A]; + ctrl.AttrExtMode = value & 0x01; + ctrl.BgExtMode = value & 0x02; + ctrl.FpgaRamSrc = (value & 0x0C) >> 2; + ctrl.FillMode = value & 0x20; + ctrl.Source = (value & 0xC0) >> 6; + UpdateState(); + } else if(addr >= 0x41A0 && addr <= 0x41AA) { + _audio->WriteRegister(addr, value); + } else if(addr >= 0x4200 && addr <= 0x423F) { + _spriteExtData[addr - 0x4200] = value; + } + + if(addr >= 0x6000) { + AddressInfo absAddr = GetAbsoluteAddress(addr); + if(absAddr.Address >= 0 && absAddr.Type == MemoryType::NesPrgRom) { + _prgFlash->Write(absAddr.Address, value); + } else { + WritePrgRam(addr, value); + } + } +} + +vector Rainbow::GetMapperStateEntries() +{ + vector entries; + entries.push_back(MapperStateEntry("", "CPU Mappings")); + entries.push_back(MapperStateEntry("$4100.0-2", "ROM Mode (8000-FFFF)", _highMode, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4100.7", "RAM Mode (6000-7FFF)", _lowMode, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("$4106/16", "RAM Bank 0", _lowBanks[0] & 0x7FFF, MapperStateValueType::Number16)); + string source; + switch((_lowBanks[0] & 0xC000) >> 14) { + case 0: case 1: source = "PRG-ROM"; break; + case 2: source = "PRG-RAM"; break; + case 3: source = "FPGA RAM"; break; + } + entries.push_back(MapperStateEntry("$4116.6-7", "PRG-RAM Bank 0 Source", source)); + + entries.push_back(MapperStateEntry("$4107/17", "PRG-RAM Bank 1", _lowBanks[1], MapperStateValueType::Number16)); + switch((_lowBanks[0] & 0xC000) >> 14) { + case 0: case 1: source = "PRG-ROM"; break; + case 2: source = "PRG-RAM"; break; + case 3: source = "FPGA RAM"; break; + } + entries.push_back(MapperStateEntry("$4117.6-7", "PRG-RAM Bank 1 Source", source)); + + for(int i = 0; i < 8; i++) { + entries.push_back(MapperStateEntry( + "$" + HexUtilities::ToHex(0x4108 + i) + "/" + HexUtilities::ToHex(0x4118 + i) + ".0-14", + "ROM Bank " + std::to_string(i), + _highBanks[i] & 0x7FFF, + MapperStateValueType::Number16 + )); + + entries.push_back(MapperStateEntry( + HexUtilities::ToHex(0x4118 + i) + ".7", + "ROM Bank " + std::to_string(i) + " Source", + (string)((_highBanks[i] & 0x8000) ? "RAM" : "ROM") + )); + } + + entries.push_back(MapperStateEntry("$4115.0", "FPGA RAM Bank", _fpgaRamBank, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "PPU Config")); + entries.push_back(MapperStateEntry("$4120.0-2", "CHR Banking Mode", _chrMode, MapperStateValueType::Number16)); + entries.push_back(MapperStateEntry("$4120.4", "Window Split Mode", _windowEnabled)); + entries.push_back(MapperStateEntry("$4120.5", "Sprite Extended Mode", _spriteExtMode)); + entries.push_back(MapperStateEntry("$4120.6-7", "CHR Source", string(_chrSource ? "RAM" : "ROM"), _chrSource)); + entries.push_back(MapperStateEntry("$4121.0-5", "Extended BG CHR Bank", _bgExtModeOffset, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "Fill Mode")); + entries.push_back(MapperStateEntry("$4124", "Tile Index", _fillModeTileIndex, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4125", "Attribute Index", _fillModeAttrIndex, MapperStateValueType::Number8)); + + auto processNtControl = [&](uint16_t addr, NtControl& ctrl) { + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(addr) + ".0", "Attribute Ext. Mode", ctrl.AttrExtMode)); + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(addr) + ".1", "BG Ext. Mode", ctrl.BgExtMode)); + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(addr) + ".2-3", "Ext. Mode FPGA RAM Source", "$" + HexUtilities::ToHex(ctrl.FpgaRamSrc * 0x400), ctrl.FpgaRamSrc)); + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(addr) + ".5", "Fill Mode", ctrl.FillMode)); + + string src; + switch(ctrl.Source) { + case 0: src = "Nametable RAM"; break; + case 1: src = "CHR RAM"; break; + case 2: src = "FPGA RAM"; break; + case 3: src = "CHR ROM"; break; + } + entries.push_back(MapperStateEntry("$412A.6-7", "Source", src, ctrl.Source)); + }; + + for(int i = 0; i < 4; i++) { + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(0x4126 + i) + "/" + HexUtilities::ToHex(0x412A + i).substr(3, 1), "Nametable " + std::to_string(i) + " ($" + HexUtilities::ToHex(0x2000 + i * 0x400) + ")")); + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(0x4126 + i), "Selected Bank", _ntBanks[i], MapperStateValueType::Number8)); + processNtControl(0x412A + i, _ntControl[i]); + } + + entries.push_back(MapperStateEntry("", "Window")); + entries.push_back(MapperStateEntry("$412E", "Window Bank", _windowBank, MapperStateValueType::Number8)); + processNtControl(0x412F, _windowControl); + + entries.push_back(MapperStateEntry("", "PPU Mappings")); + for(int i = 0; i < 16; i++) { + entries.push_back(MapperStateEntry( + "$" + HexUtilities::ToHex(0x4130 + i) + "/" + HexUtilities::ToHex(0x4140 + i), + "CHR Bank " + std::to_string(i), + _chrBanks[i], + MapperStateValueType::Number16 + )); + } + + entries.push_back(MapperStateEntry("", "Scanline IRQ")); + entries.push_back(MapperStateEntry("$4150", "Target Scanline", _slIrqScanline, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4151/2", "Enabled", _slIrqEnabled)); + entries.push_back(MapperStateEntry("$4153", "Cycle Offset", _slIrqOffset, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4154", "Jitter Counter", _jitterCounter, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "CPU IRQ")); + entries.push_back(MapperStateEntry("$4158/9", "Reload Value", _cpuIrqReloadValue, MapperStateValueType::Number16)); + entries.push_back(MapperStateEntry("$415A.0", "Enabled", _cpuIrqEnabled)); + entries.push_back(MapperStateEntry("$415A.1", "Enable after Ack", _cpuIrqEnableAfterAck)); + entries.push_back(MapperStateEntry("$415A.2", "Ack after $4011 read", _cpuIrqAckOn4011)); + entries.push_back(MapperStateEntry("", "Counter", _cpuIrqCounter, MapperStateValueType::Number16)); + + entries.push_back(MapperStateEntry("", "FPGA RAM")); + entries.push_back(MapperStateEntry("$415C/D", "Address", _fpgaRamAddr, MapperStateValueType::Number16)); + entries.push_back(MapperStateEntry("$415E", "Increment", _fpgaRamInc, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("$4160", "Mapper Version", 0x20, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4161.0", "Wi-Fi IRQ Pending", _wifiIrqPending)); + entries.push_back(MapperStateEntry("$4161.6", "CPU IRQ Pending", _cpuIrqPending)); + entries.push_back(MapperStateEntry("$4161.7", "Scanline IRQ Pending", _slIrqPending)); + + entries.push_back(MapperStateEntry("", "IRQ/NMI Vectors")); + entries.push_back(MapperStateEntry("$416B.0", "NMI Redirection Enabled", _nmiVectorEnabled)); + entries.push_back(MapperStateEntry("$416B.1", "IRQ Redirection Enabled", _irqVectorEnabled)); + entries.push_back(MapperStateEntry("$416C/D", "NMI Vector", _nmiVectorAddr, MapperStateValueType::Number16)); + entries.push_back(MapperStateEntry("$416E/F", "IRQ Vector", _irqVectorAddr, MapperStateValueType::Number16)); + + entries.push_back(MapperStateEntry("", "Window Split Config")); + entries.push_back(MapperStateEntry("$4170.0-4", "Start Column", _windowX1, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4171.0-4", "End Column", _windowX2, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4172", "Start Scanline", _windowY1, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4173", "End Scanline", _windowY2, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4174.0-4", "Scroll X", _windowScrollX, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4175", "Scroll Y", _windowScrollX, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "Wi-Fi")); + entries.push_back(MapperStateEntry("$4190.0", "ESP Enabled", _espEnabled)); + entries.push_back(MapperStateEntry("$4190.1", "Wi-Fi IRQ Enabled", _wifiIrqEnabled)); + + entries.push_back(MapperStateEntry("$4191.6", "Data Ready", _dataReady)); + entries.push_back(MapperStateEntry("$4191.7", "Data Received", _dataReceived)); + entries.push_back(MapperStateEntry("$4192.7", "Data Sent", _dataSent)); + entries.push_back(MapperStateEntry("$4193", "Receive Destination Page", _recvDstAddr, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4194", "Send Source Page", _sendSrcAddr, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "Sprite Extended Data")); + for(int i = 0; i < 64; i++) { + entries.push_back(MapperStateEntry("$" + HexUtilities::ToHex(0x4200 + i), "Sprite #" + std::to_string(i), _spriteExtData[i], MapperStateValueType::Number8)); + } + entries.push_back(MapperStateEntry("$4240", "Sprite Extended Bank", _spriteExtBank, MapperStateValueType::Number8)); + + entries.push_back(MapperStateEntry("", "OAM Functions")); + entries.push_back(MapperStateEntry("$4241", "OAM Update Page", _oamSlowUpdatePage, MapperStateValueType::Number8)); + entries.push_back(MapperStateEntry("$4242", "OAM Ext. Update Page", _oamExtUpdatePage, MapperStateValueType::Number8)); + + //todo audio? + + return entries; +} + +bool Rainbow::HasExtendedAttributes(ExtModeConfig& cfg, uint8_t ntIndex) +{ + return cfg.Nametables[ntIndex].AttrExtMode; +} + +bool Rainbow::HasExtendedBackground(ExtModeConfig& cfg, uint8_t ntIndex) +{ + return cfg.Nametables[ntIndex].BgExtMode; +} + +bool Rainbow::HasExtendedSprites(ExtModeConfig& cfg) +{ + return cfg.SpriteExtMode; +} + +uint8_t Rainbow::DebugReadChr(ExtModeConfig& cfg, uint32_t addr) +{ + switch(cfg.ChrSource) { + default: + case 0: return _chrRomSize ? _chrRom[addr & (_chrRomSize - 1)] : 0; + case 1: return _chrRamSize ? _chrRam[addr & (_chrRamSize - 1)] : 0; + + case 2: + case 3: + return cfg.ExtRam[addr & 0x1FFF]; + } +} + +uint8_t Rainbow::GetExAttributePalette(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset) +{ + uint8_t extData = cfg.ExtRam[cfg.Nametables[ntIndex].SourceOffset + ntOffset]; + return (extData & 0xC0) >> 6; +} + +uint8_t Rainbow::GetExBackgroundChrData(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset, uint16_t chrAddr) +{ + uint8_t extData = cfg.ExtRam[cfg.Nametables[ntIndex].SourceOffset + ntOffset]; + uint32_t fetchAddr = (chrAddr & 0xFFF) | ((extData & 0x3F) << 12) | (cfg.BgExtBank << 18); + return DebugReadChr(cfg, fetchAddr); +} + +uint8_t Rainbow::GetExSpriteChrData(ExtModeConfig& cfg, uint8_t spriteIndex, uint16_t chrAddr) +{ + uint32_t addr; + if(_largeSprites) { + addr = (cfg.SpriteExtBank << 21) | (cfg.SpriteExtData[spriteIndex] << 13) | (chrAddr & 0x1FFF); + } else { + addr = (cfg.SpriteExtBank << 20) | (cfg.SpriteExtData[spriteIndex] << 12) | (chrAddr & 0xFFF); + } + return DebugReadChr(cfg, addr); +} + +ExtModeConfig Rainbow::GetExModeConfig() +{ + ExtModeConfig cfg = {}; + + for(int i = 0; i < 4; i++) { + cfg.Nametables[i].SourceOffset = _ntControl[i].FpgaRamSrc * 0x400; + cfg.Nametables[i].AttrExtMode = _ntControl[i].AttrExtMode; + cfg.Nametables[i].BgExtMode = _ntControl[i].BgExtMode; + cfg.Nametables[i].FillMode = _ntControl[i].FillMode; + } + + cfg.Nametables[4].SourceOffset = _windowControl.FpgaRamSrc * 0x400; + cfg.Nametables[4].AttrExtMode = _windowControl.AttrExtMode; + cfg.Nametables[4].BgExtMode = _windowControl.BgExtMode; + cfg.Nametables[4].FillMode = _windowControl.FillMode; + + cfg.WindowScrollX = _windowScrollX * 8; + cfg.WindowScrollY = _windowScrollY; + + memcpy(cfg.ExtRam, _mapperRam, 0x1000); + cfg.SpriteExtMode = _spriteExtMode; + cfg.BgExtBank = _bgExtModeOffset; + cfg.ChrSource = _chrSource; + + memcpy(cfg.SpriteExtData, _spriteExtData, sizeof(_spriteExtData)); + cfg.SpriteExtBank = _spriteExtBank; + + return cfg; +} + +void Rainbow::Serialize(Serializer& s) +{ + BaseMapper::Serialize(s); + + SVArray(_highBanks, 8); + SVArray(_lowBanks, 2); + SVArray(_chrBanks, 16); + SV(_fpgaRamBank); + SV(_highMode); + SV(_lowMode); + SV(_chrMode); + SV(_chrSource); + SV(_windowEnabled); + SV(_spriteExtMode); + SV(_bgExtModeOffset); + SVArray(_ntBanks, 4); + + for(int i = 0; i < 4; i++) { + SVI(_ntControl[i].AttrExtMode); + SVI(_ntControl[i].BgExtMode); + SVI(_ntControl[i].FillMode); + SVI(_ntControl[i].FpgaRamSrc); + SVI(_ntControl[i].Source); + } + + SV(_fillModeTileIndex); + SV(_fillModeAttrIndex); + + SV(_windowControl.AttrExtMode); + SV(_windowControl.BgExtMode); + SV(_windowControl.FillMode); + SV(_windowControl.FpgaRamSrc); + SV(_windowControl.Source); + + SV(_windowBank); + SV(_windowX1); + SV(_windowX2); + SV(_windowY1); + SV(_windowY2); + SV(_windowScrollX); + SV(_windowScrollY); + SV(_inWindow); + SV(_slIrqEnabled); + SV(_slIrqPending); + SV(_slIrqScanline); + SV(_slIrqOffset); + SV(_lastPpuReadAddr); + SV(_scanlineCounter); + SV(_ppuIdleCounter); + SV(_ntReadCounter); + SV(_ppuReadCounter); + SV(_inFrame); + SV(_inHBlank); + SV(_jitterCounter); + SV(_cpuIrqCounter); + SV(_cpuIrqReloadValue); + SV(_cpuIrqEnabled); + SV(_cpuIrqPending); + SV(_cpuIrqEnableAfterAck); + SV(_cpuIrqAckOn4011); + SV(_fpgaRamAddr); + SV(_fpgaRamInc); + SV(_nmiVectorEnabled); + SV(_irqVectorEnabled); + SV(_nmiVectorAddr); + SV(_irqVectorAddr); + SV(_overrideTileFetch); + SV(_extData); + SV(_ntFetchCounter); + SVArray(_spriteExtData, 64); + SVArray(_oamPosY, 64); + SVArray(_oamMappings, 8); + SV(_spriteExtBank); + SV(_largeSprites); + SV(_oamAddr); + SV(_oamExtUpdatePage); + SV(_oamSlowUpdatePage); + SVArray(_oamCode, 0x506); + SV(_oamCodeLocked); + + SV(_espEnabled); + SV(_wifiIrqEnabled); + SV(_wifiIrqPending); + SV(_dataSent); + SV(_dataReceived); + SV(_dataReady); + SV(_sendSrcAddr); + SV(_recvDstAddr); + + SV(_prgFlash); + SV(_chrFlash); + + SV(_audio); +} diff --git a/Core/NES/Mappers/Homebrew/Rainbow.h b/Core/NES/Mappers/Homebrew/Rainbow.h new file mode 100644 index 000000000..3e8e371cb --- /dev/null +++ b/Core/NES/Mappers/Homebrew/Rainbow.h @@ -0,0 +1,184 @@ +#pragma once +#include "pch.h" +#include "NES/BaseMapper.h" +#include "NES/Debugger/IExtModeMapperDebug.h" + +class RainbowAudio; +class FlashS29; + +class Rainbow : public BaseMapper, public IExtModeMapperDebug +{ +private: + struct NtControl + { + bool AttrExtMode; + bool BgExtMode; + uint8_t FpgaRamSrc; + bool FillMode; + uint8_t Source; + + uint8_t ToByte() + { + return ( + ((uint8_t)AttrExtMode << 0) | + ((uint8_t)BgExtMode << 1) | + (FpgaRamSrc << 2) | + ((uint8_t)FillMode << 5) | + (Source << 6) + ); + } + }; + + unique_ptr _audio; + unique_ptr _prgFlash; + unique_ptr _chrFlash; + + vector _orgPrgRom; + vector _orgChrRom; + + uint16_t _highBanks[8] = {}; + uint16_t _lowBanks[2] = {}; + uint16_t _chrBanks[16] = {}; + + uint8_t _fpgaRamBank = 0; + + uint8_t _highMode = 0; + uint8_t _lowMode = 0; + uint8_t _chrMode = 0; + uint8_t _chrSource = 0; + bool _windowEnabled = false; + bool _spriteExtMode = false; + uint8_t _bgExtModeOffset = 0; + + uint8_t _ntBanks[4] = {}; + NtControl _ntControl[4] = {}; + + uint8_t _fillModeTileIndex = 0; + uint8_t _fillModeAttrIndex = 0; + + NtControl _windowControl = {}; + uint8_t _windowBank = 0; + uint8_t _windowX1 = 0; + uint8_t _windowX2 = 0; + uint8_t _windowY1 = 0; + uint8_t _windowY2 = 0; + uint8_t _windowScrollX = 0; + uint8_t _windowScrollY = 0; + bool _inWindow = false; + + bool _slIrqEnabled = false; + bool _slIrqPending = false; + uint8_t _slIrqScanline = 0; + uint8_t _slIrqOffset = 0; + + uint16_t _lastPpuReadAddr = 0; + int16_t _scanlineCounter = 0; + uint8_t _ppuIdleCounter = 0; + uint8_t _ntReadCounter = 0; + uint8_t _ppuReadCounter = 0; + + bool _inFrame = false; + bool _inHBlank = false; + uint8_t _jitterCounter = 0; + + uint16_t _cpuIrqCounter = 0; + uint16_t _cpuIrqReloadValue = 0; + bool _cpuIrqEnabled = false; + bool _cpuIrqPending = false; + bool _cpuIrqEnableAfterAck = false; + bool _cpuIrqAckOn4011 = false; + + uint16_t _fpgaRamAddr = 0; + uint8_t _fpgaRamInc = 0; + + bool _nmiVectorEnabled = false; + bool _irqVectorEnabled = false; + uint16_t _nmiVectorAddr = 0; + uint16_t _irqVectorAddr = 0; + + bool _overrideTileFetch = false; + uint8_t _extData = 0; + uint8_t _ntFetchCounter = 0; + + uint8_t _spriteExtData[64] = {}; + uint8_t _oamPosY[64] = {}; + uint8_t _oamMappings[8] = {}; + uint8_t _spriteExtBank = 0; + bool _largeSprites = false; + uint8_t _oamAddr = 0; + + uint8_t _oamExtUpdatePage = 0; + uint8_t _oamSlowUpdatePage = 0; + uint8_t _oamCode[0x506] = {}; + bool _oamCodeLocked = false; + + bool _espEnabled = false; + bool _wifiIrqEnabled = false; + bool _wifiIrqPending = false; + bool _dataSent = false; + bool _dataReceived = false; + bool _dataReady = false; + uint8_t _sendSrcAddr = 0; + uint8_t _recvDstAddr = 0; + + uint8_t ReadChr(uint32_t addr); + void UpdateInWindowFlag(); + void UpdateIrqStatus(); + void AckCpuIrq(); + void ProcessSpriteEval(); + void DetectScanlineStart(uint16_t addr); + + void GenerateOamClear(); + void GenerateExtUpdate(); + void GenerateOamSlowUpdate(); + + PrgMemoryType GetWorkRamType(); + void SelectHighBank(uint16_t start, uint16_t size, uint8_t reg); + void SelectLowBank(uint16_t start, uint16_t size, uint8_t reg); + void SelectChrBank(uint16_t start, uint16_t size, uint8_t reg); + void UpdateState(); + + void ApplySaveData(); + +protected: + uint16_t GetPrgPageSize() override { return 0x1000; } + uint16_t GetChrPageSize() override { return 0x200; } + uint32_t GetMapperRamSize() override { return 0x2000; } + uint16_t RegisterStartAddress() override { return 0x4100; } + uint16_t RegisterEndAddress() override { return 0x4785; } + bool AllowRegisterRead() override { return true; } + bool EnableCpuClockHook() override { return true; } + bool EnableCustomVramRead() override { return true; } + + void InitMapper() override; + void SaveBattery() override; + void Reset(bool softReset) override; + void OnAfterResetPowerOn() override; + + uint8_t MapperReadVram(uint16_t addr, MemoryOperationType memoryOperationType) override; + void MapperWriteVram(uint16_t addr, uint8_t value) override; + +public: + Rainbow(); + + void WriteRam(uint16_t addr, uint8_t value) override; + uint8_t ReadRam(uint16_t addr) override; + + void ProcessCpuClock() override; + + uint8_t ReadRegister(uint16_t addr) override; + void WriteRegister(uint16_t addr, uint8_t value) override; + + //Debugger + vector GetMapperStateEntries() override; + bool HasExtendedAttributes(ExtModeConfig& cfg, uint8_t ntIndex) override; + bool HasExtendedBackground(ExtModeConfig& cfg, uint8_t ntIndex) override; + bool HasExtendedSprites(ExtModeConfig& cfg) override; + uint8_t DebugReadChr(ExtModeConfig& cfg, uint32_t addr); + uint8_t GetExAttributePalette(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset) override; + uint8_t GetExBackgroundChrData(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset, uint16_t chrAddr) override; + uint8_t GetExSpriteChrData(ExtModeConfig& cfg, uint8_t spriteIndex, uint16_t chrAddr) override; + ExtModeConfig GetExModeConfig() override; + + void Serialize(Serializer& s) override; +}; \ No newline at end of file diff --git a/Core/NES/Mappers/Homebrew/RainbowAudio.h b/Core/NES/Mappers/Homebrew/RainbowAudio.h new file mode 100644 index 000000000..d9f9355f1 --- /dev/null +++ b/Core/NES/Mappers/Homebrew/RainbowAudio.h @@ -0,0 +1,91 @@ +#pragma once +#include "NES/APU/BaseExpansionAudio.h" +#include "NES/Mappers/Audio/Vrc6Pulse.h" +#include "NES/Mappers/Audio/Vrc6Saw.h" +#include "NES/APU/NesApu.h" +#include "NES/NesConsole.h" +#include "Utilities/Serializer.h" + +class RainbowAudio : public BaseExpansionAudio +{ +private: + Vrc6Pulse _pulse1; + Vrc6Pulse _pulse2; + Vrc6Saw _saw; + bool _outputExpPin6 = false; + bool _outputExpPin9 = false; + bool _outputTo4011 = false; + uint8_t _volume = 0; + uint8_t _lastOutput = 0; + +protected: + void Serialize(Serializer& s) override + { + SV(_pulse1); + SV(_pulse2); + SV(_saw); + SV(_outputExpPin6); + SV(_outputExpPin9); + SV(_outputTo4011); + SV(_volume); + SV(_lastOutput); + } + + void ClockAudio() override + { + _pulse1.Clock(); + _pulse2.Clock(); + _saw.Clock(); + + uint8_t outputLevel = _pulse1.GetVolume() + _pulse2.GetVolume() + _saw.GetVolume(); + if(_outputExpPin6 || _outputExpPin9) { + _console->GetApu()->AddExpansionAudioDelta(AudioChannel::VRC6, (int16_t)outputLevel - (int16_t)_lastOutput); + } + _lastOutput = outputLevel; + } + +public: + RainbowAudio(NesConsole* console) : BaseExpansionAudio(console) + { + Reset(); + } + + void Reset() + { + _lastOutput = 0; + } + + uint8_t GetLastOutput() + { + return _lastOutput; + } + + void WriteRegister(uint16_t addr, uint8_t value) + { + addr &= 0x0F; + + switch(addr) { + case 0x00: case 0x01: case 0x02: + _pulse1.WriteReg(addr, value); + break; + + case 0x03: case 0x04: case 0x05: + _pulse2.WriteReg(addr - 0x03, value); + break; + + case 0x06: case 0x07: case 0x08: + _saw.WriteReg(addr - 0x06, value); + break; + + case 0x09: + _outputExpPin6 = value & 0x01; + _outputExpPin9 = value & 0x02; + _outputTo4011 = value & 0x04; + break; + + case 0x0A: + _volume = value & 0x0F; + break; + } + } +}; \ No newline at end of file diff --git a/Core/NES/Mappers/Nintendo/MMC5.h b/Core/NES/Mappers/Nintendo/MMC5.h index ec5d5617c..2e993f2d9 100644 --- a/Core/NES/Mappers/Nintendo/MMC5.h +++ b/Core/NES/Mappers/Nintendo/MMC5.h @@ -4,19 +4,18 @@ #include "NES/NesConsole.h" #include "NES/NesCpu.h" #include "NES/Mappers/Audio/Mmc5Audio.h" +#include "NES/Debugger/IExtModeMapperDebug.h" #include "NES/Mappers/Nintendo/Mmc5MemoryHandler.h" #include "Utilities/HexUtilities.h" -class MMC5 : public BaseMapper +class MMC5 : public BaseMapper, public IExtModeMapperDebug { private: - static constexpr int ExRamSize = 0x400; - unique_ptr _audio; unique_ptr _mmc5MemoryHandler; - uint8_t* _fillNametable; - uint8_t* _emptyNametable; + uint8_t* _fillNametable = nullptr; + uint8_t* _emptyNametable = nullptr; uint8_t _prgRamProtect1 = 0; uint8_t _prgRamProtect2 = 0; @@ -93,34 +92,18 @@ class MMC5 : public BaseMapper // 1x 8kb : 0 0 0 0 - - - - // 2x 8kb : 0 0 0 0 1 1 1 1 // 1x 32kb : 0 1 2 3 - - - - - int32_t realWorkRamSize = _workRamSize - (HasBattery() ? 0 : MMC5::ExRamSize); - int32_t realSaveRamSize = _saveRamSize - (!HasBattery() ? 0 : MMC5::ExRamSize); if(IsNes20() || _romInfo.IsInDatabase) { memoryType = PrgMemoryType::WorkRam; - if(HasBattery() && (bankNumber <= 3 || realSaveRamSize > 0x2000)) { + if(HasBattery() && (bankNumber <= 3 || _saveRamSize > 0x2000)) { memoryType = PrgMemoryType::SaveRam; } - if(realSaveRamSize + realWorkRamSize != 0x4000 && bankNumber >= 4) { + if(_saveRamSize + _workRamSize != 0x4000 && bankNumber >= 4) { //When not 2x 8kb (=16kb), banks 4/5/6/7 select the empty socket and return open bus accessType = MemoryAccessType::NoAccess; } } else { memoryType = HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam; - } - - if(memoryType == PrgMemoryType::WorkRam) { - //Properly mirror work ram (by ignoring the extra 1kb ExRAM section) - bankNumber &= (realWorkRamSize / 0x2000) - 1; - if(_workRamSize == MMC5::ExRamSize) { - accessType = MemoryAccessType::NoAccess; - } - } else if(memoryType == PrgMemoryType::SaveRam) { - //Properly mirror work ram (by ignoring the extra 1kb ExRAM section) - bankNumber &= (realSaveRamSize / 0x2000) - 1; - if(_saveRamSize == MMC5::ExRamSize) { - accessType = MemoryAccessType::NoAccess; - } } } else { accessType = MemoryAccessType::Read; @@ -253,8 +236,7 @@ class MMC5 : public BaseMapper SetNametable(i, nametableId); } else if(nametableId == 2) { if(_extendedRamMode <= 1) { - uint8_t* source = HasBattery() ? (_saveRam + (_saveRamSize - MMC5::ExRamSize)) : (_workRam + (_workRamSize - MMC5::ExRamSize)); - SetPpuMemoryMapping(0x2000 + i * 0x400, 0x2000 + i * 0x400 + 0x3FF, source, 0, MMC5::ExRamSize, MemoryAccessType::ReadWrite); + SetPpuMemoryMapping(0x2000 + i * 0x400, 0x2000 + i * 0x400 + 0x3FF, ChrMemoryType::MapperRam, 0, MemoryAccessType::ReadWrite); } else { SetPpuMemoryMapping(0x2000 + i * 0x400, 0x2000 + i * 0x400 + 0x3FF, _emptyNametable, 0, BaseMapper::NametableSize, MemoryAccessType::Read); } @@ -281,11 +263,7 @@ class MMC5 : public BaseMapper accessType = MemoryAccessType::Read; } - if(HasBattery()) { - SetCpuMemoryMapping(0x5C00, 0x5FFF, PrgMemoryType::SaveRam, _saveRamSize - MMC5::ExRamSize, accessType); - } else { - SetCpuMemoryMapping(0x5C00, 0x5FFF, PrgMemoryType::WorkRam, _workRamSize - MMC5::ExRamSize, accessType); - } + SetCpuMemoryMapping(0x5C00, 0x5FFF, PrgMemoryType::MapperRam, 0, accessType); SetNametableMapping(_nametableMapping); } @@ -306,6 +284,7 @@ class MMC5 : public BaseMapper protected: uint16_t GetPrgPageSize() override { return 0x2000; } uint16_t GetChrPageSize() override { return 0x400; } + uint32_t GetMapperRamSize() override { return 0x400; } uint16_t RegisterStartAddress() override { return 0x5000; } uint16_t RegisterEndAddress() override { return 0x5206; } uint32_t GetSaveRamPageSize() override { return 0x2000; } @@ -327,12 +306,6 @@ class MMC5 : public BaseMapper //Emulate as if a single 64k block of work/save ram existed size = _romInfo.HasBattery ? 0x10000 : 0; } - - if(HasBattery()) { - //If there's a battery on the board, exram gets saved, too. - size += MMC5::ExRamSize; - } - return size; } @@ -347,10 +320,6 @@ class MMC5 : public BaseMapper //Emulate as if a single 64k block of work/save ram existed (+ 1kb of ExRAM) size = (_romInfo.HasBattery ? 0 : 0x10000); } - if(!HasBattery()) { - size += MMC5::ExRamSize; - } - return size; } @@ -393,6 +362,27 @@ class MMC5 : public BaseMapper _console->GetMemoryManager()->RegisterWriteHandler(_mmc5MemoryHandler.get(), 0x2000, 0x2007); } + void LoadBattery() override + { + if(HasBattery() && _saveRamSize > 0) { + //Load EXRAM and save ram from the same file + vector data(_saveRamSize + _mapperRamSize); + _emu->GetBatteryManager()->LoadBattery(".sav", _saveRam, _saveRamSize + _mapperRamSize); + memcpy(_saveRam, data.data(), _saveRamSize); + memcpy(_mapperRam, data.data()+_saveRamSize, _mapperRamSize); + } + } + + void SaveBattery() override + { + if(HasBattery()) { + //Save EXRAM and save ram to the same file + vector data(_saveRam, _saveRam + _saveRamSize); + data.insert(data.end(), _mapperRam, _mapperRam + _mapperRamSize); + _emu->GetBatteryManager()->SaveBattery(".sav", data.data(), (uint32_t)data.size()); + } + } + void Serialize(Serializer& s) override { BaseMapper::Serialize(s); @@ -681,31 +671,52 @@ class MMC5 : public BaseMapper } public: - bool IsExtendedAttributes() + bool HasExtendedAttributes(ExtModeConfig& cfg, uint8_t ntIndex) override + { + return cfg.Nametables[0].AttrExtMode; + } + + bool HasExtendedBackground(ExtModeConfig& cfg, uint8_t ntIndex) override { - return _extendedRamMode == 1; + if(ntIndex == 4) { + return true; + } + + return cfg.Nametables[0].BgExtMode; } - uint8_t GetExAttributeNtPalette(uint16_t ntAddr) + uint8_t GetExAttributePalette(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset) override { - ntAddr &= 0x3FF; - uint8_t value = InternalReadRam(0x5C00 + ntAddr); + uint8_t value = cfg.ExtRam[ntOffset]; return (value & 0xC0) >> 6; } - uint32_t GetExAttributeAbsoluteTileAddr(uint16_t ntAddr, uint16_t chrAddr) + uint8_t GetExBackgroundChrData(ExtModeConfig& cfg, uint8_t ntIndex, uint16_t ntOffset, uint16_t chrAddr) override { - ntAddr &= 0x3FF; - uint8_t value = InternalReadRam(0x5C00 + ntAddr); - - //"The pattern fetches ignore the standard CHR banking bits, and instead use the top two bits of $5130 and the bottom 6 bits from Expansion RAM to choose a 4KB bank to select the tile from." - uint16_t chrBank = (value & 0x3F) | (_chrUpperBits << 6); - - return (chrBank << 12) + (chrAddr & 0xFFF); + if(ntIndex == 4) { + //Split mode + return ReadFromChr((cfg.Nametables[4].SourceOffset << 12) + (chrAddr & 0xFFF)); + } else { + uint8_t value = cfg.ExtRam[ntOffset]; + uint16_t chrBank = (value & 0x3F) | (cfg.BgExtBank << 6); + uint32_t addr = (chrBank << 12) + (chrAddr & 0xFFF); + return ReadFromChr(addr); + } } - uint8_t GetExAttributeTileData(uint16_t ntAddr, uint16_t chrAddr) + ExtModeConfig GetExModeConfig() override { - return ReadFromChr(GetExAttributeAbsoluteTileAddr(ntAddr, chrAddr)); + ExtModeConfig cfg = {}; + + cfg.Nametables[0].AttrExtMode = _extendedRamMode == 1; + cfg.Nametables[0].BgExtMode = _extendedRamMode == 1; + cfg.BgExtBank = _chrUpperBits; + + cfg.Nametables[4].SourceOffset = _verticalSplitBank; + cfg.WindowScrollY = _verticalSplitScroll; + + memcpy(cfg.ExtRam, _mapperRam, _mapperRamSize); + + return cfg; } }; diff --git a/Core/NES/NesMemoryManager.cpp b/Core/NES/NesMemoryManager.cpp index 098d4c7cb..be51ad7ad 100644 --- a/Core/NES/NesMemoryManager.cpp +++ b/Core/NES/NesMemoryManager.cpp @@ -60,7 +60,7 @@ void NesMemoryManager::InitializeMemoryHandlers(INesMemoryHandler** memoryHandle { for(uint16_t address : *addresses) { if(!allowOverride && memoryHandlers[address] != &_openBusHandler && memoryHandlers[address] != handler) { - throw std::runtime_error("Not supported"); + throw std::runtime_error("Can't override existing mapping"); } memoryHandlers[address] = handler; } @@ -77,11 +77,18 @@ void NesMemoryManager::RegisterIODevice(INesMemoryHandler*handler) void NesMemoryManager::RegisterWriteHandler(INesMemoryHandler* handler, uint32_t start, uint32_t end) { - for(uint32_t i = start; i < end; i++) { + for(uint32_t i = start; i <= end; i++) { _ramWriteHandlers[i] = handler; } } +void NesMemoryManager::RegisterReadHandler(INesMemoryHandler* handler, uint32_t start, uint32_t end) +{ + for(uint32_t i = start; i <= end; i++) { + _ramReadHandlers[i] = handler; + } +} + void NesMemoryManager::UnregisterIODevice(INesMemoryHandler*handler) { MemoryRanges ranges; diff --git a/Core/NES/NesMemoryManager.h b/Core/NES/NesMemoryManager.h index 4fefcc898..222b56286 100644 --- a/Core/NES/NesMemoryManager.h +++ b/Core/NES/NesMemoryManager.h @@ -45,6 +45,7 @@ class NesMemoryManager : public ISerializable void Reset(bool softReset); void RegisterIODevice(INesMemoryHandler* handler); void RegisterWriteHandler(INesMemoryHandler* handler, uint32_t start, uint32_t end); + void RegisterReadHandler(INesMemoryHandler* handler, uint32_t start, uint32_t end); void UnregisterIODevice(INesMemoryHandler* handler); uint8_t DebugRead(uint16_t addr); diff --git a/Core/NES/NesTypes.h b/Core/NES/NesTypes.h index 6c92c772e..1f82a7a0e 100644 --- a/Core/NES/NesTypes.h +++ b/Core/NES/NesTypes.h @@ -59,6 +59,7 @@ enum class PrgMemoryType PrgRom, SaveRam, WorkRam, + MapperRam, }; enum class ChrMemoryType @@ -66,7 +67,8 @@ enum class ChrMemoryType Default, ChrRom, ChrRam, - NametableRam + NametableRam, + MapperRam, }; enum MemoryAccessType @@ -165,7 +167,7 @@ struct CartridgeState bool HasBattery = false; uint32_t CustomEntryCount = 0; - MapperStateEntry CustomEntries[100] = {}; + MapperStateEntry CustomEntries[200] = {}; }; struct PPUStatusFlags diff --git a/Core/PCE/Debugger/PceVdcTools.cpp b/Core/PCE/Debugger/PceVdcTools.cpp index 812f2f207..e6cf3106e 100644 --- a/Core/PCE/Debugger/PceVdcTools.cpp +++ b/Core/PCE/Debugger/PceVdcTools.cpp @@ -29,7 +29,7 @@ FrameInfo PceVdcTools::GetTilemapSize(GetTilemapOptions options, BaseState& base } } -DebugTilemapTileInfo PceVdcTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo PceVdcTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -61,7 +61,7 @@ DebugTilemapTileInfo PceVdcTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uin return result; } -DebugTilemapInfo PceVdcTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo PceVdcTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { PceVdcState& state = options.Layer == 0 ? ((PceVideoState&)baseState).Vdc : ((PceVideoState&)baseState).Vdc2; @@ -296,7 +296,7 @@ void PceVdcTools::InternalGetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* sprit } } -void PceVdcTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void PceVdcTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { PceVdcState& state = ((PceVideoState&)baseState).Vdc; for(int i = 0, len = _console->IsSuperGrafx() ? 128 : 64; i < len; i++) { @@ -307,7 +307,7 @@ void PceVdcTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& base GetSpritePreview(options, baseState, outBuffer, spritePreviews, palette, screenPreview); } -DebugSpritePreviewInfo PceVdcTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState) +DebugSpritePreviewInfo PceVdcTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState) { PceVdcState& state = ((PceVideoState&)baseState).Vdc; diff --git a/Core/PCE/Debugger/PceVdcTools.h b/Core/PCE/Debugger/PceVdcTools.h index bf4b71d4f..f2047e160 100644 --- a/Core/PCE/Debugger/PceVdcTools.h +++ b/Core/PCE/Debugger/PceVdcTools.h @@ -27,12 +27,12 @@ class PceVdcTools final : public PpuTools void SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle) override; - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/SMS/Debugger/SmsVdpTools.cpp b/Core/SMS/Debugger/SmsVdpTools.cpp index b474987d6..11b98a596 100644 --- a/Core/SMS/Debugger/SmsVdpTools.cpp +++ b/Core/SMS/Debugger/SmsVdpTools.cpp @@ -19,7 +19,7 @@ FrameInfo SmsVdpTools::GetTilemapSize(GetTilemapOptions options, BaseState& base return { isTextMode ? 240u : 256u, state.NametableHeight }; } -DebugTilemapInfo SmsVdpTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo SmsVdpTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { SmsVdpState& state = (SmsVdpState&)baseState; @@ -149,7 +149,7 @@ DebugTilemapInfo SmsVdpTools::GetTilemap(GetTilemapOptions options, BaseState& b return result; } -DebugTilemapTileInfo SmsVdpTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo SmsVdpTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -277,7 +277,7 @@ void SmsVdpTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& b } } -DebugSpritePreviewInfo SmsVdpTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState) +DebugSpritePreviewInfo SmsVdpTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState) { SmsVdpState& state = (SmsVdpState&)baseState; DebugSpritePreviewInfo info = {}; @@ -391,7 +391,7 @@ void SmsVdpTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview } } -void SmsVdpTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void SmsVdpTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { SmsVdpState& state = (SmsVdpState&)baseState; int spriteCount = state.UseMode4 ? 64 : 32; diff --git a/Core/SMS/Debugger/SmsVdpTools.h b/Core/SMS/Debugger/SmsVdpTools.h index 657d31fbc..63f0a2607 100644 --- a/Core/SMS/Debugger/SmsVdpTools.h +++ b/Core/SMS/Debugger/SmsVdpTools.h @@ -18,12 +18,12 @@ class SmsVdpTools final : public PpuTools public: SmsVdpTools(Debugger* debugger, Emulator *emu, SmsConsole* console); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/SNES/Debugger/SnesPpuTools.cpp b/Core/SNES/Debugger/SnesPpuTools.cpp index ae6a32dd1..ff5801a9e 100644 --- a/Core/SNES/Debugger/SnesPpuTools.cpp +++ b/Core/SNES/Debugger/SnesPpuTools.cpp @@ -40,7 +40,7 @@ void SnesPpuTools::SetPpuRowBuffers(uint16_t scanline, uint16_t xStart, uint16_t } } -DebugTilemapInfo SnesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo SnesPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { SnesPpuState& state = (SnesPpuState&)baseState; FrameInfo outputSize = GetTilemapSize(options, state); @@ -229,10 +229,10 @@ static constexpr uint8_t _oamSizes[8][2][2] = { { { 2, 4 }, { 4, 4 } } //16x32 + 32x32 }; -void SnesPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& baseState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer) +void SnesPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer) { SnesPpuState& state = (SnesPpuState&)baseState; - DebugSpritePreviewInfo size = GetSpritePreviewInfo(options, state); + DebugSpritePreviewInfo size = GetSpritePreviewInfo(options, state, ppuToolsState); uint32_t bgColor = GetSpriteBackgroundColor(options.Background, palette, false); std::fill(outBuffer, outBuffer + size.Width * size.Height, GetSpriteBackgroundColor(options.Background, palette, true)); @@ -393,7 +393,7 @@ void SnesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePrevie } } -void SnesPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void SnesPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { SnesPpuState& state = (SnesPpuState&)baseState; for(int i = 0; i < 128; i++) { @@ -401,7 +401,7 @@ void SnesPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& bas GetSpriteInfo(outBuffer[i], spritePreviews + (i * _spritePreviewSize), i, options, state, vram, oamRam, palette); } - GetSpritePreview(options, baseState, outBuffer, spritePreviews, palette, screenPreview); + GetSpritePreview(options, baseState, ppuToolsState, outBuffer, spritePreviews, palette, screenPreview); } FrameInfo SnesPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& baseState) @@ -444,7 +444,7 @@ FrameInfo SnesPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& bas return size; } -DebugTilemapTileInfo SnesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo SnesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -514,7 +514,7 @@ DebugTilemapTileInfo SnesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, ui return result; } -DebugSpritePreviewInfo SnesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState) +DebugSpritePreviewInfo SnesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState) { SnesPpuState& state = (SnesPpuState&)baseState; diff --git a/Core/SNES/Debugger/SnesPpuTools.h b/Core/SNES/Debugger/SnesPpuTools.h index cb1c4d5a2..79f7554ae 100644 --- a/Core/SNES/Debugger/SnesPpuTools.h +++ b/Core/SNES/Debugger/SnesPpuTools.h @@ -34,7 +34,7 @@ class SnesPpuTools final : public PpuTools template void RenderTilemap(GetTilemapOptions& options, int rowCount, LayerConfig& layer, int columnCount, uint8_t* vram, int tileHeight, int tileWidth, bool largeTileHeight, bool largeTileWidth, uint8_t bpp, uint32_t* outBuffer, FrameInfo outputSize, const uint32_t* palette, uint16_t basePaletteOffset); DebugTilemapInfo RenderScreenView(uint8_t layer, uint32_t* outBuffer); - void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer); + void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState, DebugSpriteInfo* sprites, uint32_t* spritePreviews, uint32_t* palette, uint32_t* outBuffer); public: SnesPpuTools(Debugger* debugger, Emulator *emu); @@ -44,12 +44,12 @@ class SnesPpuTools final : public PpuTools void SetPpuScanlineState(uint16_t scanline, uint8_t mode, int32_t mode7startX, int32_t mode7startY, int32_t mode7endX, int32_t mode7endY); void SetPpuRowBuffers(uint16_t scanline, uint16_t xStart, uint16_t xEnd, uint16_t mainScreenRowBuffer[256], uint16_t subScreenRowBuffer[256]); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/Core/Shared/MemoryType.h b/Core/Shared/MemoryType.h index 6475d0c36..9ee0eb0f6 100644 --- a/Core/Shared/MemoryType.h +++ b/Core/Shared/MemoryType.h @@ -48,6 +48,7 @@ enum class MemoryType NesWorkRam, NesSaveRam, NesNametableRam, + NesMapperRam, NesSpriteRam, NesSecondarySpriteRam, NesPaletteRam, diff --git a/Core/WS/Debugger/WsPpuTools.cpp b/Core/WS/Debugger/WsPpuTools.cpp index cf7af4e95..92d92015b 100644 --- a/Core/WS/Debugger/WsPpuTools.cpp +++ b/Core/WS/Debugger/WsPpuTools.cpp @@ -17,7 +17,7 @@ FrameInfo WsPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& baseS return { 256, 256 }; } -DebugTilemapInfo WsPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) +DebugTilemapInfo WsPpuTools::GetTilemap(GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) { WsPpuState& state = (WsPpuState&)baseState; @@ -115,7 +115,7 @@ DebugTilemapInfo WsPpuTools::GetTilemap(GetTilemapOptions options, BaseState& ba return result; } -DebugTilemapTileInfo WsPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) +DebugTilemapTileInfo WsPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugTilemapTileInfo result = {}; @@ -200,7 +200,7 @@ void WsPpuTools::GetSpritePreview(GetSpritePreviewOptions options, BaseState& ba } } -DebugSpritePreviewInfo WsPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState) +DebugSpritePreviewInfo WsPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState) { DebugSpritePreviewInfo info = {}; info.Height = 256; @@ -318,7 +318,7 @@ void WsPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t* spritePreview, } } -void WsPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) +void WsPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) { WsPpuState& state = (WsPpuState&)baseState; int spriteCount = 128; diff --git a/Core/WS/Debugger/WsPpuTools.h b/Core/WS/Debugger/WsPpuTools.h index 33f3d1b19..d3c9508ab 100644 --- a/Core/WS/Debugger/WsPpuTools.h +++ b/Core/WS/Debugger/WsPpuTools.h @@ -18,12 +18,12 @@ class WsPpuTools final : public PpuTools public: WsPpuTools(Debugger* debugger, Emulator *emu, WsConsole* console); - DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; + DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t *outBuffer) override; FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) override; - DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState) override; + DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) override; - DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override; - void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; + DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) override; + void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) override; DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override; void SetPaletteColor(int32_t colorIndex, uint32_t color) override; diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 419ca37d2..39c8e73f4 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -159,12 +159,12 @@ extern "C" DllExport void __stdcall GetTileView(CpuType cpuType, GetTileViewOptions options, uint8_t* source, uint32_t srcSize, uint32_t* colors, uint32_t* buffer) { WithToolVoid(GetPpuTools(cpuType), GetTileView(options, source, srcSize, colors, buffer)); } DllExport void __stdcall GetPpuToolsState(CpuType cpuType, BaseState& state) { return WithToolVoid(GetPpuTools(cpuType), GetPpuToolsState(state)); } - DllExport DebugTilemapInfo __stdcall GetTilemap(CpuType cpuType, GetTilemapOptions options, BaseState& state, uint8_t* vram, uint32_t* palette, uint32_t* outputBuffer) { return WithTool(DebugTilemapInfo, GetPpuTools(cpuType), GetTilemap(options, state, vram, palette, outputBuffer)); } + DllExport DebugTilemapInfo __stdcall GetTilemap(CpuType cpuType, GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outputBuffer) { return WithTool(DebugTilemapInfo, GetPpuTools(cpuType), GetTilemap(options, state, ppuToolsState, vram, palette, outputBuffer)); } DllExport FrameInfo __stdcall GetTilemapSize(CpuType cpuType, GetTilemapOptions options, BaseState& state) { return WithTool(FrameInfo, GetPpuTools(cpuType), GetTilemapSize(options, state)); } - DllExport DebugTilemapTileInfo __stdcall GetTilemapTileInfo(uint32_t x, uint32_t y, CpuType cpuType, GetTilemapOptions options, uint8_t* vram, BaseState& state) { return WithTool(DebugTilemapTileInfo, GetPpuTools(cpuType), GetTilemapTileInfo(x, y, vram, options, state)); } + DllExport DebugTilemapTileInfo __stdcall GetTilemapTileInfo(uint32_t x, uint32_t y, CpuType cpuType, GetTilemapOptions options, uint8_t* vram, BaseState& state, BaseState& ppuToolsState) { return WithTool(DebugTilemapTileInfo, GetPpuTools(cpuType), GetTilemapTileInfo(x, y, vram, options, state, ppuToolsState)); } - DllExport DebugSpritePreviewInfo __stdcall GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state) { return WithTool(DebugSpritePreviewInfo, GetPpuTools(cpuType), GetSpritePreviewInfo(options, state)); } - DllExport void __stdcall GetSpriteList(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo sprites[], uint32_t* spritePreviews, uint32_t* screenPreview) { WithToolVoid(GetPpuTools(cpuType), GetSpriteList(options, state, vram, oamRam, palette, sprites, spritePreviews, screenPreview)); } + DllExport DebugSpritePreviewInfo __stdcall GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) { return WithTool(DebugSpritePreviewInfo, GetPpuTools(cpuType), GetSpritePreviewInfo(options, state, ppuToolsState)); } + DllExport void __stdcall GetSpriteList(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo sprites[], uint32_t* spritePreviews, uint32_t* screenPreview) { WithToolVoid(GetPpuTools(cpuType), GetSpriteList(options, state, ppuToolsState, vram, oamRam, palette, sprites, spritePreviews, screenPreview)); } DllExport DebugPaletteInfo __stdcall GetPaletteInfo(CpuType cpuType, GetPaletteInfoOptions options) { return WithTool(DebugPaletteInfo, GetPpuTools(cpuType), GetPaletteInfo(options)); } DllExport void __stdcall SetPaletteColor(CpuType cpuType, int32_t colorIndex, uint32_t color) { WithToolVoid(GetPpuTools(cpuType), SetPaletteColor(colorIndex, color)); } diff --git a/UI/Config/IntegrationConfig.cs b/UI/Config/IntegrationConfig.cs index 00a63399a..8de02ba68 100644 --- a/UI/Config/IntegrationConfig.cs +++ b/UI/Config/IntegrationConfig.cs @@ -54,6 +54,7 @@ public bool IsMemoryTypeImportEnabled(MemoryType memType) case MemoryType.GbVideoRam: case MemoryType.GbSpriteRam: case MemoryType.NesNametableRam: + case MemoryType.NesMapperRam: case MemoryType.NesSpriteRam: case MemoryType.NesSecondarySpriteRam: case MemoryType.NesPaletteRam: diff --git a/UI/Debugger/Documentation/LuaDocumentation.json b/UI/Debugger/Documentation/LuaDocumentation.json index c0a58cc84..28ef3513e 100644 --- a/UI/Debugger/Documentation/LuaDocumentation.json +++ b/UI/Debugger/Documentation/LuaDocumentation.json @@ -494,7 +494,8 @@ { "name": "nes", "description": "NES - Main CPU - 2A03 (6502)" }, { "name": "pce", "description": "PC Engine - Main CPU - HuC6280" }, { "name": "sms", "description": "SMS - Main CPU - Z80" }, - { "name": "gba", "description": "GBA - Main CPU - ARM7TDMI" } + { "name": "gba", "description": "GBA - Main CPU - ARM7TDMI" }, + { "name": "ws", "description": "WS - Main CPU - NEC V30MZ" }, ] }, { @@ -540,6 +541,7 @@ { "name": "pceMemory", "description": "PC Engine - CPU memory" }, { "name": "smsMemory", "description": "SMS - CPU memory" }, { "name": "gbaMemory", "description": "GBA - CPU memory" }, + { "name": "wsMemory", "description": "WS - CPU memory" }, { "name": "snesDebug", "description": "SNES - S-CPU memory (no read/write side-effects)" }, { "name": "spcDebug", "description": "SNES - SPC memory (no read/write side-effects)" }, @@ -553,6 +555,7 @@ { "name": "pceDebug", "description": "PC Engine - CPU memory (no read/write side-effects)" }, { "name": "smsDebug", "description": "SMS - CPU memory (no read/write side-effects)" }, { "name": "gbaDebug", "description": "GBA - CPU memory (no read/write side-effects)" }, + { "name": "wsDebug", "description": "WS - CPU memory (no read/write side-effects)" }, { "name": "snesPrgRom", "description": "SNES - PRG ROM" }, { "name": "snesWorkRam", "description": "SNES - Work RAM" }, @@ -584,6 +587,7 @@ { "name": "nesWorkRam", "description": "NES - Work RAM" }, { "name": "nesSaveRam", "description": "NES - Save RAM" }, { "name": "nesNametableRam", "description": "NES - Nametable RAM (CIRAM)" }, + { "name": "nesMapperRam", "description": "NES - Mapper RAM (EXRAM)" }, { "name": "nesSpriteRam", "description": "NES - Sprite RAM (OAM)" }, { "name": "nesSecondarySpriteRam", "description": "NES - Secondary Sprite RAM" }, { "name": "nesPaletteRam", "description": "NES - Palette RAM" }, @@ -619,6 +623,14 @@ { "name": "gbaVideoRam", "description": "GBA - Video RAM" }, { "name": "gbaSpriteRam", "description": "GBA - Sprite RAM" }, { "name": "gbaPaletteRam", "description": "GBA - Palette RAM" } + + { "name": "wsPrgRom", "description": "WS - ROM" } + { "name": "wsWorkRam", "description": "WS - Work RAM" } + { "name": "wsCartRam", "description": "WS - Save RAM" } + { "name": "wsCartEeprom", "description": "WS - Cart EEPROM" } + { "name": "wsBootRom", "description": "WS - Boot ROM" } + { "name": "wsInternalEeprom", "description": "WS - Internal EEPROM" } + { "name": "wsPort", "description": "WS - I/O Port" } ] }, { diff --git a/UI/Debugger/ViewModels/MemoryMappingViewModel.cs b/UI/Debugger/ViewModels/MemoryMappingViewModel.cs index e1e7c7508..614719faf 100644 --- a/UI/Debugger/ViewModels/MemoryMappingViewModel.cs +++ b/UI/Debugger/ViewModels/MemoryMappingViewModel.cs @@ -74,18 +74,21 @@ private List GetNesCpuMappings(NesCartridgeState state) Dictionary mainColors = new() { { NesPrgMemoryType.WorkRam, Color.FromRgb(0xCD, 0xDC, 0xFA) }, { NesPrgMemoryType.SaveRam, Color.FromRgb(0xFA, 0xDC, 0xCD) }, - { NesPrgMemoryType.PrgRom, Color.FromRgb(0xC4, 0xE7, 0xD4) } + { NesPrgMemoryType.PrgRom, Color.FromRgb(0xC4, 0xE7, 0xD4) }, + { NesPrgMemoryType.MapperRam, Color.FromRgb(0xFF, 0xEB, 0x6F) } }; Dictionary altColors = new() { { NesPrgMemoryType.WorkRam, Color.FromRgb(0xBD, 0xCC, 0xEA) }, { NesPrgMemoryType.SaveRam, Color.FromRgb(0xEA, 0xCC, 0xBD) }, - { NesPrgMemoryType.PrgRom, Color.FromRgb(0xA4, 0xD7, 0xB4) } + { NesPrgMemoryType.PrgRom, Color.FromRgb(0xA4, 0xD7, 0xB4) }, + { NesPrgMemoryType.MapperRam, Color.FromRgb(0xEF, 0xDB, 0x5F) } }; Dictionary blockNames = new() { { NesPrgMemoryType.WorkRam, "WRAM" }, { NesPrgMemoryType.SaveRam, "SRAM" }, + { NesPrgMemoryType.MapperRam, "EXRAM" }, { NesPrgMemoryType.PrgRom, "" } }; @@ -121,7 +124,8 @@ void addBlock(int i) int page = memoryType.Value switch { NesPrgMemoryType.WorkRam => (int)(state.PrgMemoryOffset[startIndex] / state.WorkRamPageSize), NesPrgMemoryType.SaveRam => (int)(state.PrgMemoryOffset[startIndex] / state.SaveRamPageSize), - _ or NesPrgMemoryType.PrgRom => (int)(state.PrgMemoryOffset[startIndex] / state.PrgPageSize) + NesPrgMemoryType.PrgRom => (int)(state.PrgMemoryOffset[startIndex] / state.PrgPageSize), + _ => -1 }; mappings.Add(new MemoryMappingBlock() { Length = currentSize, Name = blockNames[memoryType.Value], Page = page, Note = accessNotes[accessType], Color = color }); @@ -166,13 +170,15 @@ private List GetNesPpuMappings(NesCartridgeState state) Dictionary mainColors = new() { { NesChrMemoryType.NametableRam, Color.FromRgb(0xF4, 0xC7, 0xD4) }, { NesChrMemoryType.ChrRom, Color.FromRgb(0xC4, 0xE7, 0xD4) }, - { NesChrMemoryType.ChrRam, Color.FromRgb(0xC4, 0xE0, 0xF4) } + { NesChrMemoryType.ChrRam, Color.FromRgb(0xC4, 0xE0, 0xF4) }, + { NesChrMemoryType.MapperRam, Color.FromRgb(0xFF, 0xEB, 0x6F) } }; Dictionary altColors = new() { { NesChrMemoryType.NametableRam, Color.FromRgb(0xD4, 0xA7, 0xB4) }, { NesChrMemoryType.ChrRom, Color.FromRgb(0xA4, 0xD7, 0xB4) }, - { NesChrMemoryType.ChrRam, Color.FromRgb(0xB4, 0xD0, 0xE4) } + { NesChrMemoryType.ChrRam, Color.FromRgb(0xB4, 0xD0, 0xE4) }, + { NesChrMemoryType.MapperRam, Color.FromRgb(0xEF, 0xDB, 0x5F) } }; Dictionary accessNotes = new() { @@ -202,13 +208,16 @@ void addBlock(int i) int page = memoryType.Value switch { NesChrMemoryType.NametableRam => (int)(state.ChrMemoryOffset[startIndex] / 0x400), NesChrMemoryType.ChrRom => (int)(state.ChrMemoryOffset[startIndex] / state.ChrPageSize), - _ or NesChrMemoryType.ChrRam => (int)(state.ChrMemoryOffset[startIndex] / state.ChrRamPageSize), + NesChrMemoryType.ChrRam => (int)(state.ChrMemoryOffset[startIndex] / state.ChrRamPageSize), + _ => -1 }; string name = ""; if(memoryType == NesChrMemoryType.NametableRam) { name = "NT" + page.ToString(); page = -1; + } else if(memoryType == NesChrMemoryType.MapperRam) { + name = "EXRAM"; } mappings.Add(new MemoryMappingBlock() { Length = currentSize, Name = name, Page = page, Note = accessNotes[accessType], Color = color }); diff --git a/UI/Debugger/ViewModels/SpriteViewerViewModel.cs b/UI/Debugger/ViewModels/SpriteViewerViewModel.cs index cb37215dd..8042d4310 100644 --- a/UI/Debugger/ViewModels/SpriteViewerViewModel.cs +++ b/UI/Debugger/ViewModels/SpriteViewerViewModel.cs @@ -495,6 +495,7 @@ public void RefreshData() { lock(_updateLock) { _coreData.PpuState = DebugApi.GetPpuState(CpuType); + _coreData.PpuToolsState = DebugApi.GetPpuToolsState(CpuType); UpdateVramData(); @@ -534,7 +535,7 @@ private void RefreshTab() _coreData.CopyTo(_data); } - if(_data.PpuState == null || _data.Palette == null) { + if(_data.PpuState == null || _data.Palette == null || _data.PpuToolsState == null) { return; } @@ -542,7 +543,7 @@ private void RefreshTab() Background = Config.Background }; - DebugSpritePreviewInfo previewInfo = DebugApi.GetSpritePreviewInfo(CpuType, options, _data.PpuState); + DebugSpritePreviewInfo previewInfo = DebugApi.GetSpritePreviewInfo(CpuType, options, _data.PpuState, _data.PpuToolsState); InitBitmap((int)previewInfo.Width, (int)previewInfo.Height); UInt32[] palette = _data.Palette.Value.GetRgbPalette(); @@ -553,7 +554,7 @@ private void RefreshTab() BottomClipSize = Config.ShowOffscreenRegions ? 0 : (int)(previewInfo.Height - (previewInfo.VisibleHeight + previewInfo.VisibleY)); using(var framebuffer = ViewerBitmap.Lock(true)) { - DebugApi.GetSpriteList(ref _spriteList, ref _spritePreviews, CpuType, options, _data.PpuState, _data.Vram, _data.SpriteRam, palette, framebuffer.FrameBuffer.Address); + DebugApi.GetSpriteList(ref _spriteList, ref _spritePreviews, CpuType, options, _data.PpuState, _data.PpuToolsState, _data.Vram, _data.SpriteRam, palette, framebuffer.FrameBuffer.Address); } InitPreviews(_spriteList, _spritePreviews, previewInfo); @@ -672,6 +673,7 @@ public void OnGameLoaded() public class SpriteViewerData { public BaseState? PpuState; + public BaseState? PpuToolsState; public byte[] SpriteRam = Array.Empty(); public byte[] Vram = Array.Empty(); public DebugPaletteInfo? Palette = null; @@ -680,6 +682,7 @@ public void CopyTo(SpriteViewerData dst) { dst.PpuState = PpuState; dst.Palette = Palette; + dst.PpuToolsState = PpuToolsState; CopyArray(SpriteRam, ref dst.SpriteRam); CopyArray(Vram, ref dst.Vram); } diff --git a/UI/Debugger/ViewModels/TilemapViewerViewModel.cs b/UI/Debugger/ViewModels/TilemapViewerViewModel.cs index c2ca51e28..40b265a38 100644 --- a/UI/Debugger/ViewModels/TilemapViewerViewModel.cs +++ b/UI/Debugger/ViewModels/TilemapViewerViewModel.cs @@ -332,6 +332,19 @@ private void InitForCpuType() break; case CpuType.Nes: + FrameInfo size = DebugApi.GetTilemapSize(CpuType.Nes, new GetTilemapOptions() { Layer = 1 }, new NesPpuState()); + if(size.Width != 0 && size.Height != 0) { + Tabs = new List() { + new() { Title = "Nametables", Layer = 0 }, + new() { Title = "Window", Layer = 1 } + }; + } else { + Tabs = new List() { + new() { Title = "", Layer = 0 } + }; + } + break; + case CpuType.Sms: Tabs = new List() { new() { Title = "", Layer = 0 } @@ -384,7 +397,7 @@ private void InitForCpuType() private DebugTilemapTileInfo? GetSelectedTileInfo() { - if(_data.PpuState == null || _data.Vram == null) { + if(_data.PpuState == null || _data.PpuToolsState == null || _data.Vram == null) { return null; } else { PixelPoint p; @@ -396,7 +409,7 @@ private void InitForCpuType() } p = PixelPoint.FromPoint(SelectionRect.TopLeft, 1); } - return DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState); + return DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState, _data.PpuToolsState); } } @@ -471,7 +484,7 @@ private void RefreshTab() _coreData.CopyTo(_data); } - if(_data.PpuState == null) { + if(_data.PpuState == null || _data.PpuToolsState == null) { return; } @@ -500,7 +513,7 @@ private void RefreshTab() InitBitmap((int)size.Width, (int)size.Height); using(var framebuffer = ViewerBitmap.Lock()) { - _data.TilemapInfo = DebugApi.GetTilemap(CpuType, options, _data.PpuState, _data.Vram, _data.RgbPalette, framebuffer.FrameBuffer.Address); + _data.TilemapInfo = DebugApi.GetTilemap(CpuType, options, _data.PpuState, _data.PpuToolsState, _data.Vram, _data.RgbPalette, framebuffer.FrameBuffer.Address); } if(_data.TilemapInfo.Bpp == 0) { @@ -559,11 +572,11 @@ private void UpdateTilemapInfo() public DynamicTooltip? GetPreviewPanel(PixelPoint p, DynamicTooltip? tooltipToUpdate) { - if(_data.PpuState == null) { + if(_data.PpuState == null || _data.PpuToolsState == null) { return null; } - DebugTilemapTileInfo? result = DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState); + DebugTilemapTileInfo? result = DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState, _data.PpuToolsState); if(result == null) { return null; } @@ -667,7 +680,7 @@ private string FormatAddress(int address) private void EditTileGrid(int columnCount, int rowCount, Window wnd) { - if(_data.PpuState == null) { + if(_data.PpuState == null || _data.PpuToolsState == null) { return; } @@ -677,7 +690,7 @@ private void EditTileGrid(int columnCount, int rowCount, Window wnd) int palette = -1; for(int row = 0; row < rowCount; row++) { for(int col = 0; col < columnCount; col++) { - DebugTilemapTileInfo? tile = DebugApi.GetTilemapTileInfo((uint)(p.X + GridSizeX*col), (uint)(p.Y + GridSizeY*row), CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState); + DebugTilemapTileInfo? tile = DebugApi.GetTilemapTileInfo((uint)(p.X + GridSizeX*col), (uint)(p.Y + GridSizeY*row), CpuType, GetOptions(SelectedTab), _data.Vram, _data.PpuState, _data.PpuToolsState); if(tile == null) { return; } diff --git a/UI/Debugger/Windows/BreakpointEditWindow.axaml.cs b/UI/Debugger/Windows/BreakpointEditWindow.axaml.cs index b1deec4f7..d7c071747 100644 --- a/UI/Debugger/Windows/BreakpointEditWindow.axaml.cs +++ b/UI/Debugger/Windows/BreakpointEditWindow.axaml.cs @@ -13,6 +13,9 @@ namespace Mesen.Debugger.Windows { public class BreakpointEditWindow : MesenWindow { + [Obsolete("For designer only")] + public BreakpointEditWindow() : this(new BreakpointEditViewModel()) { } + public BreakpointEditWindow(BreakpointEditViewModel model) { DataContext = model; diff --git a/UI/Interop/ConsoleState/NesState.cs b/UI/Interop/ConsoleState/NesState.cs index 98acb954b..6dd5d4324 100644 --- a/UI/Interop/ConsoleState/NesState.cs +++ b/UI/Interop/ConsoleState/NesState.cs @@ -101,6 +101,7 @@ public enum NesPrgMemoryType PrgRom, SaveRam, WorkRam, + MapperRam, } public enum NesChrMemoryType @@ -108,7 +109,8 @@ public enum NesChrMemoryType Default, ChrRom, ChrRam, - NametableRam + NametableRam, + MapperRam, } public enum NesMemoryAccessType @@ -210,7 +212,7 @@ public struct NesCartridgeState public bool HasBattery; public UInt32 CustomEntryCount; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)] public MapperStateEntry[] CustomEntries; } @@ -337,3 +339,32 @@ public struct NesApuState public NesApuDmcState Dmc; public NesApuFrameCounterState FrameCounter; } + +public struct NtExtConfig +{ + public UInt16 SourceOffset; + [MarshalAs(UnmanagedType.I1)] public bool AttrExtMode; + [MarshalAs(UnmanagedType.I1)] public bool BgExtMode; + [MarshalAs(UnmanagedType.I1)] public bool FillMode; +}; + +public struct ExtModeConfig +{ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public NtExtConfig[] Nametables; + + [MarshalAs(UnmanagedType.I1)] public bool SpriteExtMode; + public byte BgExtBank; + public byte SpriteExtBank; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] SpriteExtData; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2000)] + public byte[] ExtRam; +}; + +public struct NesPpuToolsState : BaseState +{ + public ExtModeConfig ExtConfig; +} \ No newline at end of file diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index 6a863038b..e7c2ebca4 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -138,7 +138,7 @@ public static BaseState GetPpuToolsState(CpuType cpuType) { return cpuType switch { CpuType.Snes => GetPpuToolsState(cpuType), - CpuType.Nes => GetPpuToolsState(cpuType), + CpuType.Nes => GetPpuToolsState(cpuType), CpuType.Gameboy => GetPpuToolsState(cpuType), CpuType.Pce => GetPpuToolsState(cpuType), CpuType.Sms => GetPpuToolsState(cpuType), @@ -283,8 +283,8 @@ public static void GetMemoryState(MemoryType type, ref byte[] dst) DebugApi.GetMemoryStateWrapper(type, dst); } - [DllImport(DllPath)] private static extern DebugTilemapInfo GetTilemap(CpuType cpuType, InteropGetTilemapOptions options, IntPtr state, byte[] vram, UInt32[] palette, IntPtr outputBuffer); - public unsafe static DebugTilemapInfo GetTilemap(CpuType cpuType, GetTilemapOptions options, BaseState state, byte[] vram, UInt32[] palette, IntPtr outputBuffer) + [DllImport(DllPath)] private static extern DebugTilemapInfo GetTilemap(CpuType cpuType, InteropGetTilemapOptions options, IntPtr state, IntPtr ppuToolsState, byte[] vram, UInt32[] palette, IntPtr outputBuffer); + public unsafe static DebugTilemapInfo GetTilemap(CpuType cpuType, GetTilemapOptions options, BaseState state, BaseState ppuToolsState, byte[] vram, UInt32[] palette, IntPtr outputBuffer) { Debug.Assert(state.GetType().IsValueType); Debug.Assert(IsValidPpuState(ref state, cpuType)); @@ -293,10 +293,14 @@ public unsafe static DebugTilemapInfo GetTilemap(CpuType cpuType, GetTilemapOpti fixed(AddressCounters* accessCounters = options.AccessCounters) { byte* stateBuffer = stackalloc byte[GetStateSize(state)]; Marshal.StructureToPtr(state, (IntPtr)stateBuffer, false); + + byte* ppuToolsStateBuffer = stackalloc byte[GetStateSize(ppuToolsState)]; + Marshal.StructureToPtr(ppuToolsState, (IntPtr)ppuToolsStateBuffer, false); + InteropGetTilemapOptions interopOptions = options.ToInterop(); interopOptions.CompareVram = (IntPtr)compareVramPtr; interopOptions.AccessCounters = (IntPtr)accessCounters; - return DebugApi.GetTilemap(cpuType, interopOptions, (IntPtr)stateBuffer, vram, palette, outputBuffer); + return DebugApi.GetTilemap(cpuType, interopOptions, (IntPtr)stateBuffer, (IntPtr)ppuToolsStateBuffer, vram, palette, outputBuffer); } } } @@ -309,36 +313,45 @@ public unsafe static FrameInfo GetTilemapSize(CpuType cpuType, GetTilemapOptions byte* ptr = stackalloc byte[GetStateSize(state)]; Marshal.StructureToPtr(state, (IntPtr)ptr, false); + return DebugApi.GetTilemapSize(cpuType, options.ToInterop(), (IntPtr)ptr); } - [DllImport(DllPath)] private static extern DebugTilemapTileInfo GetTilemapTileInfo(UInt32 x, UInt32 y, CpuType cpuType, InteropGetTilemapOptions options, byte[] vram, IntPtr state); - public unsafe static DebugTilemapTileInfo? GetTilemapTileInfo(UInt32 x, UInt32 y, CpuType cpuType, GetTilemapOptions options, byte[] vram, BaseState state) + [DllImport(DllPath)] private static extern DebugTilemapTileInfo GetTilemapTileInfo(UInt32 x, UInt32 y, CpuType cpuType, InteropGetTilemapOptions options, byte[] vram, IntPtr state, IntPtr ppuToolsState); + public unsafe static DebugTilemapTileInfo? GetTilemapTileInfo(UInt32 x, UInt32 y, CpuType cpuType, GetTilemapOptions options, byte[] vram, BaseState state, BaseState ppuToolsState) { Debug.Assert(state.GetType().IsValueType); Debug.Assert(IsValidPpuState(ref state, cpuType)); byte* ptr = stackalloc byte[GetStateSize(state)]; Marshal.StructureToPtr(state, (IntPtr)ptr, false); - DebugTilemapTileInfo info = DebugApi.GetTilemapTileInfo(x, y, cpuType, options.ToInterop(), vram, (IntPtr)ptr); + + byte* ppuToolsStateBuffer = stackalloc byte[GetStateSize(ppuToolsState)]; + Marshal.StructureToPtr(ppuToolsState, (IntPtr)ppuToolsStateBuffer, false); + + DebugTilemapTileInfo info = DebugApi.GetTilemapTileInfo(x, y, cpuType, options.ToInterop(), vram, (IntPtr)ptr, (IntPtr)ppuToolsStateBuffer); return info.Row >= 0 ? info : null; } [DllImport(DllPath)] public static extern void GetTileView(CpuType cpuType, GetTileViewOptions options, byte[] source, int srcSize, UInt32[] palette, IntPtr buffer); - [DllImport(DllPath)] private static extern DebugSpritePreviewInfo GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, IntPtr state); - public unsafe static DebugSpritePreviewInfo GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, BaseState state) + [DllImport(DllPath)] private static extern DebugSpritePreviewInfo GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, IntPtr state, IntPtr ppuToolsState); + public unsafe static DebugSpritePreviewInfo GetSpritePreviewInfo(CpuType cpuType, GetSpritePreviewOptions options, BaseState state, BaseState ppuToolsState) { Debug.Assert(state.GetType().IsValueType); Debug.Assert(IsValidPpuState(ref state, cpuType)); byte* ptr = stackalloc byte[GetStateSize(state)]; Marshal.StructureToPtr(state, (IntPtr)ptr, false); - return DebugApi.GetSpritePreviewInfo(cpuType, options, (IntPtr)ptr); + + byte* ppuToolsStateBuffer = stackalloc byte[GetStateSize(ppuToolsState)]; + Marshal.StructureToPtr(ppuToolsState, (IntPtr)ppuToolsStateBuffer, false); + + return DebugApi.GetSpritePreviewInfo(cpuType, options, (IntPtr)ptr, (IntPtr)ppuToolsStateBuffer); } - [DllImport(DllPath)] private static extern void GetSpriteList(CpuType cpuType, GetSpritePreviewOptions options, IntPtr state, byte[] vram, byte[]? spriteRam, UInt32[] palette, IntPtr sprites, IntPtr spritePreviews, IntPtr screenPreview); - public unsafe static void GetSpriteList(ref DebugSpriteInfo[] result, ref UInt32[] spritePreviews, CpuType cpuType, GetSpritePreviewOptions options, BaseState state, byte[] vram, byte[] spriteRam, UInt32[] palette, IntPtr screenPreview) + [DllImport(DllPath)] private static extern void GetSpriteList(CpuType cpuType, GetSpritePreviewOptions options, IntPtr state, IntPtr ppuToolsState, byte[] vram, byte[]? spriteRam, UInt32[] palette, IntPtr sprites, IntPtr spritePreviews, IntPtr screenPreview); + public unsafe static void GetSpriteList(ref DebugSpriteInfo[] result, ref UInt32[] spritePreviews, CpuType cpuType, GetSpritePreviewOptions options, BaseState state, BaseState ppuToolsState, byte[] vram, byte[] spriteRam, UInt32[] palette, IntPtr screenPreview) { Debug.Assert(state.GetType().IsValueType); Debug.Assert(IsValidPpuState(ref state, cpuType)); @@ -346,7 +359,10 @@ public unsafe static void GetSpriteList(ref DebugSpriteInfo[] result, ref UInt32 byte* statePtr = stackalloc byte[GetStateSize(state)]; Marshal.StructureToPtr(state, (IntPtr)statePtr, false); - int count = (int)GetSpritePreviewInfo(cpuType, options, (IntPtr)statePtr).SpriteCount; + byte* ppuToolsStateBuffer = stackalloc byte[GetStateSize(ppuToolsState)]; + Marshal.StructureToPtr(ppuToolsState, (IntPtr)ppuToolsStateBuffer, false); + + int count = (int)GetSpritePreviewInfo(cpuType, options, (IntPtr)statePtr, (IntPtr)ppuToolsStateBuffer).SpriteCount; if(count != result.Length) { Array.Resize(ref result, count); } @@ -357,7 +373,7 @@ public unsafe static void GetSpriteList(ref DebugSpriteInfo[] result, ref UInt32 fixed(DebugSpriteInfo* spritesPtr = result) { fixed(UInt32* spritePreviewsPtr = spritePreviews) { - DebugApi.GetSpriteList(cpuType, options, (IntPtr)statePtr, vram, spriteRam.Length > 0 ? spriteRam : null, palette, (IntPtr)spritesPtr, (IntPtr)spritePreviewsPtr, screenPreview); + DebugApi.GetSpriteList(cpuType, options, (IntPtr)statePtr, (IntPtr)ppuToolsStateBuffer, vram, spriteRam.Length > 0 ? spriteRam : null, palette, (IntPtr)spritesPtr, (IntPtr)spritePreviewsPtr, screenPreview); } } } @@ -614,6 +630,7 @@ public enum MemoryType NesWorkRam, NesSaveRam, NesNametableRam, + NesMapperRam, NesSpriteRam, NesSecondarySpriteRam, NesPaletteRam, diff --git a/UI/Interop/MemoryTypeExtensions.cs b/UI/Interop/MemoryTypeExtensions.cs index 1b8c006fd..dd8f8019f 100644 --- a/UI/Interop/MemoryTypeExtensions.cs +++ b/UI/Interop/MemoryTypeExtensions.cs @@ -49,6 +49,7 @@ public static CpuType ToCpuType(this MemoryType memType) case MemoryType.NesChrRom: case MemoryType.NesInternalRam: case MemoryType.NesNametableRam: + case MemoryType.NesMapperRam: case MemoryType.NesPaletteRam: case MemoryType.NesSpriteRam: case MemoryType.NesPpuMemory: @@ -272,6 +273,7 @@ public static bool SupportsLabels(this MemoryType memType) case MemoryType.NesWorkRam: case MemoryType.NesSaveRam: case MemoryType.NesInternalRam: + case MemoryType.NesMapperRam: case MemoryType.NesMemory: //PC Engine @@ -455,6 +457,7 @@ public static string GetShortName(this MemoryType memType) MemoryType.NesPrgRom => "PRG", MemoryType.NesWorkRam => "WRAM", MemoryType.NesSaveRam => "SRAM", + MemoryType.NesMapperRam => "EXRAM", MemoryType.NesInternalRam => "RAM", MemoryType.NesSpriteRam => "SPR", diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml index 2fa484c8e..57ccf4ab0 100644 --- a/UI/Localization/resources.en.xml +++ b/UI/Localization/resources.en.xml @@ -336,7 +336,7 @@ Disable PPU $2004 reads (Famicom behavior) Do not reset PPU when resetting console (Famicom behavior) Disable Game Genie bus conflict emulation - Disable flash saves (UNROM512 / GTROM) + Disable flash saves (UNROM512 / GTROM / Rainbow) Enable DMC sample duplication glitch (late-G & H CPU behavior) Enable CPU test mode registers Console model: @@ -2716,6 +2716,7 @@ E Work RAM Save RAM Nametable RAM (CIRAM) + Mapper RAM (EXRAM) Sprite RAM (OAM) Secondary OAM RAM CHR ROM diff --git a/Utilities/Patches/IpsPatcher.cpp b/Utilities/Patches/IpsPatcher.cpp index a2909bc10..2d0965def 100644 --- a/Utilities/Patches/IpsPatcher.cpp +++ b/Utilities/Patches/IpsPatcher.cpp @@ -177,7 +177,7 @@ vector IpsPatcher::CreatePatch(vector originalData, vector(&newData[patchRecord.Address], &newData[patchRecord.Address + patchRecord.Length]); + patchRecord.Replacement = vector(newData.data() + patchRecord.Address, newData.data() + patchRecord.Address + patchRecord.Length); } patchRecord.WriteRecord(patchFile); }