Core: Refactored nametable RAM management to fix some issues and remove some limitations

(This breaks save state compatibility)
This commit is contained in:
Sour 2019-01-09 20:19:16 -05:00
parent 97fb853d66
commit ce68ce57c0
16 changed files with 151 additions and 295 deletions

View file

@ -149,7 +149,7 @@ void BaseMapper::SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8
endAddr >>= 8;
for(uint16_t i = startAddr; i <= endAddr; i++) {
_prgPages[i] = source;
_prgPageAccessType[i] = accessType != -1 ? accessType : (uint8_t)MemoryAccessType::Read;
_prgMemoryAccess[i] = accessType != -1 ? (MemoryAccessType)accessType : MemoryAccessType::Read;
source += 0x100;
}
@ -215,6 +215,12 @@ void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint1
pageCount = _chrRamSize / pageSize;
defaultAccessType |= MemoryAccessType::Write;
break;
case ChrMemoryType::NametableRam:
pageSize = BaseMapper::NametableSize;
pageCount = BaseMapper::NametableCount;
defaultAccessType |= MemoryAccessType::Write;
break;
}
if(pageCount == 0) {
@ -250,6 +256,7 @@ void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, ChrMe
case ChrMemoryType::Default: sourceMemory = _onlyChrRam ? _chrRam : _chrRom; break;
case ChrMemoryType::ChrRom: sourceMemory = _chrRom; break;
case ChrMemoryType::ChrRam: sourceMemory = _chrRam; break;
case ChrMemoryType::NametableRam: sourceMemory = _nametableRam; break;
}
int firstSlot = startAddr >> 8;
int slotCount = (endAddr - startAddr + 1) >> 8;
@ -272,7 +279,7 @@ void BaseMapper::SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8
endAddr >>= 8;
for(uint16_t i = startAddr; i <= endAddr; i++) {
_chrPages[i] = sourceMemory;
_chrPageAccessType[i] = accessType != -1 ? accessType : (uint8_t)MemoryAccessType::ReadWrite;
_chrMemoryAccess[i] = accessType != -1 ? (MemoryAccessType)accessType : MemoryAccessType::ReadWrite;
if(sourceMemory != nullptr) {
sourceMemory += 0x100;
@ -353,16 +360,16 @@ void BaseMapper::SelectChrPage2x(uint16_t slot, uint16_t page, ChrMemoryType mem
void BaseMapper::SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType)
{
uint16_t pageSize = memoryType == ChrMemoryType::ChrRam ? InternalGetChrRamPageSize() : InternalGetChrPageSize();
uint16_t pageSize;
if(memoryType == ChrMemoryType::NametableRam) {
pageSize = BaseMapper::NametableSize;
} else {
pageSize = memoryType == ChrMemoryType::ChrRam ? InternalGetChrRamPageSize() : InternalGetChrPageSize();
}
uint16_t startAddr = slot * pageSize;
uint16_t endAddr = startAddr + pageSize - 1;
if(page == ChrSpecialPage::NametableA) {
SetPpuMemoryMapping(startAddr, endAddr, GetNametable(0));
} else if(page == ChrSpecialPage::NametableB) {
SetPpuMemoryMapping(startAddr, endAddr, GetNametable(1));
} else {
SetPpuMemoryMapping(startAddr, endAddr, page, memoryType);
}
SetPpuMemoryMapping(startAddr, endAddr, page, memoryType);
}
void BaseMapper::InitializeRam(void* data, uint32_t length)
@ -499,88 +506,45 @@ void BaseMapper::RemoveRegisterRange(uint16_t startAddr, uint16_t endAddr, Memor
void BaseMapper::StreamState(bool saving)
{
//Need to get the number of nametables in the state first, before we try to stream the nametable ram array
Stream(_nametableCount);
ArrayInfo<uint8_t> chrRam = { _chrRam, _chrRamSize };
ArrayInfo<uint8_t> workRam = { _workRam, _workRamSize };
ArrayInfo<uint8_t> saveRam = { _saveRam, _saveRamSize };
ArrayInfo<uint8_t> nametableRam = { _nametableRam, _nametableCount * BaseMapper::NametableSize };
uint32_t prgPages[64];
uint32_t chrPages[64];
ArrayInfo<int32_t> prgMemoryOffset = { _prgMemoryOffset, 0x100 };
ArrayInfo<int32_t> chrMemoryOffset = { _chrMemoryOffset, 0x40 };
ArrayInfo<PrgMemoryType> prgMemoryType = { _prgMemoryType, 0x100 };
ArrayInfo<ChrMemoryType> chrMemoryType = { _chrMemoryType, 0x40 };
ArrayInfo<MemoryAccessType> prgMemoryAccess = { _prgMemoryAccess, 0x100 };
ArrayInfo<MemoryAccessType> chrMemoryAccess = { _chrMemoryAccess, 0x40 };
ArrayInfo<uint8_t> nametableIndexes = { _nametableIndexes, 4 };
Stream(_mirroringType, chrRam, workRam, saveRam, nametableRam, prgMemoryOffset, chrMemoryOffset, prgMemoryType, chrMemoryType, prgMemoryAccess, chrMemoryAccess);
if(GetStateVersion() < 9) {
ArrayInfo<uint32_t> prgPageNumbers = { prgPages, 64 };
ArrayInfo<uint32_t> chrPageNumbers = { chrPages, 64 };
Stream(_mirroringType, chrRam, workRam, saveRam, prgPageNumbers, chrPageNumbers, nametableIndexes);
} else {
ArrayInfo<int32_t> prgMemoryOffset = { _prgMemoryOffset, 0x100 };
ArrayInfo<int32_t> chrMemoryOffset = { _chrMemoryOffset, 0x40 };
ArrayInfo<PrgMemoryType> prgMemoryType = { _prgMemoryType, 0x100 };
ArrayInfo<ChrMemoryType> chrMemoryType = { _chrMemoryType, 0x40 };
ArrayInfo<MemoryAccessType> prgMemoryAccess = { _prgMemoryAccess, 0x100 };
ArrayInfo<MemoryAccessType> chrMemoryAccess = { _chrMemoryAccess, 0x40 };
Stream(_mirroringType, chrRam, workRam, saveRam, nametableIndexes, prgMemoryOffset, chrMemoryOffset, prgMemoryType, chrMemoryType, prgMemoryAccess, chrMemoryAccess);
}
if(GetStateVersion() >= 7) {
bool hasExtraNametable[2] = { _cartNametableRam[0] != nullptr, _cartNametableRam[1] != nullptr };
Stream(hasExtraNametable[0], hasExtraNametable[1]);
for(int i = 0; i < 2; i++) {
if(hasExtraNametable[i]) {
if(!_cartNametableRam[i]) {
_cartNametableRam[i] = new uint8_t[0x400];
}
ArrayInfo<uint8_t> ram = { _cartNametableRam[i], 0x400 };
Stream(ram);
}
}
}
if(!saving) {
RestorePrgChrState(prgPages, chrPages);
for(int i = 0; i < 4; i++) {
SetNametable(i, _nametableIndexes[i]);
}
RestorePrgChrState();
}
}
void BaseMapper::RestorePrgChrState(uint32_t* prgPages, uint32_t* chrPages)
void BaseMapper::RestorePrgChrState()
{
if(GetStateVersion() < 9) {
//Support for older save states
for(uint16_t i = 0; i < 64; i++) {
if(prgPages[i] != 0xEEEEEEEE) {
BaseMapper::SelectPRGPage(i, (uint16_t)prgPages[i]);
}
for(uint16_t i = 0; i < 0x100; i++) {
uint16_t startAddr = i << 8;
if(_prgMemoryAccess[i] != MemoryAccessType::NoAccess) {
SetCpuMemoryMapping(startAddr, startAddr + 0xFF, _prgMemoryType[i], _prgMemoryOffset[i], _prgMemoryAccess[i]);
} else {
RemoveCpuMemoryMapping(startAddr, startAddr + 0xFF);
}
}
for(uint16_t i = 0; i < 64; i++) {
if(chrPages[i] != 0xEEEEEEEE) {
BaseMapper::SelectCHRPage(i, (uint16_t)chrPages[i]);
}
}
} else {
for(uint16_t i = 0; i < 0x100; i++) {
uint16_t startAddr = i << 8;
if(_prgMemoryAccess[i] != MemoryAccessType::NoAccess) {
SetCpuMemoryMapping(startAddr, startAddr + 0xFF, _prgMemoryType[i], _prgMemoryOffset[i], _prgMemoryAccess[i]);
} else {
RemoveCpuMemoryMapping(startAddr, startAddr + 0xFF);
}
}
for(uint16_t i = 0; i < 0x40; i++) {
uint16_t startAddr = i << 8;
if(_chrMemoryAccess[i] != MemoryAccessType::NoAccess) {
SetPpuMemoryMapping(startAddr, startAddr + 0xFF, _chrMemoryType[i], _chrMemoryOffset[i], _chrMemoryAccess[i]);
} else {
RemovePpuMemoryMapping(startAddr, startAddr + 0xFF);
}
for(uint16_t i = 0; i < 0x40; i++) {
uint16_t startAddr = i << 8;
if(_chrMemoryAccess[i] != MemoryAccessType::NoAccess) {
SetPpuMemoryMapping(startAddr, startAddr + 0xFF, _chrMemoryType[i], _chrMemoryOffset[i], _chrMemoryAccess[i]);
} else {
RemovePpuMemoryMapping(startAddr, startAddr + 0xFF);
}
}
}
@ -609,8 +573,6 @@ void BaseMapper::Initialize(RomData &romData)
memset(_isWriteRegisterAddr, 0, sizeof(_isWriteRegisterAddr));
AddRegisterRange(RegisterStartAddress(), RegisterEndAddress(), MemoryOperation::Any);
_mirroringType = romData.Info.Mirroring;
_prgSize = (uint32_t)romData.PrgRom.size();
_chrRomSize = (uint32_t)romData.ChrRom.size();
_originalPrgRom = romData.PrgRom;
@ -637,24 +599,18 @@ void BaseMapper::Initialize(RomData &romData)
InitializeRam(_saveRam, _saveRamSize);
InitializeRam(_workRam, _workRamSize);
memset(_cartNametableRam, 0, sizeof(_cartNametableRam));
memset(_nametableIndexes, 0, sizeof(_nametableIndexes));
for(int i = 0; i <= 0xFF; i++) {
//Allow us to map a different page every 256 bytes
_prgPages[i] = nullptr;
_prgPageAccessType[i] = MemoryAccessType::NoAccess;
_chrPages[i] = nullptr;
_chrPageAccessType[i] = MemoryAccessType::NoAccess;
}
_nametableCount = 2;
_nametableRam = new uint8_t[BaseMapper::NametableSize*BaseMapper::NametableCount];
InitializeRam(_nametableRam, BaseMapper::NametableSize*BaseMapper::NametableCount);
for(int i = 0; i < 0x100; i++) {
//Allow us to map a different page every 256 bytes
_prgPages[i] = nullptr;
_prgMemoryOffset[i] = -1;
_prgMemoryType[i] = PrgMemoryType::PrgRom;
_prgMemoryAccess[i] = MemoryAccessType::NoAccess;
}
for(int i = 0; i < 0x40; i++) {
_chrPages[i] = nullptr;
_chrMemoryOffset[i] = -1;
_chrMemoryType[i] = ChrMemoryType::Default;
_chrMemoryAccess[i] = MemoryAccessType::NoAccess;
@ -687,6 +643,8 @@ void BaseMapper::Initialize(RomData &romData)
SetupDefaultWorkRam();
SetMirroringType(romData.Info.Mirroring);
InitMapper();
InitMapper(romData);
@ -702,14 +660,7 @@ BaseMapper::~BaseMapper()
delete[] _prgRom;
delete[] _saveRam;
delete[] _workRam;
if(_cartNametableRam[0]) {
delete[] _cartNametableRam[0];
}
if(_cartNametableRam[1]) {
delete[] _cartNametableRam[1];
}
delete[] _nametableRam;
}
void BaseMapper::ProcessNotification(ConsoleNotificationType type, void* parameter)
@ -746,47 +697,35 @@ void BaseMapper::SetConsole(shared_ptr<Console> console)
_console = console;
}
void BaseMapper::SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB)
uint8_t* BaseMapper::GetNametable(uint8_t nametableIndex)
{
_nesNametableRam[0] = nametableA;
_nesNametableRam[1] = nametableB;
SetMirroringType(_mirroringType);
}
void BaseMapper::AddNametable(uint8_t index, uint8_t *nametable)
{
assert(index >= 4);
_cartNametableRam[index - 2] = nametable;
}
uint8_t* BaseMapper::GetNametable(uint8_t index)
{
if(index <= 1) {
return _nesNametableRam[index];
} else {
return _cartNametableRam[index - 2];
if(nametableIndex >= BaseMapper::NametableCount) {
#ifdef _DEBUG
MessageManager::Log("Invalid nametable index");
#endif
return _nametableRam;
}
_nametableCount = std::max<uint8_t>(_nametableCount, nametableIndex);
return _nametableRam + (nametableIndex * BaseMapper::NametableSize);
}
void BaseMapper::SetNametable(uint8_t index, uint8_t nametableIndex)
{
if(nametableIndex == 2 && _cartNametableRam[0] == nullptr) {
_cartNametableRam[0] = new uint8_t[0x400];
InitializeRam(_cartNametableRam[0], 0x400);
}
if(nametableIndex == 3 && _cartNametableRam[1] == nullptr) {
_cartNametableRam[1] = new uint8_t[0x400];
InitializeRam(_cartNametableRam[1], 0x400);
if(nametableIndex >= BaseMapper::NametableCount) {
#ifdef _DEBUG
MessageManager::Log("Invalid nametable index");
#endif
return;
}
_nametableCount = std::max<uint8_t>(_nametableCount, nametableIndex);
_nametableIndexes[index] = nametableIndex;
SetPpuMemoryMapping(0x2000 + index * 0x400, 0x2000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex));
SetPpuMemoryMapping(0x2000 + index * 0x400, 0x2000 + (index + 1) * 0x400 - 1, nametableIndex, ChrMemoryType::NametableRam);
//Mirror $2000-$2FFF to $3000-$3FFF, while keeping a distinction between the addresses
//Previously, $3000-$3FFF was being "redirected" to $2000-$2FFF to avoid MMC3 IRQ issues (which is incorrect)
//More info here: https://forums.nesdev.com/viewtopic.php?p=132145#p132145
SetPpuMemoryMapping(0x3000 + index * 0x400, 0x3000 + (index + 1) * 0x400 - 1, GetNametable(nametableIndex));
SetPpuMemoryMapping(0x3000 + index * 0x400, 0x3000 + (index + 1) * 0x400 - 1, nametableIndex, ChrMemoryType::NametableRam);
}
void BaseMapper::SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index)
@ -833,7 +772,7 @@ uint8_t BaseMapper::ReadRAM(uint16_t addr)
{
if(_allowRegisterRead && _isReadRegisterAddr[addr]) {
return ReadRegister(addr);
} else if(_prgPageAccessType[addr >> 8] & MemoryAccessType::Read) {
} else if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
return _prgPages[addr >> 8][(uint8_t)addr];
} else {
//assert(false);
@ -848,7 +787,7 @@ uint8_t BaseMapper::PeekRAM(uint16_t addr)
uint8_t BaseMapper::DebugReadRAM(uint16_t addr)
{
if(_prgPageAccessType[addr >> 8] & MemoryAccessType::Read) {
if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
return _prgPages[addr >> 8][(uint8_t)addr];
} else {
//assert(false);
@ -883,7 +822,7 @@ void BaseMapper::DebugWriteRAM(uint16_t addr, uint8_t value)
void BaseMapper::WritePrgRam(uint16_t addr, uint8_t value)
{
if(_prgPageAccessType[addr >> 8] & MemoryAccessType::Write) {
if(_prgMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
_prgPages[addr >> 8][(uint8_t)addr] = value;
}
}
@ -896,7 +835,7 @@ void BaseMapper::NotifyVRAMAddressChange(uint16_t addr)
uint8_t BaseMapper::InternalReadVRAM(uint16_t addr)
{
if(_chrPageAccessType[addr >> 8] & MemoryAccessType::Read) {
if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Read) {
return _chrPages[addr >> 8][(uint8_t)addr];
}
@ -928,7 +867,7 @@ void BaseMapper::DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEf
}
} else {
NotifyVRAMAddressChange(addr);
if(_chrPageAccessType[addr >> 8] & MemoryAccessType::Write) {
if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
_chrPages[addr >> 8][(uint8_t)addr] = value;
}
}
@ -938,7 +877,7 @@ void BaseMapper::WriteVRAM(uint16_t addr, uint8_t value)
{
_console->DebugProcessVramWriteOperation(addr, value);
if(_chrPageAccessType[addr >> 8] & MemoryAccessType::Write) {
if(_chrMemoryAccess[addr >> 8] & MemoryAccessType::Write) {
_chrPages[addr >> 8][(uint8_t)addr] = value;
}
}
@ -1205,10 +1144,6 @@ CartridgeState BaseMapper::GetState()
state.ChrMemoryAccess[i] = _chrMemoryAccess[i];
}
for(int i = 0; i < 4; i++) {
state.Nametables[i] = _nametableIndexes[i];
}
state.WorkRamPageSize = GetWorkRamPageSize();
state.SaveRamPageSize = GetSaveRamPageSize();

View file

@ -25,9 +25,8 @@ private:
uint16_t InternalGetChrRamPageSize();
bool ValidateAddressRange(uint16_t startAddr, uint16_t endAddr);
uint8_t *_nesNametableRam[2];
uint8_t *_cartNametableRam[10];
uint8_t _nametableIndexes[4];
uint8_t *_nametableRam = nullptr;
uint8_t _nametableCount = 2;
bool _onlyChrRam = false;
bool _hasBusConflicts = false;
@ -36,23 +35,25 @@ private:
bool _isReadRegisterAddr[0x10000];
bool _isWriteRegisterAddr[0x10000];
MemoryAccessType _prgMemoryAccess[0x100];
uint8_t* _prgPages[0x100];
MemoryAccessType _chrMemoryAccess[0x100];
uint8_t* _chrPages[0x100];
uint8_t _prgPageAccessType[0x100];
uint8_t _chrPageAccessType[0x100];
int32_t _prgMemoryOffset[0x100];
PrgMemoryType _prgMemoryType[0x100];
MemoryAccessType _prgMemoryAccess[0x100];
int32_t _chrMemoryOffset[0x40];
ChrMemoryType _chrMemoryType[0x40];
MemoryAccessType _chrMemoryAccess[0x40];
int32_t _chrMemoryOffset[0x100];
ChrMemoryType _chrMemoryType[0x100];
vector<uint8_t> _originalPrgRom;
vector<uint8_t> _originalChrRom;
protected:
static constexpr uint32_t NametableCount = 0x10;
static constexpr uint32_t NametableSize = 0x400;
RomInfo _romInfo;
shared_ptr<BaseControlDevice> _mapperControlDevice;
@ -142,10 +143,9 @@ protected:
virtual void StreamState(bool saving) override;
void RestorePrgChrState(uint32_t* prgPages, uint32_t* chrPages);
void RestorePrgChrState();
uint8_t* GetNametable(uint8_t index);
void AddNametable(uint8_t index, uint8_t *nametable);
uint8_t* GetNametable(uint8_t nametableIndex);
void SetNametable(uint8_t index, uint8_t nametableIndex);
void SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index);
void SetMirroringType(MirroringType type);
@ -170,7 +170,6 @@ public:
virtual void SaveBattery() override;
void SetConsole(shared_ptr<Console> console);
virtual void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB);
shared_ptr<BaseControlDevice> GetMapperControlDevice();
RomInfo GetRomInfo();

View file

@ -5,10 +5,6 @@
//Missing Flash rom support, and only tested via a test rom
class Cheapocabra : public BaseMapper
{
private:
uint8_t _reg;
uint8_t* _extraNametables[4];
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
@ -19,47 +15,17 @@ protected:
void InitMapper() override
{
AddRegisterRange(0x7000, 0x7FFF, MemoryOperation::Write);
for(int i = 0; i < 4; i++) {
_extraNametables[i] = new uint8_t[0x400];
InitializeRam(_extraNametables[i], 0x400);
AddNametable(4 + i, _extraNametables[i]);
}
_reg = GetPowerOnByte();
UpdateState();
WriteRegister(0x5000, GetPowerOnByte());
}
virtual ~Cheapocabra()
void WriteRegister(uint16_t addr, uint8_t value) override
{
delete[] _extraNametables[0];
delete[] _extraNametables[1];
delete[] _extraNametables[2];
delete[] _extraNametables[3];
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> extraNametable0{ _extraNametables[0], 0x400 };
ArrayInfo<uint8_t> extraNametable1{ _extraNametables[1], 0x400 };
ArrayInfo<uint8_t> extraNametable2{ _extraNametables[2], 0x400 };
ArrayInfo<uint8_t> extraNametable3{ _extraNametables[3], 0x400 };
Stream(_reg, extraNametable0, extraNametable1, extraNametable2, extraNametable3);
}
void UpdateState()
{
SelectPRGPage(0, _reg & 0x0F);
SelectCHRPage(0, (_reg >> 4) & 0x01);
if(_reg & 0x20) {
SelectPRGPage(0, value & 0x0F);
SelectCHRPage(0, (value >> 4) & 0x01);
if(value & 0x20) {
SetNametables(4, 5, 6, 7);
} else {
SetNametables(0, 1, 2, 3);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_reg = value;
UpdateState();
}
};

View file

@ -8,8 +8,8 @@ class MMC5 : public BaseMapper
{
private:
const uint8_t NtWorkRamIndex = 4;
const uint8_t NtEmptyIndex = 5;
const uint8_t NtFillModeIndex = 6;
const uint8_t NtEmptyIndex = 2;
const uint8_t NtFillModeIndex = 3;
unique_ptr<MMC5Audio> _audio;
@ -18,9 +18,6 @@ private:
uint8_t _fillModeTile;
uint8_t _fillModeColor;
uint8_t *_fillModeNametable;
uint8_t *_emptyNametable;
bool _verticalSplitEnabled;
bool _verticalSplitRightSide;
@ -227,7 +224,14 @@ private:
NtFillModeIndex //"3 - Fill-mode data"
};
SetNametables(nametables[value & 0x03], nametables[(value >> 2) & 0x03], nametables[(value >> 4) & 0x03], nametables[(value >> 6) & 0x03]);
for(int i = 0; i < 4; i++) {
uint8_t nametableId = nametables[(value >> (i * 2)) & 0x03];
if(nametableId == NtWorkRamIndex) {
SetPpuMemoryMapping(0x2000+i*0x400, 0x2000+i*0x400+0x3FF, _workRam, MemoryAccessType::ReadWrite);
} else {
SetNametable(i, nametableId);
}
}
}
void SetExtendedRamMode(uint8_t mode)
@ -252,13 +256,13 @@ private:
void SetFillModeTile(uint8_t tile)
{
_fillModeTile = tile;
memset(_fillModeNametable, tile, 32 * 30); //32 tiles per row, 30 rows
memset(GetNametable(NtFillModeIndex), tile, 32 * 30); //32 tiles per row, 30 rows
}
void SetFillModeColor(uint8_t color)
{
_fillModeColor = color;
memset(_fillModeNametable + 32 * 30, color, 64); //Attribute table is 64 bytes
memset(GetNametable(NtFillModeIndex) + 32 * 30, color, 64); //Attribute table is 64 bytes
}
bool IsSpriteFetch()
@ -317,18 +321,11 @@ protected:
_splitTile = 0;
_splitTileNumber = -1;
_fillModeNametable = new uint8_t[0x400];
_emptyNametable = new uint8_t[0x400];
InitializeRam(_emptyNametable, 0x400);
InitializeRam(_fillModeNametable, 0x400);
memset(GetNametable(NtEmptyIndex), 0, BaseMapper::NametableSize);
//"Expansion RAM ($5C00-$5FFF, read/write)"
SetCpuMemoryMapping(0x5C00, 0x5FFF, 0, PrgMemoryType::WorkRam);
AddNametable(NtWorkRamIndex, _workRam);
AddNametable(NtEmptyIndex, _emptyNametable);
AddNametable(NtFillModeIndex, _fillModeNametable);
//"Additionally, Romance of the 3 Kingdoms 2 seems to expect it to be in 8k PRG mode ($5100 = $03)."
WriteRegister(0x5100, 0x03);
@ -336,29 +333,23 @@ protected:
WriteRegister(0x5117, 0xFF);
}
virtual ~MMC5()
{
delete[] _fillModeNametable;
delete[] _emptyNametable;
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> prgBanks = { _prgBanks, 5 };
ArrayInfo<uint16_t> chrBanks = { _chrBanks, 12 };
ArrayInfo<uint8_t> fillModeNametable = { _fillModeNametable, 0x400 };
SnapshotInfo audio{ _audio.get() };
Stream(_prgRamProtect1, _prgRamProtect2, _fillModeTile, _fillModeColor, _verticalSplitEnabled, _verticalSplitRightSide,
_verticalSplitDelimiterTile, _verticalSplitScroll, _verticalSplitBank, _multiplierValue1, _multiplierValue2,
_nametableMapping, _extendedRamMode, _exAttributeLastNametableFetch, _exAttrLastFetchCounter, _exAttrSelectedChrBank,
_prgMode, prgBanks, _chrMode, _chrUpperBits, chrBanks, _lastChrReg,
_spriteFetch, _largeSprites, _irqCounterTarget, _irqEnabled, _previousScanline, _irqCounter, _irqPending, _ppuInFrame, audio, fillModeNametable,
_spriteFetch, _largeSprites, _irqCounterTarget, _irqEnabled, _previousScanline, _irqCounter, _irqPending, _ppuInFrame, audio,
_splitInSplitRegion, _splitVerticalScroll, _splitTile, _splitTileNumber, _lastVramOperationType);
if(!saving) {
UpdatePrgBanks();
SetNametableMapping(_nametableMapping);
}
}

View file

@ -14,11 +14,6 @@ protected:
if(GetMirroringType() == MirroringType::FourScreens) {
SetMirroringType(_romInfo.NesHeader.Byte6 & 0x01 ? MirroringType::ScreenBOnly : MirroringType::ScreenAOnly);
}
}
void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB) override
{
BaseMapper::SetDefaultNametables(nametableA, nametableB);
uint16_t mask = 0;
switch(GetMirroringType()) {
@ -30,7 +25,7 @@ protected:
}
for(int i = 0; i < 8; i++) {
SetPpuMemoryMapping(i * 0x400, i * 0x400 + 0x3FF, (i * 0x400) & mask ? GetNametable(1) : GetNametable(0));
SetPpuMemoryMapping(i*0x400, i*0x400+0x3FF, ((i * 0x400) & mask) ? 1 : 0, ChrMemoryType::NametableRam);
}
}
};

View file

@ -11,11 +11,6 @@ MemoryManager::MemoryManager(shared_ptr<Console> console)
_internalRAM = new uint8_t[InternalRAMSize];
_internalRamHandler.SetInternalRam(_internalRAM);
for(int i = 0; i < 2; i++) {
_nametableRAM[i] = new uint8_t[NameTableScreenSize];
_console->GetMapper()->InitializeRam(_nametableRAM[i], NameTableScreenSize);
}
_ramReadHandlers = new IMemoryHandler*[RAMSize];
_ramWriteHandlers = new IMemoryHandler*[RAMSize];
@ -30,9 +25,6 @@ MemoryManager::MemoryManager(shared_ptr<Console> console)
MemoryManager::~MemoryManager()
{
delete[] _internalRAM;
for(int i = 0; i < 2; i++) {
delete[] _nametableRAM[i];
}
delete[] _ramReadHandlers;
delete[] _ramWriteHandlers;
@ -41,7 +33,6 @@ MemoryManager::~MemoryManager()
void MemoryManager::SetMapper(shared_ptr<BaseMapper> mapper)
{
_mapper = mapper;
_mapper->SetDefaultNametables(_nametableRAM[0], _nametableRAM[1]);
}
void MemoryManager::Reset(bool softReset)
@ -165,9 +156,7 @@ uint32_t MemoryManager::ToAbsolutePrgAddress(uint16_t ramAddr)
void MemoryManager::StreamState(bool saving)
{
ArrayInfo<uint8_t> internalRam = { _internalRAM, MemoryManager::InternalRAMSize };
ArrayInfo<uint8_t> nameTable0Ram = { _nametableRAM[0], MemoryManager::NameTableScreenSize };
ArrayInfo<uint8_t> nameTable1Ram = { _nametableRAM[1], MemoryManager::NameTableScreenSize };
Stream(internalRam, nameTable0Ram, nameTable1Ram);
Stream(internalRam);
}
uint8_t MemoryManager::GetOpenBus(uint8_t mask)

View file

@ -14,13 +14,11 @@ class MemoryManager : public Snapshotable
private:
static constexpr int RAMSize = 0x10000;
static constexpr int VRAMSize = 0x4000;
static constexpr int NameTableScreenSize = 0x400;
shared_ptr<Console> _console;
shared_ptr<BaseMapper> _mapper;
uint8_t *_internalRAM;
uint8_t *_nametableRAM[2];
OpenBusHandler _openBusHandler;
InternalRamHandler<0x7FF> _internalRamHandler;

View file

@ -169,7 +169,7 @@ protected:
case 0x8000: case 0x8800: case 0x9000: case 0x9800: {
uint8_t bankNumber = (addr - 0x8000) >> 11;
if(!_lowChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) {
SelectCHRPage(bankNumber, (value & 0x01) == 0x01 ? ChrSpecialPage::NametableB : ChrSpecialPage::NametableA);
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
} else {
SelectCHRPage(bankNumber, value);
}
@ -179,7 +179,7 @@ protected:
case 0xA000: case 0xA800: case 0xB000: case 0xB800: {
uint8_t bankNumber = ((addr - 0xA000) >> 11) + 4;
if(!_highChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) {
SelectCHRPage(bankNumber, (value & 0x01) == 0x01 ? ChrSpecialPage::NametableB : ChrSpecialPage::NametableA);
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
} else {
SelectCHRPage(bankNumber, value);
}
@ -199,7 +199,7 @@ protected:
} else {
uint8_t bankNumber = ((addr - 0xC000) >> 11) + 8;
if(value >= 0xE0) {
SelectCHRPage(bankNumber, (value & 0x01) == 0x01 ? ChrSpecialPage::NametableB : ChrSpecialPage::NametableA);
SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam);
} else {
SelectCHRPage(bankNumber, value);
}

View file

@ -118,7 +118,7 @@ protected:
ArrayInfo<uint8_t> internalRam{ _internalRam, 0x80 };
ArrayInfo<int16_t> channelOutput{ _channelOutput, 8 };
Stream(internalRam, channelOutput, _ramPosition, _autoIncrement, _updateCounter, _currentChannel, _lastOutput);
Stream(internalRam, channelOutput, _ramPosition, _autoIncrement, _updateCounter, _currentChannel, _lastOutput, _disableSound);
}
void ClockAudio() override

View file

@ -129,27 +129,18 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
}
stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion));
if(fileFormatVersion < 5) {
if(fileFormatVersion < 10) {
MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion");
return false;
} else if(fileFormatVersion == 5) {
//No SHA1 field in version 5
if(hashCheckRequired) {
//Can't manually load < v5 save states, since we can't know what game the save state is for
MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion");
return false;
}
} else {
int32_t mapperId = -1;
int32_t subMapperId = -1;
if(fileFormatVersion >= 8) {
uint16_t id;
uint8_t sid;
stream.read((char*)&id, sizeof(uint16_t));
stream.read((char*)&sid, sizeof(uint8_t));
mapperId = id;
subMapperId = sid;
}
uint16_t id;
uint8_t sid;
stream.read((char*)&id, sizeof(uint16_t));
stream.read((char*)&sid, sizeof(uint8_t));
mapperId = id;
subMapperId = sid;
char hash[41] = {};
stream.read(hash, 40);

View file

@ -14,7 +14,7 @@ private:
string GetStateFilepath(int stateIndex);
public:
static constexpr uint32_t FileFormatVersion = 9;
static constexpr uint32_t FileFormatVersion = 10;
SaveStateManager(shared_ptr<Console> console);

View file

@ -15,16 +15,18 @@ private:
void UpdateNametables()
{
AddNametable(4, _chrRom + _ntRegs[0] * 0x400);
AddNametable(5, _chrRom + _ntRegs[1] * 0x400);
if(_useChrForNametables) {
switch(GetMirroringType()) {
case MirroringType::FourScreens: break; //4-screen mirroring is not supported by this mapper
case MirroringType::Vertical: SetNametables(4, 5, 4, 5); break;
case MirroringType::Horizontal: SetNametables(4, 4, 5, 5); break;
case MirroringType::ScreenAOnly: SetNametables(4, 4, 4, 4); break;
case MirroringType::ScreenBOnly: SetNametables(5, 5, 5, 5); break;
for(int i = 0; i < 4; i++) {
uint8_t reg = 0;
switch(GetMirroringType()) {
case MirroringType::FourScreens: break; //4-screen mirroring is not supported by this mapper
case MirroringType::Vertical: reg = i & 0x01; break;
case MirroringType::Horizontal: reg = (i & 0x02) >> 1; break;
case MirroringType::ScreenAOnly: reg = 0; break;
case MirroringType::ScreenBOnly: reg = 1; break;
}
SetPpuMemoryMapping(0x2000+i*0x400, 0x2000+i*0x400+0x3FF, ChrMemoryType::Default, _ntRegs[reg] * 0x400, _chrRamSize > 0 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
}
} else {
//Reset to default mirroring
@ -57,11 +59,6 @@ protected:
BaseMapper::StreamState(saving);
Stream(_ntRegs[0], _ntRegs[1], _useChrForNametables, _prgRamEnabled, _usingExternalRom, _externalPage);
if(!saving) {
UpdateNametables();
UpdateState();
}
}
void UpdateState()

View file

@ -78,7 +78,8 @@ enum class ChrMemoryType
{
Default,
ChrRom,
ChrRam
ChrRam,
NametableRam
};
enum MemoryAccessType
@ -90,12 +91,6 @@ enum MemoryAccessType
ReadWrite = 0x03
};
enum ChrSpecialPage
{
NametableA = 0x7000,
NametableB = 0x7001
};
enum class MirroringType
{
Horizontal,
@ -123,8 +118,6 @@ struct CartridgeState
ChrMemoryType ChrType[0x40];
MemoryAccessType ChrMemoryAccess[0x40];
uint32_t Nametables[8];
uint32_t WorkRamPageSize;
uint32_t SaveRamPageSize;

View file

@ -24,15 +24,11 @@ protected:
} else {
_enableMirroringBit = GetMirroringType() == MirroringType::FourScreens;
}
}
void SetDefaultNametables(uint8_t* nametableA, uint8_t* nametableB) override
{
BaseMapper::SetDefaultNametables(nametableA, nametableB);
if(GetMirroringType() == MirroringType::FourScreens && _chrRam && _chrRamSize >= 0x8000) {
//InfiniteNesLives four-screen mirroring variation, last 8kb of CHR RAM is always mapped to 0x2000-0x3FFF (0x3EFF due to palette)
//This "breaks" the "UNROM512_4screen_test" test ROM - was the ROM actually tested on this board? Seems to contradict hardware specs
SetPpuMemoryMapping(0x2000, 0x3FFF, _chrRam + 0x6000);
SetPpuMemoryMapping(0x2000, 0x3FFF, ChrMemoryType::ChrRam, 0x6000, MemoryAccessType::ReadWrite);
}
}

View file

@ -135,6 +135,11 @@ namespace Mesen.GUI.Debugger.Controls
if(memoryType == null) {
regions.Add(new MemoryRegionInfo() { Name = "N/A", Size = currentSize, Color = Color.FromArgb(222, 222, 222) });
} else if(memoryType == ChrMemoryType.NametableRam) {
int page = (int)(state.ChrMemoryOffset[startIndex] / 0x400);
Color color = alternateColor ? Color.FromArgb(0xF4, 0xC7, 0xD4) : Color.FromArgb(0xD4, 0xA7, 0xB4);
alternateColor = !alternateColor;
regions.Add(new MemoryRegionInfo() { Name = "NT" + page.ToString(), Size = currentSize, Color = color });
} else if(memoryType == ChrMemoryType.ChrRom || memoryType == ChrMemoryType.Default && state.ChrRomSize > 0) {
int page = (int)(state.ChrMemoryOffset[startIndex] / state.ChrPageSize);
Color color = alternateColor ? Color.FromArgb(0xC4, 0xE7, 0xD4) : Color.FromArgb(0xA4, 0xD7, 0xB4);
@ -150,9 +155,16 @@ namespace Mesen.GUI.Debugger.Controls
startIndex = i;
};
for(int i = 0; i < 0x20; i++) {
for(int i = 0; i < 0x30; i++) {
if(state.ChrMemoryAccess[i] != MemoryAccessType.NoAccess) {
bool forceNewBlock = (i - startIndex) << 8 >= state.ChrPageSize;
bool forceNewBlock = false;
int blockSize = (i - startIndex) << 8;
if(memoryType == ChrMemoryType.NametableRam && blockSize >= 0x400) {
forceNewBlock = true;
} else if(memoryType != ChrMemoryType.NametableRam && blockSize >= state.ChrPageSize) {
forceNewBlock = true;
}
if(forceNewBlock || memoryType != state.ChrMemoryType[i] || state.ChrMemoryOffset[i] - state.ChrMemoryOffset[i - 1] != 0x100) {
addSection(i);
}
@ -169,10 +181,6 @@ namespace Mesen.GUI.Debugger.Controls
}
addSection(-1);
for(int i = 0; i < 4; i++) {
regions.Add(new MemoryRegionInfo() { Name = "NT " + state.Nametables[i].ToString(), Size = 0x400, Color = i % 2 == 0 ? Color.FromArgb(0xF4, 0xC7, 0xD4) : Color.FromArgb(0xD4, 0xA7, 0xB4) });
}
UpdateRegionArray(regions);
}

View file

@ -1227,7 +1227,8 @@ namespace Mesen.GUI
{
Default,
ChrRom,
ChrRam
ChrRam,
NametableRam
}
public enum MemoryAccessType
@ -1290,9 +1291,6 @@ namespace Mesen.GUI
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)]
public MemoryAccessType[] ChrMemoryAccess;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public UInt32[] Nametables;
public UInt32 WorkRamPageSize;
public UInt32 SaveRamPageSize;