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