Backport mapper fixes

This commit is contained in:
vailkyte 2023-05-12 20:39:46 -05:00 committed by GitHub
parent bbd2149ae0
commit ecb146c225
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 4093 additions and 321 deletions

View File

@ -17,11 +17,11 @@ protected:
{
_selectedReg = 0;
_mirroringBit = 0;
memset(_regs, 0, sizeof(_regs));
memset(_regs, 0xFF, sizeof(_regs));
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
SelectPRGPage(1, -1);
UpdateState();
}
void StreamState(bool saving) override

View File

@ -574,6 +574,12 @@ void BaseMapper::Initialize(RomData &romData)
}
}
if(romData.Info.MiscRoms) {
_miscRomSize = (uint32_t)romData.MiscRomsData.size();
_miscRom = new uint8_t[_miscRomSize];
memcpy(_miscRom, romData.MiscRomsData.data(), _miscRomSize);
}
SetupDefaultWorkRam();
SetMirroringType(romData.Info.Mirroring);
@ -595,6 +601,7 @@ BaseMapper::~BaseMapper()
delete[] _saveRam;
delete[] _workRam;
delete[] _nametableRam;
delete[] _miscRom;
}
void BaseMapper::GetMemoryRanges(MemoryRanges &ranges)
@ -1166,6 +1173,27 @@ void BaseMapper::RestorePrgChrBackup(vector<uint8_t> &backupData)
}
}
void BaseMapper::RevertPrgChrChanges()
{
memcpy(_prgRom, _originalPrgRom.data(), _originalPrgRom.size());
if(_chrRom) {
memcpy(_chrRom, _originalChrRom.data(), _originalChrRom.size());
}
}
bool BaseMapper::HasPrgChrChanges()
{
if(memcmp(_prgRom, _originalPrgRom.data(), _originalPrgRom.size()) != 0) {
return true;
}
if(_chrRom) {
if(memcmp(_chrRom, _originalChrRom.data(), _originalChrRom.size()) != 0) {
return true;
}
}
return false;
}
void BaseMapper::CopyPrgChrRom(shared_ptr<BaseMapper> mapper)
{
if(_prgSize == mapper->_prgSize && _chrRomSize == mapper->_chrRomSize) {

View File

@ -68,6 +68,9 @@ protected:
bool _hasChrBattery = false;
int16_t _vramOpenBusValue = -1;
uint8_t* _miscRom = nullptr;
uint32_t _miscRomSize = 0;
virtual void InitMapper() = 0;
virtual void InitMapper(RomData &romData);
virtual uint16_t GetPRGPageSize() = 0;
@ -232,5 +235,7 @@ public:
vector<uint8_t> GetPrgChrCopy();
void RestorePrgChrBackup(vector<uint8_t>& backupData);
void RevertPrgChrChanges();
bool HasPrgChrChanges();
void CopyPrgChrRom(std::shared_ptr<BaseMapper> mapper);
};

View File

@ -51,7 +51,7 @@ protected:
{
if(addr <= 0x7FFF) {
if(CanWriteToWorkRam()) {
_workRam[addr - 0x6000] = value;
WritePrgRam(addr, value);
if ((_exReg & 0x07) == 0) {
_exReg = addr & 0x3F;
UpdatePrgMapping();
@ -62,4 +62,4 @@ protected:
MMC3::WriteRegister(addr, value);
}
}
};
};

View File

@ -4,12 +4,12 @@
class Bmc235 : public BaseMapper
{
private:
bool _openBus = false;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
uint8_t _unrom;
void InitMapper() override
{
SelectPrgPage2x(0, 0);
@ -19,45 +19,34 @@ protected:
void Reset(bool softReset) override
{
SelectPrgPage2x(0, 0);
_openBus = false;
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_openBus);
if(!saving && _openBus) {
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
if(_prgSize & 0x20000) {
if(softReset) {
_unrom = !_unrom;
} else {
_unrom = false;
}
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SetMirroringType((addr & 0x0400) ? MirroringType::ScreenAOnly : (addr & 0x2000) ? MirroringType::Horizontal : MirroringType::Vertical);
uint8_t bank = ((addr >> 3) & 0x60) | (addr & 0x1F);
const uint8_t config[4][4][2] = {
{ { 0x00, 0 }, { 0x00, 1 }, { 0x00, 1 }, { 0x00, 1 } },
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x00, 1 } },
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x40, 0 } },
{ { 0x00, 0 }, { 0x20, 0 }, { 0x40, 0 }, { 0x60, 0 } }
};
if(_unrom) {
SetMirroringType(MirroringType::Vertical);
SelectPRGPage(0, (GetPRGPageCount() & 0xC0) | (value & 0x07));
SelectPRGPage(1, (GetPRGPageCount() & 0xC0) | 0x07);
return;
}
uint8_t mode;
switch(GetPRGPageCount()) {
case 64: mode = 0; break;
case 128: mode = 1; break;
case 256: mode = 2; break;
default: mode = 3; break;
};
uint8_t bank = config[mode][addr >> 8 & 0x03][0] | (addr & 0x1F);
_openBus = false;
if(config[mode][addr >> 8 & 0x03][1]) {
//Open bus
_openBus = true;
if(bank >= (GetPRGPageCount() >> 1)) {
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
} else if(addr & 0x800) {
return;
}
SetMirroringType((addr & 0x0400) ? MirroringType::ScreenAOnly : (addr & 0x2000) ? MirroringType::Horizontal : MirroringType::Vertical);
if(addr & 0x800) {
bank = (bank << 1) | (addr >> 12 & 0x01);
SelectPRGPage(0, bank);
SelectPRGPage(1, bank);

View File

@ -13,7 +13,7 @@ protected:
void InitMapper() override
{
AddRegisterRange(0x5000, 0x5003, MemoryOperation::Write);
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
}
void Reset(bool softReset) override
@ -37,14 +37,15 @@ protected:
{
if(_regs[0] & 0x80) {
if(_regs[1] & 0x80) {
SelectPrgPage2x(0, (_regs[1] & 0x1F) << 1);
SelectPrgPage2x(0, (_regs[1] & 0x3F) << 1);
} else {
int bank = ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01);
int bank = ((_regs[1] & 0x3F) << 1) | ((_regs[1] >> 6) & 0x01);
SelectPRGPage(0, bank);
SelectPRGPage(1, bank);
}
} else {
SelectPRGPage(1, ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01));
SelectPRGPage(0, ((_regs[1] & 0x3F) << 1) | (_regs[3] & 0x07));
SelectPRGPage(1, ((_regs[1] & 0x3F) << 1) | 0x07);
}
SetMirroringType(_regs[0] & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
SelectCHRPage(0, (_regs[2] << 2) | ((_regs[0] >> 1) & 0x03));
@ -53,7 +54,11 @@ protected:
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[addr & 0x03] = value;
if(HasChrRom() == false) {
_regs[addr & 0x01] = value;
} else {
_regs[addr & 0x03] = value;
}
} else {
_regs[3] = value;
}

View File

@ -82,14 +82,15 @@ protected:
{
if(addr & 0x4000) {
_bankMode = addr & 0x30;
_prgReg = addr & 0x07;
_prgReg = addr & 0x0F;
} else {
SetMirroringType(addr & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
if(_useOuterBank) {
_outerBank = (addr & 0x03) << 3;
_outerBank = (addr & 0x07) << 3;
} else {
_chrReg = addr & 0x07;
_chrReg = addr & 0x0F;
}
}
UpdateState();

View File

@ -33,11 +33,11 @@ protected:
if(_mode & 0x02) {
SelectPRGPage(0, (_regs[0] & 0x0F) | (_regs[1] & 0x70));
} else {
SelectPRGPage(0, _regs[0] & 0x03);
SelectPRGPage(0, (_regs[0] & ((GetPRGPageCount() - 1) & 0x0F)) | 0x80);
}
SelectPRGPage(1, _regs[1] & 0x7F);
SetMirroringType(_regs[0] & 0x10 ? MirroringType::Vertical : MirroringType::Horizontal);
SetMirroringType((_regs[0] & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
@ -49,6 +49,6 @@ protected:
_regs[1] = value;
_mode = reg;
}
UpdateState();
UpdateState();
}
};

View File

@ -19,6 +19,7 @@ protected:
{
_reg = 0;
MMC3::Reset(softReset);
UpdateState();
}
void StreamState(bool saving) override

View File

@ -56,4 +56,4 @@ protected:
MMC3::WriteRegister(addr, value);
}
}
};
};

47
Core/Bmc830752C.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc830752C : public BaseMapper
{
private:
uint8_t _regs[2];
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_regs[0] = _regs[1] = 0;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1]);
}
void UpdateState()
{
uint8_t bank = (_regs[1] & 0x0F) << 3;
SelectPRGPage(0, bank | (_regs[0] & 0x07));
SelectPRGPage(1, bank | 0x07);
SelectCHRPage(0, 0);
SetMirroringType((_regs[1] & 0x60) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if ((addr >= 0xA000) && (addr < 0xC000)) {
_regs[1] = value;
} else {
_regs[0] = value;
}
UpdateState();
}
};

65
Core/Bmc891227.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc891227 : public BaseMapper
{
private:
uint8_t _reg;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
}
void Reset(bool softReset) override
{
if(!softReset) {
_reg = 0;
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_reg);
}
void UpdateState() {
SetCpuMemoryMapping(0x6000, 0x7FFF, PrgMemoryType::PrgRom, 0x2000, MemoryAccessType::Read);
switch((_reg & 0x60) >> 5) {
case 0: // NROM-128
SelectPRGPage(0, _reg & 0x1F);
SelectPRGPage(1, _reg & 0x1F);
break;
case 1: // NROM-256
SelectPrgPage2x(0, _reg & 0x1E);
break;
default: // UNROM
SelectPRGPage(0, _reg & 0x3F);
SelectPRGPage(1, (_reg & 0x3F) | 7);
break;
}
SetMirroringType(_reg & 0x80 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0xC000) {
_reg &= 0x07;
_reg |= value & ~0x07;
} else {
_reg &= _reg & ~0x07;
_reg |= value & 0x07;
}
UpdateState();
}
};

View File

@ -28,4 +28,4 @@ protected:
SelectCHRPage(0, value & 0x0F);
}
}
};
};

66
Core/BmcCtc12in1.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BmcCtc12in1 : public BaseMapper
{
private:
uint8_t _reg;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
}
void Reset(bool softReset) override
{
if(!softReset) {
_reg = 0;
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_reg);
}
void UpdateState() {
SetCpuMemoryMapping(0x6000, 0x7FFF, PrgMemoryType::PrgRom, 0x2000, MemoryAccessType::Read);
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (_reg & 0x80) ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
switch((_reg & 0xC0) >> 6) {
case 0: // NROM-128
SelectPRGPage(0, _reg & 0x1F);
SelectPRGPage(1, _reg & 0x1F);
break;
case 1: // NROM-256
SelectPrgPage2x(0, _reg & 0x1E);
break;
default: // UNROM
SelectPRGPage(0, _reg & 0x3F);
SelectPRGPage(1, (_reg & 0x3F) | 7);
break;
}
SetMirroringType(_reg & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0xC000) {
_reg &= 0x07;
_reg |= value & ~0x07;
} else {
_reg &= _reg & ~0x07;
_reg |= value & 0x07;
}
UpdateState();
}
};

51
Core/BmcDs07.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BmcDs07 : public BaseMapper
{
private:
uint8_t _regs[2];
uint8_t _latch;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
{
_regs[0] = _regs[1] = -1;
_latch = 0;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _latch);
}
void UpdateState()
{
uint8_t base = (_regs[0] & 0xF0) >> 1;
SelectPRGPage(0, base | (_latch & 0x07));
SelectPRGPage(1, base | 0x07);
SetMirroringType((_latch & 0x80) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[addr & 0x01] = value;
} else {
if((_regs[0] & 0x80) == 0) {
_latch = (_latch & 0xF8) | (value & 0x07);
} else {
_latch = value;
}
}
UpdateState();
}
};

59
Core/BmcResetNromX1n1.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// BMC-RESETNROM-XIN1 (Mapper 343)
// submapper 1 - Sheng Tian 2-in-1(Unl,ResetBase)[p1] - Kung Fu (Spartan X), Super Mario Bros (alt)
// submapper 1 - Sheng Tian 2-in-1(Unl,ResetBase)[p2] - B-Wings, Twin-beeSMB1 (wrong mirroring)
class BmcResetNromX1n1 : public BaseMapper
{
private:
uint8_t _game;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
if (!_romInfo.IsNes20Header || !_romInfo.IsInDatabase) {
if (_romInfo.Hash.PrgChrCrc32 == 0x3470F395 || // Sheng Tian 2-in-1(Unl,ResetBase)[p1].unf
_romInfo.Hash.PrgChrCrc32 == 0x39F9140F) { // Sheng Tian 2-in-1(Unl,ResetBase)[p2].unf
_romInfo.SubMapperID = 1;
}
}
}
void Reset(bool softReset) override
{
if(!softReset) {
_game = 0;
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_game);
}
void UpdateState()
{
if(_romInfo.SubMapperID == 1) {
SelectPrgPage2x(0, _game << 1);
} else {
SelectPRGPage(0, _game);
SelectPRGPage(1, _game);
}
SelectCHRPage(0, _game);
SetMirroringType((_game & 0x80) ? MirroringType::Vertical : MirroringType::Horizontal);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_game = ~value;
UpdateState();
}
};

View File

@ -8,7 +8,6 @@ private:
uint8_t _prgBank;
uint8_t _outerBank;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
@ -31,6 +30,7 @@ protected:
SelectPRGPage(0, (_outerBank << 3) | (_prgBank & 0x07));
SelectPRGPage(1, (_outerBank << 3) | 0x07);
SelectCHRPage(0, 0);
SetMirroringType(((_outerBank & 0x20) == 0x20) ? MirroringType::Vertical : MirroringType::Horizontal);
}
void StreamState(bool saving) override
@ -39,6 +39,8 @@ protected:
Stream(_prgBank, _outerBank);
}
// TODO: suppose to have bus conflicts but why it crash if done so?
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
@ -48,4 +50,4 @@ protected:
}
UpdateState();
}
};
};

64
Core/Ctc15.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Ctc15 : public BaseMapper
{
private:
uint8_t _latch;
uint16_t _irqCounter;
bool _irqEnabled;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Write);
AddRegisterRange(0x5000, 0x57FF, MemoryOperation::Write);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Write);
_latch = 0x07;
_irqCounter = 0;
_irqEnabled = false;
SelectCHRPage(0, 0);
SelectPRGPage(0, _latch ^ 0x05);
SelectPRGPage(1, 0x03);
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::WorkRam);
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_latch, _irqCounter, _irqEnabled);
}
void ProcessCpuClock() override
{
if (_irqEnabled) {
if (_irqCounter == 23680) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
} else if (_irqCounter == 24320) {
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
_irqCounter++;
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr < 0x5000) {
_latch = ((addr >> 3) & 0x04) | ((addr >> 2) & 0x03);
_irqEnabled = (addr & 0x04) != 0x04;
if (!_irqEnabled) {
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
} else {
SelectPRGPage(0, _latch ^ 0x05);
}
}
};

View File

@ -1,6 +1,8 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
#include "A12Watcher.h"
class Fk23C : public BaseMapper
{
@ -11,17 +13,17 @@ private:
bool _mmc3ChrMode;
bool _cnromChrMode;
uint16_t _prgBaseBits;
uint8_t _chrBaseBits;
uint16_t _chrBaseBits;
bool _extendedMmc3Mode;
uint8_t _wramBankSelect;
bool _ramInFirstChrBank;
bool _allowSingleScreenMirroring;
bool _fk23RegistersEnabled;
bool _wramConfigEnabled;
bool _wramEnabled;
bool _wramWriteProtected;
bool _invertPrgA14;
bool _invertChrA12;
@ -34,24 +36,52 @@ private:
uint8_t _mirroringReg;
uint8_t _cnromChrReg;
uint8_t _latch;
uint8_t _mmc3Registers[12];
uint8_t _irqDelay;
A12Watcher _a12Watcher;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x0400; }
uint32_t GetChrRamSize() override { return 0x40000; } //Some games have up to 256kb of CHR RAM (only used for iNES 1.0 files w/ no DB entry)
uint16_t GetChrRamPageSize() override { return 0x400; }
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
uint32_t GetWorkRamSize() override { return 0x8000; } //Somes games have up to 32kb of Work RAM (only used for iNES 1.0 files w/ no DB entry)
uint32_t GetWorkRamPageSize() override { return 0x2000; }
virtual uint32_t GetChrRamSize() override {
if(!_romInfo.IsNes20Header && !_romInfo.IsInDatabase) {
if(HasChrRom()) {
// Rockman I-VI uses mixed chr rom/ram
if((_prgSize == 2048 * 1024) && (_chrRomSize == 512 * 1024)) {
return 0x2000;
} else {
return 0;
}
}
}
return 0x40000; // Some games have up to 256kb of CHR RAM (only used for iNES 1.0 files w/ no DB entry)
}
void InitMapper() override
virtual uint16_t GetChrRamPageSize() override { return 0x400; }
virtual uint32_t GetWorkRamSize() override { return 0x8000; } //Somes games have up to 32kb of Work RAM (only used for iNES 1.0 files w/ no DB entry)
virtual uint32_t GetWorkRamPageSize() override { return 0x2000; }
virtual void InitMapper() override
{
if(!_romInfo.IsNes20Header && !_romInfo.IsInDatabase) {
if(!HasChrRom()) {
if(_prgSize >= 8192 * 1024) { // 120-in-1 (Unl)[U].unif
_romInfo.SubMapperID = 2;
}
} else {
if((_prgSize == 1024 *1024) && (_chrRomSize == _prgSize)) { // 4-in-1 (FK23C8078) (Ch) [p1][U][!].unf
_romInfo.SubMapperID = 1;
} else if((_prgSize >= 128 * 1024) && (_chrRomSize == 64 * 1024)) { // 126-in-1 (5-in-1, 16-in-1, 22-in-1, 42-in-1, 56-in-1, 62-in-1) [p1][U][!].unf
_romInfo.SubMapperID = 1;
}
}
}
//$5000
_prgBankingMode = 0;
_outerChrBankSize = 0;
@ -72,15 +102,15 @@ protected:
//$A001
_wramBankSelect = 0;
_ramInFirstChrBank = false;
_allowSingleScreenMirroring = false;
_allowSingleScreenMirroring = (_romInfo.SubMapperID == 2);
_wramConfigEnabled = false;
_fk23RegistersEnabled = false;
_wramEnabled = false;
_wramEnabled = true;
_wramWriteProtected = false;
_currentRegister = 0;
_cnromChrReg = 0;
_latch = 0;
constexpr uint8_t initValues[12] = { 0,2,4,5,6,7,0,1,0xFE, 0xFF, 0xFF, 0xFF };
for(int i = 0; i < 12; i++) {
@ -99,10 +129,15 @@ protected:
_irqDelay = 0;
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
if(_romInfo.SubMapperID == 5) {
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Write);
}
UpdateState();
}
void Reset(bool softReset) override
virtual void Reset(bool softReset) override
{
if(softReset) {
if(_wramConfigEnabled && _selectChrRam && HasBattery()) {
@ -112,7 +147,7 @@ protected:
}
}
void StreamState(bool saving) override
virtual void StreamState(bool saving) override
{
SnapshotInfo a12Watcher { &_a12Watcher };
ArrayInfo<uint8_t> regs { _mmc3Registers, 12 };
@ -120,7 +155,7 @@ protected:
Stream(
_prgBankingMode, _outerChrBankSize, _selectChrRam, _mmc3ChrMode, _cnromChrMode, _prgBaseBits, _chrBaseBits, _extendedMmc3Mode,
_wramBankSelect, _ramInFirstChrBank, _allowSingleScreenMirroring, _fk23RegistersEnabled, _wramConfigEnabled, _wramEnabled, _wramWriteProtected,
_invertPrgA14, _invertChrA12, _currentRegister, _irqReloadValue,_irqCounter, _irqReload, _irqEnabled, _mirroringReg, _cnromChrReg,
_invertPrgA14, _invertChrA12, _currentRegister, _irqReloadValue,_irqCounter, _irqReload, _irqEnabled, _mirroringReg, _latch,
_irqDelay, regs, a12Watcher
);
@ -129,35 +164,43 @@ protected:
}
}
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
virtual void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
{
bool useChrRam = !HasChrRom() || (_selectChrRam && _chrRamSize > 0) || (_wramConfigEnabled && _ramInFirstChrBank && page <= 7);
BaseMapper::SelectCHRPage(slot, page, useChrRam ? ChrMemoryType::ChrRam : ChrMemoryType::ChrRom);
}
void UpdatePrg()
virtual void UpdatePrg()
{
switch(_prgBankingMode) {
case 0:
case 1:
case 2:
case 2: {
uint8_t innerMask = 0x3F >> _prgBankingMode;
if((_romInfo.SubMapperID == 1 || _romInfo.SubMapperID == 3)) {
if(_prgBankingMode == 0) {
innerMask = 0xFF;
}
}
if(_extendedMmc3Mode) {
uint8_t swap = _invertPrgA14 ? 2 : 0;
uint16_t outer = (_prgBaseBits << 1);
SelectPRGPage(0 ^ swap, _mmc3Registers[6] | outer);
SelectPRGPage(1, _mmc3Registers[7] | outer);
SelectPRGPage(2 ^ swap, _mmc3Registers[8] | outer);
SelectPRGPage(3, _mmc3Registers[9] | outer);
} else {
uint8_t swap = _invertPrgA14 ? 2 : 0;
uint8_t innerMask = 0x3F >> _prgBankingMode;
uint16_t outer = (_prgBaseBits << 1) & ~innerMask;
SelectPRGPage(0 ^ swap, (_mmc3Registers[6] & innerMask) | outer);
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
SelectPRGPage(2 ^ swap, (0xFE & innerMask) | outer);
SelectPRGPage(3, (0xFF & innerMask) | outer);
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
SelectPRGPage(2 ^ swap, (_mmc3Registers[8] & innerMask) | outer);
SelectPRGPage(3, (_mmc3Registers[9] & innerMask) | outer);
} else {
uint8_t swap = _invertPrgA14 ? 2 : 0;
uint16_t outer = (_prgBaseBits << 1) & ~innerMask;
SelectPRGPage(0 ^ swap, (_mmc3Registers[6] & innerMask) | outer);
SelectPRGPage(1, (_mmc3Registers[7] & innerMask) | outer);
SelectPRGPage(2 ^ swap, (0xFE & innerMask) | outer);
SelectPRGPage(3, (0xFF & innerMask) | outer);
}
break;
}
case 3:
SelectPrgPage2x(0, _prgBaseBits << 1);
@ -167,18 +210,23 @@ protected:
case 4:
SelectPrgPage4x(0, (_prgBaseBits & 0xFFE) << 1);
break;
case 5:
SelectPrgPage2x(0, ((_latch & 0x07) | (_prgBaseBits & ~0x07)) << 1);
SelectPrgPage2x(1, (0x07 | _prgBaseBits) << 1);
break;
default:
break;
}
}
void UpdateChr()
virtual void UpdateChr()
{
if(!_mmc3ChrMode) {
uint16_t innerMask = _cnromChrMode ? (_outerChrBankSize ? 1 : 3) : 0;
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, (((_cnromChrReg & innerMask) | _chrBaseBits) << 3) + i);
SelectCHRPage(i, (((_latch & innerMask) | _chrBaseBits) << 3) + i);
}
} else {
uint8_t swap = _invertChrA12 ? 0x04 : 0;
@ -208,7 +256,7 @@ protected:
}
}
void UpdateState()
virtual void UpdateMirroring()
{
switch(_mirroringReg & (_allowSingleScreenMirroring ? 0x03 : 0x01)) {
case 0: SetMirroringType(MirroringType::Vertical); break;
@ -216,7 +264,11 @@ protected:
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
}
virtual void UpdateState()
{
UpdateMirroring();
UpdatePrg();
UpdateChr();
@ -234,49 +286,81 @@ protected:
};
}
void WriteRegister(uint16_t addr, uint8_t value) override
virtual void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
if(_fk23RegistersEnabled || !_wramConfigEnabled) {
if(((addr >= 0x5000) && (addr <= 0x5FFF)) &&
(_fk23RegistersEnabled || !_wramConfigEnabled)) {
uint16_t mask = 0x5010;
if((addr & mask) != mask) {
//not a register
return;
}
switch(addr & 0x03) {
switch(addr & ((_romInfo.SubMapperID == 3) ? 0x07 : 0x03)) {
case 0:
_prgBankingMode = value & 0x07;
_outerChrBankSize = (value & 0x10) >> 4;
_selectChrRam = (value & 0x20) != 0;
if(_romInfo.SubMapperID == 1 || _romInfo.SubMapperID == 5) {
_cnromChrMode = (value & 0x20) == 0; // Only submapper 1/5 have CNROM latch
}
_mmc3ChrMode = (value & 0x40) == 0;
_prgBaseBits = (_prgBaseBits & ~0x180) | ((value & 0x80) << 1) | ((value & 0x08) << 4);
if(_romInfo.SubMapperID == 2) {
_prgBaseBits = (_prgBaseBits & ~0x180) | ((value & 0x80) << 1) | ((value & 0x08) << 4);
}
break;
case 1:
_prgBaseBits = (_prgBaseBits & ~0x7F) | (value & 0x7F);
if(_romInfo.SubMapperID == 5) {
_prgBaseBits = (_prgBaseBits & ~0x1F) | (value & 0x1F);
} else {
_prgBaseBits = (_prgBaseBits & ~0x7F) | (value & 0x7F);
}
break;
case 2:
_prgBaseBits = (_prgBaseBits & ~0x200) | ((value & 0x40) << 3);
_chrBaseBits = value;
_cnromChrReg = 0;
if(_romInfo.SubMapperID == 2) {
_prgBaseBits = (_prgBaseBits & ~0xE00) | ((value & 0xC0) << 3) | ((value & 0x20) << 6);
} else if(_romInfo.SubMapperID == 4) {
_prgBaseBits = (_prgBaseBits & ~0x80) | (value & 0x80);
}
break;
case 3:
_extendedMmc3Mode = (value & 0x02) != 0;
_cnromChrMode = (value & 0x44) != 0;
break;
// Submapper 3 has 8 registers, providing PRG/CHR A21
case 5:
_prgBaseBits = (_prgBaseBits & 0x7F) | ((value & 0x0F) << 7);
break;
case 6:
_chrBaseBits |= value << 8;
break;
}
UpdateState();
} else {
//FK23C Registers disabled, $5000-$5FFF maps to the second 4 KiB of the 8 KiB WRAM bank 2
WritePrgRam(addr, value);
if(addr <= 0x4FFF) {
// address $4800 - $4FFF is only enabled in submapper 5
_prgBaseBits = (_prgBaseBits & 0x1F) | (value << 5);
UpdatePrg();
} else {
//FK23C Registers disabled, $5000-$5FFF maps to the second 4 KiB of the 8 KiB WRAM bank 2
WritePrgRam(addr, value);
}
}
} else {
if(_cnromChrMode && (addr <= 0x9FFF || addr >= 0xC000)) {
_cnromChrReg = value & 0x03;
UpdateState();
_latch = value;
if(_prgBankingMode == 5) {
UpdatePrg(); // Update UNROM latch
}
if(!_mmc3ChrMode && _cnromChrMode) {
UpdateChr(); //Update CNROM latch
}
switch(addr & 0xE001) {
@ -307,6 +391,7 @@ protected:
break;
case 0xA001:
// NOTE: bit 3 is unknown
if((value & 0x20) == 0) {
//Ignore extra bits if bit 5 is not set
value &= 0xC0;
@ -314,10 +399,9 @@ protected:
_wramBankSelect = (value & 0x03);
_ramInFirstChrBank = (value & 0x04) != 0;
_allowSingleScreenMirroring = (value & 0x08) != 0;
_wramConfigEnabled = (value & 0x20) != 0;
_fk23RegistersEnabled = (value & 0x40) != 0;
_wramWriteProtected = (value & 0x40) != 0;
_wramEnabled = (value & 0x80) != 0;
@ -377,4 +461,4 @@ public:
break;
}
}
};
};

View File

@ -23,4 +23,4 @@ protected:
{
SelectPrgPage4x(0, value << 2);
}
};
};

View File

@ -22,18 +22,20 @@ private:
uint8_t _prgMode;
bool _enablePrgAt6000;
uint8_t _prgBlock;
uint8_t _chrMode;
bool _chrBlockMode;
uint8_t _chrBlock;
bool _mirrorChr;
uint8_t _outerBank; // $D003
uint8_t _mirroringReg;
bool _extendedMirroring;
bool _advancedNtControl;
bool _disableNtRam;
uint8_t _ntRamSelectBit;
bool _chrWriteEnabled;
uint8_t _ntLowRegs[4];
uint8_t _ntHighRegs[4];
@ -49,11 +51,15 @@ private:
uint8_t _multiplyValue1;
uint8_t _multiplyValue2;
uint8_t _accumulator;
uint8_t _regRamValue;
uint16_t _lastPpuAddr;
bool _inhibitExtendedMirroring;
protected:
virtual uint32_t GetDipSwitchCount() override { return 2; }
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
virtual bool AllowRegisterRead() override { return true; }
@ -71,19 +77,20 @@ protected:
memset(_chrHighRegs, 0, sizeof(_chrHighRegs));
_prgMode = 0;
_prgBlock = 0;
_enablePrgAt6000 = false;
_chrMode = 0;
_chrBlockMode = false;
_chrBlock = 0;
_mirrorChr = false;
_outerBank = 0;
_mirroringReg = 0;
_extendedMirroring = false;
_advancedNtControl = false;
_disableNtRam = false;
_ntRamSelectBit = 0;
_chrWriteEnabled = false;
memset(_ntLowRegs, 0, sizeof(_ntLowRegs));
memset(_ntHighRegs, 0, sizeof(_ntHighRegs));
@ -100,8 +107,13 @@ protected:
_multiplyValue1 = 0;
_multiplyValue2 = 0;
_accumulator = 0;
_regRamValue = 0;
if(_romInfo.MapperID == 90 || _romInfo.MapperID == 388) {
_inhibitExtendedMirroring = true;
}
UpdateState();
}
@ -115,10 +127,11 @@ protected:
ArrayInfo<uint8_t> ntLowRegs{ _ntLowRegs, 4 };
ArrayInfo<uint8_t> ntHighRegs{ _ntHighRegs, 4 };
Stream(_chrLatch[0], _chrLatch[1], _prgMode, _enablePrgAt6000, _chrMode, _chrBlockMode, _chrBlock, _mirrorChr, _mirroringReg, _advancedNtControl,
Stream(_chrLatch[0], _chrLatch[1], _prgMode, _enablePrgAt6000, _chrMode, _mirrorChr, _outerBank, _mirroringReg, _advancedNtControl,
_disableNtRam, _ntRamSelectBit, _irqEnabled, _irqSource, _lastPpuAddr, _irqCountDirection, _irqFunkyMode, _irqFunkyModeReg, _irqSmallPrescaler,
_irqPrescaler, _irqCounter, _irqXorReg, _multiplyValue1, _multiplyValue2, _regRamValue, prgRegs, chrLowRegs, chrHighRegs, ntLowRegs, ntHighRegs,
_prgBlock);
_irqPrescaler, _irqCounter, _irqXorReg, _multiplyValue1, _multiplyValue2, _regRamValue, _accumulator, _chrWriteEnabled, _extendedMirroring,
prgRegs, chrLowRegs, chrHighRegs, ntLowRegs, ntHighRegs
);
if(!saving) {
UpdateState();
@ -132,108 +145,202 @@ protected:
UpdateMirroringState();
}
uint8_t InvertPrgBits(uint8_t prgReg, bool needInvert)
uint8_t InvertPrgBits(uint8_t value)
{
if(needInvert) {
return (prgReg & 0x01) << 6 | (prgReg & 0x02) << 4 | (prgReg & 0x04) << 2 | (prgReg & 0x10) >> 2 | (prgReg & 0x20) >> 4 | (prgReg & 0x40) >> 6;
} else {
return prgReg;
}
return (value & 0x01) << 6 | (value & 0x02) << 4 | (value & 0x04) << 2 | (value & 0x10) >> 2 | (value & 0x20) >> 4 | (value & 0x40) >> 6;
}
void UpdatePrgState()
{
bool invertBits = (_prgMode & 0x03) == 0x03;
uint8_t lastBank = (_prgMode & 0x04) ? _prgRegs[3] : 0x3F;
uint8_t wramBank = 0;
uint8_t prgMask = 0;
uint8_t prgBase = 0;
switch(_prgMode & 0x03) {
case 0:
SelectPrgPage4x(0, ((lastBank & 0x0F) | (_prgBlock << 4)) << 2);
if(_enablePrgAt6000) {
SetCpuMemoryMapping(0x6000, 0x7FFF, ((_prgRegs[3] * 4 + 3) & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
switch(_romInfo.MapperID) {
case 281:
prgMask = 0x1F;
prgBase = (_outerBank & 0x03) << 5;
break;
case 282:
case 358:
prgMask = 0x1F;
prgBase = (_outerBank << 4) & ~prgMask;
break;
case 295:
prgMask = 0x0F;
prgBase = (_outerBank & 0x07) << 4;
break;
case 386:
prgMask = 0x1F;
prgBase = (((_outerBank & 0x02) >> 1) | ((_outerBank & 0x08) >> 2)) << 5;
break;
case 387:
prgMask = 0x0F;
prgBase = (((_outerBank & 0x02) >> 1) | ((_outerBank & 0x08) >> 2)) << 4;
break;
case 388:
prgMask = 0x1F;
prgBase = (_outerBank & 0x0C) << 3;
break;
case 397:
prgMask = 0x1F;
prgBase = (_outerBank & 0x06) << 4;
break;
case 421:
if(_outerBank & 0x04) {
prgMask = 0x3F;
prgBase = (_outerBank & 0x0C) << 4;
} else {
prgMask = 0x1F;
prgBase = (_outerBank & 0x0E) << 4;
}
break;
case 1:
SelectPrgPage2x(0, ((_prgRegs[1] & 0x1F) | (_prgBlock << 5)) << 1);
SelectPrgPage2x(1, ((lastBank & 0x1F) | (_prgBlock << 5)) << 1);
if(_enablePrgAt6000) {
SetCpuMemoryMapping(0x6000, 0x7FFF, ((_prgRegs[3] * 2 + 1) & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
}
break;
case 2:
case 3:
SelectPRGPage(0, (InvertPrgBits(_prgRegs[0], invertBits) & 0x3F) | (_prgBlock << 6));
SelectPRGPage(1, (InvertPrgBits(_prgRegs[1], invertBits) & 0x3F) | (_prgBlock << 6));
SelectPRGPage(2, (InvertPrgBits(_prgRegs[2], invertBits) & 0x3F) | (_prgBlock << 6));
SelectPRGPage(3, (InvertPrgBits(lastBank, invertBits) & 0x3F) | (_prgBlock << 6));
if(_enablePrgAt6000) {
SetCpuMemoryMapping(0x6000, 0x7FFF, (_prgRegs[3] & 0x3F) | (_prgBlock << 6), PrgMemoryType::PrgRom);
}
default: // Mapper 35/90/209/211
prgMask = 0x3F;
prgBase = (_outerBank << 5) & ~prgMask;
break;
}
if(!_enablePrgAt6000) {
switch(_prgMode & 0x03) {
case 0:
SelectPrgPage4x(0, ((lastBank & (prgMask >> 2)) | (prgBase >> 2)) << 2);
wramBank = (_prgRegs[3] * 4 + 3);
break;
case 1:
SelectPrgPage2x(0, ((_prgRegs[1] & (prgMask >> 1)) | (prgBase >> 1)) << 1);
SelectPrgPage2x(1, ((lastBank & (prgMask >> 1)) | (prgBase >> 1)) << 1);
wramBank = (_prgRegs[3] * 2 + 1);
break;
case 2:
SelectPRGPage(0, (_prgRegs[0] & prgMask) | prgBase);
SelectPRGPage(1, (_prgRegs[1] & prgMask) | prgBase);
SelectPRGPage(2, (_prgRegs[2] & prgMask) | prgBase);
SelectPRGPage(3, (lastBank & prgMask) | prgBase);
wramBank = _prgRegs[3];
break;
case 3:
SelectPRGPage(0, (InvertPrgBits(_prgRegs[0]) & prgMask) | prgBase);
SelectPRGPage(1, (InvertPrgBits(_prgRegs[1]) & prgMask) | prgBase);
SelectPRGPage(2, (InvertPrgBits(_prgRegs[2]) & prgMask) | prgBase);
SelectPRGPage(3, (InvertPrgBits(lastBank) & prgMask) | prgBase);
wramBank = _prgRegs[3];
break;
}
if(_enablePrgAt6000) {
SetCpuMemoryMapping(0x6000, 0x7FFF, (wramBank & prgMask) | prgBase, PrgMemoryType::PrgRom);
} else {
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
}
}
uint16_t GetChrReg(int index)
{
if(_chrMode >= 2 && _mirrorChr && (index == 2 || index == 3)) {
index -= 2;
}
if(_chrBlockMode) {
uint8_t mask = 0;
uint8_t shift = 0;
switch(_chrMode) {
default:
case 0: mask = 0x1F; shift = 5; break;
case 1: mask = 0x3F; shift = 6; break;
case 2: mask = 0x7F; shift = 7; break;
case 3: mask = 0xFF; shift = 8; break;
}
return (_chrLowRegs[index] & mask) | (_chrBlock << shift);
} else {
return _chrLowRegs[index] | (_chrHighRegs[index] << 8);
}
return _chrLowRegs[index] | (_chrHighRegs[index] << 8);
}
void UpdateChrState()
void GetChrSetup(uint16_t *mask, uint16_t *base)
{
int chrRegs[8] = { GetChrReg(0), GetChrReg(1), GetChrReg(2), GetChrReg(3), GetChrReg(4), GetChrReg(5), GetChrReg(6), GetChrReg(7) };
switch(_chrMode) {
case 0:
SelectChrPage8x(0, chrRegs[0] << 3);
switch(_romInfo.MapperID) {
case 281:
(*mask) = 0xFF;
(*base) = (_outerBank & 0x03) << 8;
break;
case 295:
case 397:
(*mask) = 0x7F;
(*base) = (_outerBank & 0x07) << 7;
break;
case 1:
SelectChrPage4x(0, chrRegs[_chrLatch[0]] << 2);
SelectChrPage4x(1, chrRegs[_chrLatch[1]] << 2);
case 358:
case 386:
case 387:
if(_outerBank & 0x20) {
(*mask) = 0x1FF;
(*base) = (_outerBank & 0x04) << 7;
} else {
(*mask) = 0x0FF;
(*base) = (((_outerBank & 0x04) >> 1) | (_outerBank & 0x01)) << 8;
}
break;
case 388:
if(_outerBank & 0x20) {
(*mask) = 0x1FF;
(*base) = (_outerBank & 0x02) << 8;
} else {
(*mask) = 0x0FF;
(*base) = (_outerBank & 0x03) << 8;
}
break;
case 2:
SelectChrPage2x(0, chrRegs[0] << 1);
SelectChrPage2x(1, chrRegs[2] << 1);
SelectChrPage2x(2, chrRegs[4] << 1);
SelectChrPage2x(3, chrRegs[6] << 1);
case 421:
(*mask) = 0x1FF;
(*base) = (_outerBank & 0x03) << 8;
break;
case 3:
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, chrRegs[i]);
default: // Mapper 35/90/209/211/282
if(_outerBank & 0x20) {
(*mask) = 0x1FF;
(*base) = (_outerBank & 0x18) << 6;
} else {
(*mask) = 0xFF;
(*base) = (_outerBank & 0x18) << 6 | (_outerBank & 0x01) << 8;
}
break;
}
}
void UpdateChrState()
{
uint16_t chrMask = 0;
uint16_t chrBase = 0;
GetChrSetup(&chrMask, &chrBase);
switch(_chrMode) {
case 0:
SelectChrPage8x(0, ((GetChrReg(0) & (chrMask >> 3)) | (chrBase >> 3)) << 3);
break;
case 1:
SelectChrPage4x(0, ((GetChrReg(_chrLatch[0]) & (chrMask >> 2)) | (chrBase >> 2)) << 2);
SelectChrPage4x(1, ((GetChrReg(_chrLatch[1]) & (chrMask >> 2)) | (chrBase >> 2)) << 2);
break;
case 2:
SelectChrPage2x(0, ((GetChrReg(0) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
SelectChrPage2x(1, ((GetChrReg(2) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
SelectChrPage2x(2, ((GetChrReg(4) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
SelectChrPage2x(3, ((GetChrReg(6) & (chrMask >> 1)) | (chrBase >> 1)) << 1);
break;
case 3:
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, (GetChrReg(i) & chrMask) | chrBase);
}
break;
}
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::ChrRam, _chrWriteEnabled ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
}
void UpdateMirroringState()
{
//"Mapper 211 behaves as though N were always set (1), and mapper 090 behaves as though N were always clear(0)."
if((_advancedNtControl || _romInfo.MapperID == 211) && _romInfo.MapperID != 90) {
if(_advancedNtControl || _extendedMirroring) {
for(int i = 0; i < 4; i++) {
SetNametable(i, _ntLowRegs[i] & 0x01);
}
@ -249,14 +356,20 @@ protected:
uint8_t ReadRegister(uint16_t addr) override
{
uint8_t openBus = _console->GetMemoryManager()->GetOpenBus();
if((addr != 0x5800) && ((addr & 0x3FF) == 0)) {
return ((GetDipSwitches() << 6) | (openBus & 0x3F));
}
switch(addr & 0xF803) {
case 0x5000: return 0; //Dip switches
case 0x5800: return (_multiplyValue1 * _multiplyValue2) & 0xFF;
case 0x5801: return ((_multiplyValue1 * _multiplyValue2) >> 8) & 0xFF;
case 0x5802: return _accumulator;
case 0x5803: return _regRamValue;
}
return _console->GetMemoryManager()->GetOpenBus();
return openBus;
}
void WriteRegister(uint16_t addr, uint8_t value) override
@ -265,7 +378,11 @@ protected:
switch(addr & 0xF803) {
case 0x5800: _multiplyValue1 = value; break;
case 0x5801: _multiplyValue2 = value; break;
case 0x5803: _regRamValue = value; break;
case 0x5802: _accumulator += value; break;
case 0x5803:
_regRamValue = value;
_accumulator = 0;
break;
}
} else if((addr >= 0xC000) && (addr < 0xD000)) {
switch(addr & 0xF007) {
@ -274,6 +391,7 @@ protected:
_irqEnabled = true;
} else {
_irqEnabled = false;
_irqPrescaler = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
break;
@ -287,6 +405,7 @@ protected:
case 0xC002:
_irqEnabled = false;
_irqPrescaler = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
@ -296,12 +415,9 @@ protected:
case 0xC006: _irqXorReg = value; break;
case 0xC007: _irqFunkyModeReg = value; break;
}
} else {
// these registers extend only through addressing ranges $x000 - $x7FF
} else if ((addr >= 0x9000) && (addr < 0xC000)) {
switch(addr & 0xF807) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
_prgRegs[addr & 0x03] = value & 0x7F;
break;
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
case 0x9004: case 0x9005: case 0x9006: case 0x9007:
_chrLowRegs[addr & 0x07] = value;
@ -319,8 +435,18 @@ protected:
case 0xB004: case 0xB005: case 0xB006: case 0xB007:
_ntHighRegs[addr & 0x03] = value;
break;
}
} else {
switch(addr & 0xF803) {
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
_prgRegs[addr & 0x03] = value & 0x7F;
break;
case 0xD000:
if(_inhibitExtendedMirroring) {
value &= ~0x20;
}
_prgMode = value & 0x07;
_chrMode = (value >> 3) & 0x03;
_advancedNtControl = (value & 0x20) == 0x20;
@ -328,16 +454,24 @@ protected:
_enablePrgAt6000 = (value & 0x80) == 0x80;
break;
case 0xD001: _mirroringReg = value & 0x03; break;
case 0xD002: _ntRamSelectBit = value & 0x80; break;
case 0xD001:
if(_inhibitExtendedMirroring) {
value &= ~0x08;
}
_mirroringReg = value & 0x03;
_extendedMirroring = (value & 0x08) == 0x08;
break;
case 0xD002:
_chrWriteEnabled = (value & 0x40) == 0x40;
_ntRamSelectBit = value & 0x80;
break;
case 0xD003:
_mirrorChr = (value & 0x80) == 0x80;
_chrBlockMode = (value & 0x20) == 0x00;
_chrBlock = ((value & 0x18) >> 2) | (value & 0x01);
_prgBlock = (value & 0x06) >> 1;
_outerBank = value & 0x3F;
break;
}
}
@ -360,10 +494,13 @@ protected:
if(addr >= 0x2000) {
//This behavior only affects reads, not writes.
//Additional info: https://forums.nesdev.com/viewtopic.php?f=3&t=17198
if((_advancedNtControl || _romInfo.MapperID == 211) && _romInfo.MapperID != 90) {
if(_advancedNtControl) {
uint8_t ntIndex = ((addr & 0x2FFF) - 0x2000) / 0x400;
if(_disableNtRam || (_ntLowRegs[ntIndex] & 0x80) != (_ntRamSelectBit & 0x80)) {
uint16_t chrPage = _ntLowRegs[ntIndex] | (_ntHighRegs[ntIndex] << 8);
uint16_t mask, base;
GetChrSetup(&mask, &base);
uint16_t chrPage = base | ((_ntLowRegs[ntIndex] | (_ntHighRegs[ntIndex] << 8)) & mask);
uint32_t chrOffset = chrPage * 0x400 + (addr & 0x3FF);
if(_chrRomSize > chrOffset) {
return _chrRom[chrOffset];
@ -384,12 +521,14 @@ protected:
}
_lastPpuAddr = addr;
if(_romInfo.MapperID == 209) {
if(_mirrorChr) {
switch(addr & 0x2FF8) {
case 0x0FD8:
case 0x0FE8:
_chrLatch[addr >> 12] = addr >> 4 & ((addr >> 10 & 0x04) | 0x02);
UpdateChrState();
if(_chrMode == 1) {
UpdateChrState();
}
break;
}
}
@ -400,28 +539,31 @@ protected:
bool clockIrqCounter = false;
uint8_t mask = _irqSmallPrescaler ? 0x07 : 0xFF;
uint8_t prescaler = _irqPrescaler & mask;
if(_irqCountDirection == 0x01) {
prescaler++;
if((prescaler & mask) == 0) {
clockIrqCounter = true;
}
} else if(_irqCountDirection == 0x02) {
if(--prescaler == 0) {
clockIrqCounter = true;
}
}
_irqPrescaler = (_irqPrescaler & ~mask) | (prescaler & mask);
if(clockIrqCounter) {
if(_irqEnabled) {
if(_irqCountDirection == 0x01) {
_irqCounter++;
if(_irqCounter == 0 && _irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
prescaler++;
if((prescaler & mask) == 0) {
clockIrqCounter = true;
}
} else if(_irqCountDirection == 0x02) {
_irqCounter--;
if(_irqCounter == 0xFF && _irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
if(--prescaler == 0) {
clockIrqCounter = true;
}
}
_irqPrescaler = (_irqPrescaler & ~mask) | (prescaler & mask);
if(clockIrqCounter) {
if(_irqCountDirection == 0x01) {
_irqCounter++;
if(_irqCounter == 0) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
} else if(_irqCountDirection == 0x02) {
_irqCounter--;
if(_irqCounter == 0xFF) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
}

38
Core/Kaiser7010.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Kaiser7010 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
SelectPRGPage(0, 10);
SelectPRGPage(1, 11);
SelectPRGPage(2, 6);
SelectPRGPage(3, 7);
SelectCHRPage(0, 0);
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, PrgMemoryType::PrgRom);
}
uint8_t ReadRegister(uint16_t addr) override
{
if ((addr >= 0xCAB6) && (addr <= 0xCAD7)) {
SelectCHRPage(0, (addr >> 2) & 0x0F);
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
}
else if (((addr & 0xFFFE) == 0xEBE2) || ((addr & 0xFFFE) == 0xEE32)) {
SelectCHRPage(0, (addr >> 2) & 0x0F);
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
}
else if ((addr & 0xFFFE) == 0xFFFC) {
SelectCHRPage(0, (addr >> 2) & 0x0F);
SetCpuMemoryMapping(0x6000, 0x7FFF, (addr >> 2) & 0x0F, PrgMemoryType::PrgRom);
}
return InternalReadRam(addr);
}
};

76
Core/Kaiser7030.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Kaiser7030 : public BaseMapper
{
private:
uint8_t _prgRegs[2];
bool _old_mask_rom;
protected:
uint16_t GetPRGPageSize() override { return 0x0400; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint32_t GetWorkRamSize() override { return 0x2000; }
uint32_t GetWorkRamPageSize() override { return 0x0400; }
uint16_t RegisterStartAddress() override { return 0x8000; }
uint16_t RegisterEndAddress() override { return 0x9FFF; }
void InitMapper() override
{
// last 32 KiB mapped to CPU $8000-$FFFF
SetCpuMemoryMapping(0x8000, 0xFFFF, 0x60, PrgMemoryType::PrgRom); // rom-offset $18000
_prgRegs[0] = 0x0F;
_prgRegs[1] = 0x0F;
_old_mask_rom = false;
if(_romInfo.Hash.PrgCrc32 == 0xFA4DAC91) {
_old_mask_rom = true;
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_prgRegs[0], _prgRegs[1], _old_mask_rom);
}
void UpdateState()
{
if(_old_mask_rom) {
// As the FCEUX source code comment indicates, the actual bank order in the 128 KiB mask ROM was unknown until July 2020. Emulators expected the ROM image to be laid out like this:
// * the first 32 KiB to contain the eight banks selected by register $8000 mapped to $7000-$7FFF;
// * the next 64 KiB to contain the sixteen banks selected by register $9000, with the first 1 KiB mapped to CPU $6C00-$6FFF and the second 3 KiB mapped to CPU $C000-$CBFF;
// * the final 32 KiB mapped to CPU $8000-$FFFF except where replaced by RAM and the switchable PRG-ROM bank.
SetCpuMemoryMapping(0x6000, 0x6BFF, 0, PrgMemoryType::WorkRam);
SetCpuMemoryMapping(0x6C00, 0x6FFF, ((_prgRegs[1] & 0x0F) << 2) + 0x20, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $8000
SetCpuMemoryMapping(0x7000, 0x7FFF, ((_prgRegs[0] & 0x07) << 2) + 0x00, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0
SetCpuMemoryMapping(0xB800, 0xBFFF, 3, PrgMemoryType::WorkRam);
SetCpuMemoryMapping(0xC000, 0xCBFF, ((_prgRegs[1] & 0x0F) << 2) + 0x21, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $8400
SetCpuMemoryMapping(0xCC00, 0xD7FF, 5, PrgMemoryType::WorkRam);
} else {
// The actual mask ROM layout is as follows:
// * the first 64 KiB contain the sixteen banks selected by register $9000, with the first 3 KiB mapped to CPU $C000-$CBFF and the second 1 KiB mapped to CPU $6C00-$6FFF;
// * the next 32 KiB contain the eight banks selected by register $8000 mapped to $7000-$7FFF;
// * the final 32 KiB mapped to CPU $8000-$FFFF except where replaced by RAM and the switchable PRG-ROM bank.
SetCpuMemoryMapping(0x6000, 0x6BFF, 0, PrgMemoryType::WorkRam);
SetCpuMemoryMapping(0x6C00, 0x6FFF, ((_prgRegs[1] & 0x0F) << 2) + 0x03, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0C00
SetCpuMemoryMapping(0x7000, 0x7FFF, ((_prgRegs[0] & 0x07) << 2) + 0x40, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $10000
SetCpuMemoryMapping(0xB800, 0xBFFF, 3, PrgMemoryType::WorkRam);
SetCpuMemoryMapping(0xC000, 0xCBFF, ((_prgRegs[1] & 0x0F) << 2) + 0x00, PrgMemoryType::PrgRom, MemoryAccessType::ReadWrite); // rom-offset $0000
SetCpuMemoryMapping(0xCC00, 0xD7FF, 5, PrgMemoryType::WorkRam);
}
SetMirroringType(_prgRegs[0] & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_prgRegs[(addr >> 12) & 0x01] = addr & 0x0F;
UpdateState();
}
};

View File

@ -7,7 +7,7 @@ class Kaiser7058 : public BaseMapper
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x1000; }
virtual uint16_t RegisterStartAddress() override { return 0xF000; }
virtual uint16_t RegisterStartAddress() override { return 0x8000; }
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
@ -17,9 +17,9 @@ protected:
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xF080) {
case 0xF000: SelectCHRPage(0, value); break;
case 0xF080: SelectCHRPage(1, value); break;
switch(addr & 0x01) {
case 0x00: SelectCHRPage(0, value); break;
case 0x01: SelectCHRPage(1, value); break;
}
}
};

View File

@ -9,6 +9,7 @@ class Mapper116 : public BaseMapper
private:
A12Watcher _a12Watcher;
uint8_t _mode;
uint8_t _game;
uint8_t _vrc2Chr[8];
uint8_t _vrc2Prg[2];
@ -28,14 +29,12 @@ private:
bool _irqEnabled;
protected:
virtual uint16_t RegisterStartAddress() override { return 0x4100; }
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; }
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x400; }
void InitMapper() override
{
_mode = 0;
_mode = 1;
_vrc2Chr[0] = -1;
_vrc2Chr[1] = -1;
@ -73,6 +72,23 @@ protected:
_mmc1Buffer = 0;
_mmc1Shift = 0;
// 5-in-1 multicart
_game = (_romInfo.SubMapperID == 3) ? 4 : 0;
UpdateState();
AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Write);
}
void Reset(bool softReset) override
{
if(_romInfo.SubMapperID == 3) {
_game++;
if(_game > 4) {
_game = 0;
}
}
UpdateState();
}
@ -119,20 +135,22 @@ protected:
void UpdatePrg()
{
uint8_t prgMask = (_romInfo.SubMapperID != 3) ? 0x3F : (_game ? 0x0F : 0x1F);
uint32_t outerBank = _game ? (_game + 1) * 0x10 : 0;
switch(_mode & 0x03) {
case 0:
SelectPRGPage(0, _vrc2Prg[0]);
SelectPRGPage(1, _vrc2Prg[1]);
SelectPRGPage(2, -2);
SelectPRGPage(3, -1);
SelectPRGPage(0, outerBank | (_vrc2Prg[0] & prgMask));
SelectPRGPage(1, outerBank | (_vrc2Prg[1] & prgMask));
SelectPRGPage(2, outerBank | ((-2) & prgMask));
SelectPRGPage(3, outerBank | ((-1) & prgMask));
break;
case 1: {
uint32_t prgMode = (_mmc3Ctrl >> 5) & 0x02;
SelectPRGPage(0, _mmc3Regs[6 + prgMode]);
SelectPRGPage(1, _mmc3Regs[7]);
SelectPRGPage(2, _mmc3Regs[6 + (prgMode ^ 0x02)]);
SelectPRGPage(3, _mmc3Regs[9]);
SelectPRGPage(0, outerBank | (_mmc3Regs[6 + prgMode] & prgMask));
SelectPRGPage(1, outerBank | (_mmc3Regs[7] & prgMask));
SelectPRGPage(2, outerBank | (_mmc3Regs[6 + (prgMode ^ 0x02)] & prgMask));
SelectPRGPage(3, outerBank | (_mmc3Regs[9] & prgMask));
break;
}
@ -140,6 +158,9 @@ protected:
case 3: {
uint8_t bank = _mmc1Regs[3] & 0x0F;
if(_mmc1Regs[0] & 0x08) {
if(_romInfo.SubMapperID == 2) { // Huang 2
bank >>= 1;
}
if(_mmc1Regs[0] & 0x04) {
SelectPrgPage2x(0, bank << 1);
SelectPrgPage2x(1, 0x0F << 1);
@ -148,7 +169,7 @@ protected:
SelectPrgPage2x(1, bank << 1);
}
} else {
SelectPrgPage4x(0, (bank & 0xFE) << 1);
SelectPrgPage4x(0, (outerBank | (bank & 0xFE)) << 1);
}
break;
}
@ -157,34 +178,39 @@ protected:
void UpdateChr()
{
uint32_t outerBank = (_mode & 0x04) << 6;
uint32_t chrMask = _game ? 0x7F : 0xFF;
uint32_t chrA18 = (_mode & 0x04) << 6;
uint32_t outerBank = 0;
if(_romInfo.SubMapperID == 3) {
outerBank |= _game ? (_game + 1) * 0x80 : 0;
}
switch(_mode & 0x03) {
case 0:
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, outerBank | _vrc2Chr[i]);
SelectCHRPage(i, outerBank | chrA18 | (_vrc2Chr[i] & chrMask));
}
break;
case 1: {
uint32_t slotSwap = (_mmc3Ctrl & 0x80) ? 4 : 0;
SelectCHRPage(0 ^ slotSwap, outerBank | ((_mmc3Regs[0]) & 0xFE));
SelectCHRPage(1 ^ slotSwap, outerBank | (_mmc3Regs[0] | 1));
SelectCHRPage(2 ^ slotSwap, outerBank | ((_mmc3Regs[1]) & 0xFE));
SelectCHRPage(3 ^ slotSwap, outerBank | (_mmc3Regs[1] | 1));
SelectCHRPage(4 ^ slotSwap, outerBank | _mmc3Regs[2]);
SelectCHRPage(5 ^ slotSwap, outerBank | _mmc3Regs[3]);
SelectCHRPage(6 ^ slotSwap, outerBank | _mmc3Regs[4]);
SelectCHRPage(7 ^ slotSwap, outerBank | _mmc3Regs[5]);
SelectCHRPage(0 ^ slotSwap, outerBank | chrA18 | (((_mmc3Regs[0]) & 0xFE) & chrMask));
SelectCHRPage(1 ^ slotSwap, outerBank | chrA18 | ((_mmc3Regs[0] | 1) & chrMask));
SelectCHRPage(2 ^ slotSwap, outerBank | chrA18 | (((_mmc3Regs[1]) & 0xFE) & chrMask));
SelectCHRPage(3 ^ slotSwap, outerBank | chrA18 | ((_mmc3Regs[1] | 1) & chrMask));
SelectCHRPage(4 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[2] & chrMask));
SelectCHRPage(5 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[3] & chrMask));
SelectCHRPage(6 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[4] & chrMask));
SelectCHRPage(7 ^ slotSwap, outerBank | chrA18 | (_mmc3Regs[5] & chrMask));
break;
}
case 2:
case 3: {
if(_mmc1Regs[0] & 0x10) {
SelectChrPage4x(0, _mmc1Regs[1] << 2);
SelectChrPage4x(1, _mmc1Regs[2] << 2);
SelectChrPage4x(0, (outerBank | (_mmc1Regs[1] & chrMask)) << 2);
SelectChrPage4x(1, (outerBank | (_mmc1Regs[2] & chrMask)) << 2);
} else {
SelectChrPage8x(0, (_mmc1Regs[1] & 0xFE) << 2);
SelectChrPage8x(0, (outerBank | ((_mmc1Regs[1] & 0xFE) & chrMask)) << 2);
}
break;
}
@ -282,7 +308,7 @@ protected:
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
if((addr & 0x4100) == 0x4100) {
if(addr & 0x100) {
_mode = value;
if(addr & 0x01) {
_mmc1Regs[0] = 0xc;
@ -290,6 +316,10 @@ protected:
_mmc1Buffer = 0;
_mmc1Shift = 0;
}
if((value & 0x03) != 1) {
_irqEnabled = false;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
UpdateState();
}
} else {

75
Core/Mapper127.h Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
// Double Dragon II pirate
class Mapper127 : public BaseMapper
{
private:
uint8_t _irqCounter;
bool _irqEnabled;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
_irqCounter = 0;
_irqEnabled = false;
SelectPRGPage(0, 0x0F);
SelectPRGPage(1, 0x0F);
SelectPRGPage(2, 0x0F);
SelectPRGPage(3, 0x0F);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_irqEnabled, _irqCounter);
}
void ProcessCpuClock() override
{
if(_irqEnabled && (!--_irqCounter)) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x73) {
case 0x00: SelectPRGPage(0, value & 0x0F); break;
case 0x01: SelectPRGPage(1, value & 0x0F); break;
case 0x02: SelectPRGPage(2, value & 0x0F); break;
case 0x03: SelectPRGPage(3, (value & 0x03) | 0x0C); break;
case 0x10: SelectCHRPage(0, value & 0x7F); break;
case 0x11: SelectCHRPage(1, value & 0x7F); break;
case 0x12: SelectCHRPage(2, value & 0x7F); break;
case 0x13: SelectCHRPage(3, value & 0x7F); break;
case 0x20: SelectCHRPage(4, value & 0x7F); break;
case 0x21: SelectCHRPage(5, value & 0x7F); break;
case 0x22: SelectCHRPage(6, value & 0x7F); break;
case 0x23: SelectCHRPage(7, value & 0x7F); break;
case 0x30: case 0x31: case 0x32: case 0x33:
_irqEnabled = true;
break;
case 0x40: case 0x41: case 0x42: case 0x43:
_irqEnabled = false;
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
case 0x50: SetNametable(0, value & 0x01); break;
case 0x51: SetNametable(1, value & 0x01); break;
case 0x52: SetNametable(2, value & 0x01); break;
case 0x53: SetNametable(3, value & 0x01); break;
}
}
};

51
Core/Mapper128.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// 4-in-1 pirate multicart
class Mapper128 : public BaseMapper
{
private:
uint8_t _reg;
uint16_t _outerBank;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_outerBank = 0;
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
_outerBank = 0;
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_reg, _outerBank);
}
void UpdateState()
{
SelectPRGPage(0, ((_outerBank >> 2) & 0x18) | (_reg & 0x07));
SelectPRGPage(1, ((_outerBank >> 2) & 0x18) | 0x07);
SelectCHRPage(0, 0);
SetMirroringType((_outerBank & 0x02) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(_outerBank < 0xF000) {
_outerBank = addr;
}
_reg = value;
UpdateState();
}
};

22
Core/Mapper217.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper217 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectCHRPage(0, addr & 0x0F);
addr >>= 2;
SelectPRGPage(0, addr & 0x03);
}
};

View File

@ -7,16 +7,34 @@ class Mapper225 : public BaseMapper
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
uint8_t RAM[4];
void InitMapper() override
{
memset(RAM, 0, sizeof(RAM));
SelectPRGPage(0, 0);
SelectPRGPage(1, 1);
SelectCHRPage(0, 0);
AddRegisterRange(0x5800, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
}
uint8_t ReadRegister(uint16_t addr) override
{
return RAM[addr & 0x03] & 0x0F;
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr <= 0x5FFF) {
RAM[addr & 0x03] = value;
return;
}
uint8_t highBit = (addr >> 8) & 0x40;
uint8_t prgPage = ((addr >> 6) & 0x3F) | highBit;
if(addr & 0x1000) {

View File

@ -2,50 +2,53 @@
#include "stdafx.h"
#include "BaseMapper.h"
// NES Mapper 226
// UNIF BMC-Ghostbusters63in1
class Mapper226 : public BaseMapper
{
protected:
uint8_t _registers[2];
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_registers[0] = 0;
_registers[1] = 0;
SelectPRGPage(0, 0);
SelectPRGPage(1, 1);
SelectCHRPage(0, 0);
}
virtual void StreamState(bool saving) override
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_registers[0], _registers[1]);
}
virtual void Reset(bool softReset) override
void Reset(bool softReset) override
{
if(softReset) {
_registers[0] = 0;
_registers[1] = 0;
_registers[0] = 0;
_registers[1] = 0;
SelectPRGPage(0, 0);
SelectPRGPage(1, 1);
SelectCHRPage(0, 0);
UpdateState();
}
uint8_t GetPrgPage()
{
const uint8_t banks[4] = { 0, 0, 1, 2 };
uint8_t base = ((_registers[0] & 0x80) >> 7) | ((_registers[1] & 0x01) << 1);
if(_prgSize == (1536 * 1024)) { // for 1536 KB PRG roms / BMC-Ghostbusters63in1
base = banks[base];
}
return (_registers[0] & 0x1F) | (base << 5);
}
virtual uint8_t GetPrgPage()
{
return (_registers[0] & 0x1F) | ((_registers[0] & 0x80) >> 2) | ((_registers[1] & 0x01) << 6);
}
void UpdatePrg()
void UpdateState()
{
uint8_t prgPage = GetPrgPage();
if(_registers[0] & 0x20) {
SelectPRGPage(0, prgPage);
SelectPRGPage(1, prgPage);
@ -53,17 +56,17 @@ protected:
SelectPRGPage(0, prgPage & 0xFE);
SelectPRGPage(1, (prgPage & 0xFE) + 1);
}
SelectCHRPage(0, 0);
SetMirroringType(_registers[0] & 0x40 ? MirroringType::Vertical : MirroringType::Horizontal);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x8001) {
case 0x8000: _registers[0] = value; break;
case 0x8001: _registers[1] = value; break;
}
_registers[addr & 0x01] = value;
UpdatePrg();
SetMirroringType(_registers[0] & 0x40 ? MirroringType::Vertical : MirroringType::Horizontal);
UpdateState();
}
};

View File

@ -4,20 +4,44 @@
class Mapper227 : public BaseMapper
{
protected:
uint32_t GetDipSwitchCount() override { return 4; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
bool _mFlag;
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void Reset(bool softreset) override
{
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_mFlag);
}
uint8_t ReadRegister(uint16_t addr) override
{
if(_mFlag) {
addr |= GetDipSwitches();
}
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint16_t prgBank = ((addr >> 2) & 0x1F) | ((addr & 0x100) >> 3);
bool sFlag = (addr & 0x01) == 0x01;
bool lFlag = ((addr >> 9) & 0x01) == 0x01;
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
_mFlag = ((addr >> 10) & 0x01) == 0x01;
if(prgMode) {
if(sFlag) {
@ -46,6 +70,9 @@ protected:
}
}
// protect CHR-RAM on nrom modes
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View File

@ -1,34 +1,52 @@
#pragma once
#include "stdafx.h"
#include "Mapper226.h"
#include "BaseMapper.h"
class Mapper233 : public Mapper226
class Mapper233 : public BaseMapper
{
private:
protected:
uint8_t _reset;
protected:
void Reset(bool softReset) override
{
Mapper226::Reset(softReset);
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
if(softReset) {
_reset = _reset ^ 0x01;
UpdatePrg();
} else {
_reset = 0;
}
void InitMapper() override
{
_reset = 0;
}
void StreamState(bool saving) override
{
Mapper226::StreamState(saving);
BaseMapper::StreamState(saving);
Stream(_reset);
}
uint8_t GetPrgPage() override
void Reset(bool softReset) override
{
return (_registers[0] & 0x1F) | (_reset << 5) | ((_registers[1] & 0x01) << 6);
if(softReset) {
_reset ^= 0x20;
}
WriteRegister(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(value & 0x20) {
SelectPRGPage(0, _reset | (value & 0x1F));
SelectPRGPage(1, _reset | (value & 0x1F));
} else {
SelectPRGPage(0, _reset | (value & 0x1E));
SelectPRGPage(1, _reset | (value & 0x1E) + 1);
}
SelectCHRPage(0, 0);
switch((value >> 6) & 0x03) {
case 0: SetMirroringType(MirroringType::ScreenAOnly); break;
case 1: SetMirroringType(MirroringType::Vertical); break;
case 2: SetMirroringType(MirroringType::Horizontal); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
}
};

72
Core/Mapper237.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// Used by Teletubbies 420-in-1 multicart
class Mapper237 : public BaseMapper
{
private:
uint8_t _prgBank;
bool _type;
bool _lock;
protected:
virtual uint32_t GetDipSwitchCount() override { return 2; }
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
virtual bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
_lock = false;
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
_lock = false;
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_prgBank, _lock, _type);
}
uint8_t ReadRegister(uint16_t addr) override
{
if (_type)
return GetDipSwitches();
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(_lock) {
_prgBank &= ~0x07;
_prgBank |= value & 0x07;
} else {
_type = (addr & 0x01) == 0x01;
_lock = (addr & 0x02) == 0x02;
_prgBank = ((addr << 3) & 0x20) | (value & 0x1F);
}
SetMirroringType((value & 0x20) == 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
if(value & 0x80) {
if(value & 0x40) {
SelectPrgPage2x(0, _prgBank & 0xFE);
} else {
SelectPRGPage(0, _prgBank);
SelectPRGPage(1, _prgBank);
}
} else {
SelectPRGPage(0, _prgBank);
SelectPRGPage(1, _prgBank | 0x07);
}
SelectCHRPage(0, 0);
}
};

View File

@ -1,28 +1,69 @@
#pragma once
#include "stdafx.h"
#pragma once
#include "BaseMapper.h"
class Mapper242 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
Reset(false);
SelectCHRPage(0, 0);
WriteRegister(0x8000, 0);
}
virtual void Reset(bool softReset) override
void Reset(bool softreset) override
{
SelectPRGPage(0, 0);
SetMirroringType(MirroringType::Vertical);
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SetMirroringType(addr & 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
SelectPRGPage(0, (addr >> 3) & 0x0F);
uint16_t prgBank = (addr >> 2) & 0x1F;
bool sFlag = (addr & 0x01) == 0x01;
bool lFlag = ((addr >> 9) & 0x01) == 0x01;
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
bool cFlag = ((addr >> 10) & 0x01) == 0x01;
bool twoChips = (_prgSize & 0x20000) && (_prgSize > 0x20000);
if(twoChips) {
if(cFlag | lFlag) {
prgBank &= 0x1F;
} else {
prgBank = 0x20 + (prgBank & 0x07);
}
}
if(prgMode) {
if(sFlag) {
SelectPrgPage2x(0, prgBank & 0xFE);
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank);
}
} else {
if(sFlag){
if(lFlag) {
SelectPRGPage(0, prgBank & 0x3E);
SelectPRGPage(1, prgBank | 0x07);
} else {
SelectPRGPage(0, prgBank & 0x3E);
SelectPRGPage(1, prgBank & 0x38);
}
} else {
if(lFlag) {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank | 0x07);
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank & 0x38);
}
}
}
// protect CHR-RAM on nrom modes
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode && (_prgSize > 256 * 1024)) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

24
Core/Mapper271.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// BMC-22026 TXC 4-in-1 multicart (MGC-026)
class Mapper271 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, (value >> 4) & 0x03);
SelectCHRPage(0, value & 0x0F);
SetMirroringType((value & 0x20) ? MirroringType::Vertical : MirroringType::Horizontal);
}
};

108
Core/Mapper272.h Normal file
View File

@ -0,0 +1,108 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper272 : public BaseMapper
{
private:
uint8_t _chrBanks[8];
uint8_t _irqCounter;
bool _irqEnabled;
uint16_t _lastPpuAddr;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
memset(_chrBanks, 0, sizeof(_chrBanks));
_irqEnabled = false;
_irqCounter = 0;
SelectPRGPage(2, -2);
SelectPRGPage(3, -1);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> chrBanks = { _chrBanks, 8 };
Stream(_irqEnabled, _irqCounter, chrBanks);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if((addr >= 0xB000) && (addr <= 0xE003)) {
uint8_t index = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
bool lowBits = (addr & 0x01) == 0x00;
if (lowBits) {
_chrBanks[index] = (_chrBanks[index] & 0xF0) | (value & 0x0F);
} else {
_chrBanks[index] = (_chrBanks[index] & 0x0F) | (value << 4);
}
SelectCHRPage(index, _chrBanks[index]);
} else {
switch(addr & 0xF000) {
case 0x8000:
SelectPRGPage(0, value);
break;
case 0xA000:
SelectPRGPage(1, value);
break;
case 0x9000:
switch(value & 0x01) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
}
break;
}
}
switch(addr & 0xC00C) {
case 0x8004:
switch(value & 0x03) {
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
break;
case 0x800C:
_console->GetCpu()->SetIrqSource(IRQSource::External);
break;
case 0xC004:
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
case 0xC008:
_irqEnabled = true;
break;
case 0xC00C:
_irqEnabled = false;
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
}
}
void NotifyVRAMAddressChange(uint16_t addr) override
{
if ((_lastPpuAddr & 0x2000) && !(addr & 0x2000)) {
if(_irqEnabled) {
_irqCounter++;
if(_irqCounter == 84) {
_irqCounter = 0;
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
_lastPpuAddr = addr;
}
};

49
Core/Mapper277.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper277 : public BaseMapper
{
private:
bool _locked;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0, 0x08);
}
void Reset(bool softReset) override
{
_locked = false;
WriteRegister(0, 0x08);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(!_locked) {
uint8_t prgBank = value & 0x0F;
_locked = (value & 0x20) == 0x20;
if(value & 0x08) {
if(value & 0x01) {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank);
} else {
SelectPrgPage2x(0, prgBank & 0xFE);
}
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank | 0x07);
}
SelectCHRPage(0, 0);
SetMirroringType(value & 0x10 ? MirroringType::Horizontal : MirroringType::Vertical);
}
}
};

74
Core/Mapper319.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// NES 2.0 Mapper 319
// UNIF BMC-HP898F
class Mapper319 : public BaseMapper
{
private:
uint8_t _regs[3];
bool _unif_BankOrder;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
{
_regs[0] = _regs[1] = _regs[2] = 0;
_unif_BankOrder = false;
if (_romInfo.Hash.PrgCrc32 == 0xC25FD362) {
_unif_BankOrder = true;
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _regs[2]);
}
void UpdateState()
{
if(_unif_BankOrder) {
// The publicly-available UNIF ROM file of Prima Soft 9999999-in-1 has the order of the 16 KiB PRG-ROM banks
// slightly mixed up, so that the PRG A14 mode bit operates on A16 instead of A14.
// To obtain the correct bank order,
// use UNIF 16 KiB PRG banks 0, 4, 1, 5, 2, 6, 3, 7.
uint8_t prgReg = (_regs[1] >> 3) & 7;
uint8_t prgMask = (_regs[1] >> 4) & 4;
SelectCHRPage(0, (((_regs[0] >> 4) & 0x07) & ~(((_regs[0] & 0x01) << 2) | (_regs[0] & 0x02))));
SelectPRGPage(0, prgReg & (~prgMask));
SelectPRGPage(1, prgReg | prgMask);
} else {
if(_regs[1] & 0x40) {
SelectPrgPage2x(0, (_regs[1] >> 2) & 0xE);
} else {
uint8_t bank = ((_regs[1] >> 2) & 0x06) | ((_regs[1] >> 5) & 0x01);
SelectPRGPage(0, bank);
SelectPRGPage(1, bank);
}
SelectCHRPage(0, ((_regs[0] >> 4) & ~((_regs[0] << 2) & 0x04)) | ((_regs[2] << 2) & ((_regs[0] << 2) & 0x04)));
}
SetMirroringType(_regs[1] & 0x80 ? MirroringType::Vertical : MirroringType::Horizontal);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[(addr & 0x04) >> 2] = value;
UpdateState();
} else {
_regs[2] = value;
UpdateState();
}
}
};

46
Core/Mapper326.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// NES 2.0 Mapper 326 is used for a bootleg version of Contra/Gryzor.
// https://forums.nesdev.org/viewtopic.php?f=9&t=17352&p=218722#p218722
class Mapper326 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
SelectPRGPage(3, -1);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xE010) {
case 0x8000: SelectPRGPage(0, value); break;
case 0xA000: SelectPRGPage(1, value); break;
case 0xC000: SelectPRGPage(2, value); break;
}
if((addr & 0x8010) == 0x8010) {
switch(addr & 0x0F) {
case 0x00: SelectCHRPage(0, value); break;
case 0x01: SelectCHRPage(1, value); break;
case 0x02: SelectCHRPage(2, value); break;
case 0x03: SelectCHRPage(3, value); break;
case 0x04: SelectCHRPage(4, value); break;
case 0x05: SelectCHRPage(5, value); break;
case 0x06: SelectCHRPage(6, value); break;
case 0x07: SelectCHRPage(7, value); break;
case 0x08: SetNametable(0, value & 1); break;
case 0x09: SetNametable(1, value & 1); break;
case 0x0A: SetNametable(2, value & 1); break;
case 0x0B: SetNametable(3, value & 1); break;
default:
break;
}
}
}
};

132
Core/Mapper330.h Normal file
View File

@ -0,0 +1,132 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "Namco163Audio.h"
#include "BatteryManager.h"
// NES 2.0 Mapper 330 is used for a bootleg version of Namco's 三国志 II: 覇王の大陸 (Sangokushi II: Haō no Tairiku).
// http://forums.nesdev.org/viewtopic.php?f=9&t=17469
class Mapper330 : public BaseMapper
{
private:
unique_ptr<Namco163Audio> _audio;
uint16_t _irqCounter;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x400; }
virtual bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
_audio.reset(new Namco163Audio(_console));
_irqCounter = 0;
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Any);
RemoveRegisterRange(0x5000, 0xFFFF, MemoryOperation::Read);
SelectPRGPage(3, -1);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
SnapshotInfo audio{ _audio.get() };
Stream(_irqCounter, audio);
}
void LoadBattery() override
{
if(HasBattery()) {
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
_console->GetBatteryManager()->LoadBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
memcpy(_saveRam, batteryContent.data(), _saveRamSize);
memcpy(_audio->GetInternalRam(), batteryContent.data()+_saveRamSize, Namco163Audio::AudioRamSize);
}
}
void SaveBattery() override
{
if(HasBattery()) {
vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0);
memcpy(batteryContent.data(), _saveRam, _saveRamSize);
memcpy(batteryContent.data() + _saveRamSize, _audio->GetInternalRam(), Namco163Audio::AudioRamSize);
_console->GetBatteryManager()->SaveBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size());
}
}
void ProcessCpuClock() override
{
if(_irqCounter & 0x8000 && (_irqCounter & 0x7FFF) != 0x7FFF) {
_irqCounter++;
if((_irqCounter & 0x7FFF) == 0x7FFF) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
_audio->Clock();
}
uint8_t ReadRegister(uint16_t addr) override
{
if(addr & 0x800) {
return _audio->ReadRegister(addr);
}
return BaseMapper::ReadRegister(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xF000) {
case 0x4000:
if(addr & 0x800) {
_audio->WriteRegister(addr, value);
}
break;
case 0x8000:
case 0x9000:
case 0xA000:
case 0xB000:
if((addr & 0x400) && !(addr & 0x4000)) {
if(addr & 0x2000) {
_irqCounter = (_irqCounter & 0x00FF) | (value << 8);
_console->GetCpu()->ClearIrqSource(IRQSource::External);
} else {
_irqCounter = (_irqCounter & 0xFF00) | value;
}
} else {
uint8_t bankNumber = (addr - 0x8000) >> 11;
SelectCHRPage(bankNumber, value);
}
break;
case 0xC000:
case 0xD000:
if(!(addr & 0x400)) {
uint8_t bankNumber = (addr - 0xC000) >> 11;
SetNametable(bankNumber, value & 0x01);
}
break;
case 0xE000:
case 0xF000:
if(((addr & 0xF000) == 0xF000) && (addr & 0x800)) {
_audio->WriteRegister(addr, value);
} else {
if(!(addr & 0x400)) {
uint8_t bankNumber = (addr - 0xE000) >> 11;
SelectPRGPage(bankNumber, value);
}
}
break;
default:
break;
}
}
};

115
Core/Mapper357.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
class Mapper357 : public BaseMapper
{
private:
const uint8_t _dipswitchLut[4] = {
0x00, 0x08, 0x10, 0x18
};
const uint8_t bankLut[2][8] = {
{ 4, 3, 5, 3, 6, 3, 7, 3 },
{ 1, 1, 5, 1, 4, 1, 5, 1 }
};
uint16_t _irqCounter;
bool _irqEnabled;
uint8_t _smb2jReg;
uint8_t _unromReg;
bool _prgSwap;
uint8_t _dipswitch;
protected:
uint16_t RegisterStartAddress() override { return 0x4020; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint32_t GetDipSwitchCount() override { return 2; }
void InitMapper() override
{
_irqCounter = 0;
_irqEnabled = false;
_unromReg = 0;
_smb2jReg = 0;
_prgSwap = 0;
UpdateState();
}
void Reset(bool softReset) override
{
_dipswitch = _dipswitchLut[GetDipSwitches() & 0x03];
_irqCounter = 0;
_irqEnabled = false;
UpdateState();
}
void UpdateState()
{
if(_dipswitch == 0) {
SetCpuMemoryMapping(0x6000, 0x7FFF, _prgSwap ? 0x00 : 0x02, PrgMemoryType::PrgRom);
SelectPRGPage(0, _prgSwap ? 0x00 : 0x01);
SelectPRGPage(1, 0x00);
SelectPRGPage(2, bankLut[_prgSwap][_smb2jReg]);
SelectPRGPage(3, _prgSwap ? 0x08 : 0x0A);
} else {
SelectPrgPage2x(0, (_dipswitch | _unromReg) << 1);
SelectPrgPage2x(1, (_dipswitch | 0x07) << 1);
}
SelectCHRPage(0, 0);
SetMirroringType((_dipswitch == 0x18) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_irqCounter, _irqEnabled, _unromReg, _smb2jReg, _prgSwap, _dipswitch);
if(!saving) {
UpdateState();
}
}
void ProcessCpuClock() override
{
if(_irqEnabled) {
_irqCounter++;
if(_irqCounter == 0x1000) {
_irqCounter = 0;
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr & 0x8000) {
_unromReg = value & 0x07;
UpdateState();
}
if((addr & 0x71FF) == 0x4022) {
_smb2jReg = value & 0x07;
UpdateState();
}
if((addr & 0x71FF) == 0x4120) {
_prgSwap = (value & 0x01) == 0x01;
UpdateState();
}
if((addr & 0x71FF) == 0x4122) {
_irqEnabled = (value & 0x01) == 0x01;
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
}
};

265
Core/Mapper359.h Normal file
View File

@ -0,0 +1,265 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper359 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
// $8000 - $8003
uint8_t _prgBanks[4];
// $9000
uint8_t _outerPrgBank;
// $9001
uint8_t _prgMask;
uint8_t _chrMask;
// $9002
uint8_t _mirroring;
// $9003
uint16_t _outerChrBank;
// $A000 - $B003
uint8_t _chrBanks[8];
// IRQ
bool _irqPpuA12;
bool _irqAutoEnable;
bool _irqEnabled;
bool _irqReload;
uint16_t _irqCounter;
A12Watcher _a12Watcher;
void InitMapper() override
{
_prgBanks[0] = -4;
_prgBanks[1] = -3;
_prgBanks[2] = -2;
_prgBanks[3] = -1;
_chrBanks[0] = 0x00;
_chrBanks[1] = 0x01;
_chrBanks[2] = 0x02;
_chrBanks[3] = 0x03;
_chrBanks[4] = 0x04;
_chrBanks[5] = 0x05;
_chrBanks[6] = 0x06;
_chrBanks[7] = 0x07;
_outerPrgBank = 0x00;
_outerChrBank = 0x00;
_prgMask = 0x3F;
_chrMask = 0xFF;
_mirroring = 0;
_irqCounter = 0;
_irqPpuA12 = false;
_irqAutoEnable = false;
_irqReload = false;
_irqEnabled = false;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
SnapshotInfo a12Watcher { &_a12Watcher };
ArrayInfo<uint8_t> prgBanks { _prgBanks, 4 };
ArrayInfo<uint8_t> chrBanks { _chrBanks, 8 };
Stream(
_outerPrgBank, _outerChrBank,
_prgMask, _chrMask, _mirroring,
_irqCounter, _irqPpuA12, _irqReload,
_irqEnabled, _irqAutoEnable,
prgBanks, chrBanks, a12Watcher
);
if(!saving) {
UpdateState();
}
}
void UpdatePrgMapping()
{
SelectPRGPage(0, _outerPrgBank | (_prgBanks[0] & _prgMask));
SelectPRGPage(1, _outerPrgBank | (_prgBanks[1] & _prgMask));
SelectPRGPage(2, _outerPrgBank | (_prgBanks[2] & _prgMask));
SelectPRGPage(3, _outerPrgBank | ((-1) & _prgMask));
SetCpuMemoryMapping(0x6000, 0x7FFF, _outerPrgBank | (_prgBanks[3] & _prgMask), PrgMemoryType::PrgRom);
}
virtual void UpdateChrMapping()
{
if(HasChrRom()) {
SelectCHRPage(0, _outerChrBank | (_chrBanks[0] & _chrMask));
SelectCHRPage(1, _outerChrBank | (_chrBanks[1] & _chrMask));
SelectCHRPage(2, _outerChrBank | (_chrBanks[2] & _chrMask));
SelectCHRPage(3, _outerChrBank | (_chrBanks[3] & _chrMask));
SelectCHRPage(4, _outerChrBank | (_chrBanks[4] & _chrMask));
SelectCHRPage(5, _outerChrBank | (_chrBanks[5] & _chrMask));
SelectCHRPage(6, _outerChrBank | (_chrBanks[6] & _chrMask));
SelectCHRPage(7, _outerChrBank | (_chrBanks[7] & _chrMask));
}
}
void UpdateMirroring()
{
switch(_mirroring) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
}
void UpdateState()
{
UpdatePrgMapping();
UpdateChrMapping();
UpdateMirroring();
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xF000) {
case 0x8000:
_prgBanks[addr & 0x03] = value;
UpdatePrgMapping();
break;
case 0x9000:
switch(addr & 0x03) {
case 0:
_outerPrgBank = (value & 0x38) << 1;
UpdatePrgMapping();
break;
case 1:
switch(value & 0x03) {
case 0: _prgMask = 0x3F; break;
case 1: _prgMask = 0x1F; break;
case 2: _prgMask = 0x2F; break;
case 3: _prgMask = 0x0F; break;
}
_chrMask = (value & 0x40) ? 0xFF : 0x7F;
UpdatePrgMapping();
UpdateChrMapping();
break;
case 2:
_mirroring = value & 0x03;
UpdateMirroring();
break;
case 3:
_outerChrBank = value << 7;
UpdateChrMapping();
break;
}
break;
case 0xA000:
_chrBanks[0 | (addr & 0x03)] = value;
UpdateChrMapping();
break;
case 0xB000:
_chrBanks[4 | (addr & 0x03)] = value;
UpdateChrMapping();
break;
case 0xC000:
switch(addr & 0x03) {
case 0:
if(_irqAutoEnable) {
_irqEnabled = false;
}
_irqCounter &= 0xFF00;
_irqCounter |= value;
break;
case 1:
if(_irqAutoEnable) {
_irqEnabled = true;
}
_irqCounter &= 0x00FF;
_irqCounter |= value << 8;
_irqReload = true;
break;
case 2:
_irqEnabled = (value & 0x01) == 0x01;
_irqPpuA12 = (value & 0x02) == 0x02;
_irqAutoEnable = (value & 0x04) == 0x04;
break;
case 3:
_irqEnabled = (value & 0x01) == 0x01;
break;
}
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
}
}
public:
void ProcessCpuClock() override
{
if(_irqPpuA12)
return;
if(_irqEnabled && _irqCounter) {
_irqCounter--;
if(_irqCounter == 0) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
void NotifyVRAMAddressChange(uint16_t addr) override
{
uint8_t counter;
if(!_irqPpuA12)
return;
switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) {
case A12StateChange::None:
case A12StateChange::Fall:
break;
case A12StateChange::Rise:
counter = _irqCounter & 0xFF;
if(counter == 0 || _irqReload) {
counter = (_irqCounter & 0xFF00) >> 8;
} else {
counter--;
}
if(counter == 0 && _irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
_irqReload =false;
_irqCounter &= 0xFF00;
_irqCounter |= counter;
break;
}
}
};

31
Core/Mapper360.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// 31-in-1 multicart using dipswitch
class Mapper360 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint32_t GetDipSwitchCount() override { return 5; }
void InitMapper() override
{
RemoveRegisterRange(0x8000, 0xFFFF);
uint32_t _dipsw = GetDipSwitches();
if (_dipsw < 2)
SelectPrgPage2x(0, _dipsw & 0xFE);
else {
SelectPRGPage(0, _dipsw);
SelectPRGPage(1, _dipsw);
}
SelectCHRPage(0, _dipsw);
SetMirroringType((_dipsw & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

162
Core/Mapper362.h Normal file
View File

@ -0,0 +1,162 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "VrcIrq.h"
#include "Console.h"
// VRC4 Clone with highest CHR-ROM bank bits are repurposed as outer bank bits
class Mapper362 : public BaseMapper
{
private:
unique_ptr<VrcIrq> _irq;
uint8_t _prgReg0;
uint8_t _prgReg1;
uint8_t _prgMode;
uint8_t _mirroring;
uint8_t _hiCHRRegs[8];
uint8_t _loCHRRegs[8];
uint8_t _game;
uint8_t _currentChrBank;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
_irq.reset(new VrcIrq(_console));
_prgMode = 0;
_prgReg0 = 0;
_prgReg1 = 1;
_mirroring = 0;
for(int i = 0; i < 8; i++) {
_loCHRRegs[i] = i;
_hiCHRRegs[i] = 0;
}
_currentChrBank = 0;
_game = 0;
UpdateState();
}
void Reset(bool softReset) override
{
if(!softReset || (_prgSize <= (512 * 1024))) {
_game = 0;
} else {
_game = (_game + 1) & 0x01;
}
UpdateState();
}
void ProcessCpuClock() override
{
_irq->ProcessCpuClock();
}
void UpdateState()
{
uint32_t base = (_game == 0) ? ((_hiCHRRegs[_currentChrBank] << 4) & 0x180) : 0x200;
uint32_t mask = (_game == 0) ? 0x7F : 0x1FF;
for(int i = 0; i < 8; i++) {
uint32_t page = _loCHRRegs[i] | (_hiCHRRegs[i] << 4);
SelectCHRPage(i, base | (page & mask));
}
base = (_game == 0) ? ((_hiCHRRegs[_currentChrBank] << 1) & 0x30) : 0x40;
mask = 0x0F;
if(_prgMode == 0) {
SelectPRGPage(0, base | (_prgReg0 & mask));
SelectPRGPage(1, base | (_prgReg1 & mask));
SelectPRGPage(2, base | ((-2) & mask));
SelectPRGPage(3, base | ((-1) & mask));
} else {
SelectPRGPage(0, base | ((-2) & mask));
SelectPRGPage(1, base | (_prgReg1 & mask));
SelectPRGPage(2, base | (_prgReg0 & mask));
SelectPRGPage(3, base | ((-1) & mask));
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
addr &= 0xF003;
if(addr >= 0x8000 && addr <= 0x8003) {
_prgReg0 = value;
} else if(addr >= 0x9000 && addr <= 0x9003) {
switch(addr & 0x03) {
case 0:
case 1:
switch(value & 0x03) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
break;
case 2:
case 3:
_prgMode = (value >> 1) & 0x01;
break;
}
} else if(addr >= 0xA000 && addr <= 0xA003) {
_prgReg1 = value;
} else {
if(addr >= 0xB000 && addr <= 0xE003) {
uint8_t regNumber = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
bool lowBits = (addr & 0x01) == 0x00;
if(lowBits) {
//The other reg contains the low 4 bits
_loCHRRegs[regNumber] = value & 0x0F;
} else {
//One reg contains the high 5 bits
_hiCHRRegs[regNumber] = value & 0x1F;
}
} else if(addr == 0xF000) {
_irq->SetReloadValueNibble(value, false);
} else if(addr == 0xF001) {
_irq->SetReloadValueNibble(value, true);
} else if(addr == 0xF002) {
_irq->SetControlValue(value);
} else if(addr == 0xF003) {
_irq->AcknowledgeIrq();
}
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> loChrRegs = { _loCHRRegs, 8 };
ArrayInfo<uint8_t> hiChrRegs = { _hiCHRRegs, 8 };
SnapshotInfo irq{ _irq.get() };
Stream(_prgReg0, _prgReg1, _prgMode, loChrRegs, hiChrRegs, irq, _game, _currentChrBank);
}
uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType type) override
{
uint8_t bank = addr >> 10;
if(!(addr & 0x2000) && (_game == 0) && (_currentChrBank != bank)) {
_currentChrBank = bank;
UpdateState();
}
return BaseMapper::MapperReadVRAM(addr, type);
}
};

79
Core/Mapper368.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// YUNG-08
class Mapper368 : public BaseMapper
{
private:
uint8_t _latch;
bool _irqEnabled;
uint16_t _irqCounter;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x4022; }
uint16_t RegisterEndAddress() override { return 0x4FFF; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
_irqEnabled = false;
_irqCounter = 0;
_latch = 0;
SetCpuMemoryMapping(0x6000, 0x7FFF, 0x02, PrgMemoryType::PrgRom);
SelectPRGPage(0, 1);
SelectPRGPage(1, 0);
SelectPRGPage(3, 8);
SelectCHRPage(0, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_latch, _irqEnabled, _irqCounter);
}
void ProcessCpuClock() override
{
if(_irqEnabled) {
_irqCounter++;
if((_irqCounter & 0xFFF) == 0) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
uint8_t ReadRegister(uint16_t addr) override
{
if((addr & 0x1FF) == 0x122) {
return (0x8A | (_latch & 0x35));
}
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x1FF) {
case 0x022:
// bank order = { 4, 3, 5, 3, 6, 3, 7, 3 };
SelectPRGPage(2, (value & 0x01) ? 0x03 : (0x04 | value >> 1));
break;
case 0x122:
_latch = value;
_irqEnabled = (value & 0x01) != 0;
if(!_irqEnabled) {
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
break;
}
}
};

78
Core/Mapper375.h Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#include "BaseMapper.h"
class Mapper375 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t latchea;
uint8_t latched;
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(latchea, latched);
}
void UpdateState()
{
uint16_t prgBank = ((latchea >> 2) & 0x1F) | ((latchea >> 3) & 0x20) | ((latchea >> 4) & 0x40);
bool sFlag = (latchea & 0x01) == 0x01;
bool lFlag = ((latchea >> 9) & 0x01) == 0x01;
bool prgMode = ((latchea >> 7) & 0x01) == 0x01;
uint16_t p0 = prgBank;
if((latchea & 0x800) == 0x800)
p0 = (prgBank & ~0x07) | (latched & 0x07);
if (prgMode) {
if (sFlag) {
SelectPrgPage2x(0, prgBank & 0xFE);
} else {
SelectPRGPage(0, p0);
SelectPRGPage(1, prgBank);
}
} else {
if (sFlag) {
if (lFlag) {
SelectPRGPage(0, p0 & 0x7E);
SelectPRGPage(1, prgBank | 0x07);
} else {
SelectPRGPage(0, p0 & 0x7E);
SelectPRGPage(1, prgBank & 0x78);
}
} else {
if (lFlag) {
SelectPRGPage(0, p0);
SelectPRGPage(1, prgBank | 0x07);
} else {
SelectPRGPage(0, p0);
SelectPRGPage(1, prgBank & 0x78);
}
}
}
// protect CHR-RAM on nrom modes
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (latchea & 0x80) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SetMirroringType((latchea & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(latchea & 0x800) {
latched = value;
} else {
latchea = addr;
latched = value;
}
UpdateState();
}
};

63
Core/Mapper380.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include "BaseMapper.h"
class Mapper380 : public BaseMapper
{
protected:
uint32_t GetDipSwitchCount() override { return 4; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
bool _mFlag;
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void Reset(bool softreset) override
{
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_mFlag);
}
uint8_t ReadRegister(uint16_t addr) override
{
if(_mFlag && _romInfo.SubMapperID == 0) {
addr |= GetDipSwitches();
}
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint16_t prgBank = (addr >> 2) & 0x1F;
bool sFlag = (addr & 0x01) == 0x01;
bool prgMode = ((addr >> 9) & 0x01) == 0x01;
_mFlag = ((addr >> 8) & 0x01) == 0x01;
if (prgMode) {
if (sFlag) {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank);
} else {
SelectPrgPage2x(0, prgBank & 0xFE);
}
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank | 0x07 | (((_romInfo.SubMapperID == 1) && (addr & 0x100)) ? 0x08: 0x00));
}
// protect CHR-RAM on nrom modes
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (addr & 0x80) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

68
Core/Mapper382.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper382 : public BaseMapper
{
private:
uint8_t _outerPrg;
uint8_t _innerPrg;
uint8_t _prgMode;
uint8_t _mirroring;
bool _locked;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
UpdateState();
}
void Reset(bool softReset) override
{
_outerPrg = 0;
_innerPrg = 0;
_prgMode = 0;
_mirroring = 0;
_locked = false;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_outerPrg, _innerPrg, _prgMode, _mirroring, _locked);
}
void UpdateState()
{
if (_prgMode) { // BNROM
SelectPrgPage2x(0, ((_outerPrg << 2) | (_innerPrg & 0x03)) << 1);
} else { // UNROM
SelectPRGPage(0, (_outerPrg << 3) | (_innerPrg & 0x07));
SelectPRGPage(1, (_outerPrg << 3) | 0x07);
}
SetMirroringType(!_mirroring ? MirroringType::Vertical : MirroringType::Horizontal);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (!_locked) {
_outerPrg = addr & 0x07;
_prgMode = (addr & 0x08) == 0x08;
_mirroring = (addr & 0x10) == 0x10;
_locked = (addr & 0x20) == 0x20;
}
_innerPrg = value & 0x07;
UpdateState();
}
};

25
Core/Mapper385.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper385 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectPRGPage(1, 0);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, (addr >> 1));
SelectPRGPage(1, (addr >> 1));
SetMirroringType(addr & 0x01 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

58
Core/Mapper389.h Normal file
View File

@ -0,0 +1,58 @@
#pragma once
#include "BaseMapper.h"
class Mapper389 : public BaseMapper
{
private:
uint8_t _regs[4];
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
memset(_regs, 0, sizeof(_regs));
UpdateState();
}
void Reset(bool softreset) override
{
memset(_regs, 0, sizeof(_regs));
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _regs[2], _regs[3]);
}
void UpdateState()
{
if (_regs[1] & 0x02) {
SelectPRGPage(0, (_regs[0] >> 2) | ((_regs[2] >> 2) & 0x03));
SelectPRGPage(1, (_regs[0] >> 2) | 0x03);
} else
SelectPrgPage2x(0, _regs[0] >> 2);
SelectCHRPage(0, ((_regs[1] >> 1) & 0xFC) | (_regs[2] & 0x03));
SetMirroringType((_regs[0] & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr <= 0x8FFF) {
_regs[0] = addr & 0xFF;
} else if (addr <= 0x9FFF) {
_regs[1] = addr & 0xFF;
} else {
_regs[2] = addr & 0xFF;
}
UpdateState();
}
};

163
Core/Mapper398.h Normal file
View File

@ -0,0 +1,163 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "VrcIrq.h"
#include "Console.h"
// VRC4 Clone with GNROM-like mode
class Mapper398 : public BaseMapper
{
private:
unique_ptr<VrcIrq> _irq;
uint8_t _prgReg0;
uint8_t _prgReg1;
uint8_t _prgMode;
uint8_t _mirroring;
uint8_t _hiCHRRegs[8];
uint8_t _loCHRRegs[8];
uint8_t _outerBank;
uint8_t _currentChrBank;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
_irq.reset(new VrcIrq(_console));
_prgMode = 0;
_prgReg0 = 0;
_prgReg1 = 1;
_mirroring = 0;
for(int i = 0; i < 8; i++) {
_loCHRRegs[i] = i;
_hiCHRRegs[i] = 0;
}
_currentChrBank = 0;
_outerBank = 0xC0;
UpdateState();
}
void Reset(bool softReset) override
{
_outerBank = 0xC0;
UpdateState();
}
void ProcessCpuClock() override
{
_irq->ProcessCpuClock();
}
void UpdateState()
{
if(_outerBank & 0x80) {
uint8_t chrbank = ((_outerBank >> 3) & 0x08) | (_loCHRRegs[_currentChrBank] & 0x07);
SelectChrPage8x(0, (0x40 | chrbank) << 3);
} else {
for(int i = 0; i < 8; i++) {
uint32_t page = _loCHRRegs[i] | (_hiCHRRegs[i] << 4);
SelectCHRPage(i, page);
}
}
if(_outerBank & 0x80) {
uint32_t prgBank = ((_outerBank >> 5) & 6) | ((_loCHRRegs[_currentChrBank] >> 2) & 0x01);
SelectPrgPage4x(0, prgBank << 2);
} else {
if(_prgMode == 0) {
SelectPRGPage(0, (_prgReg0 & 0x0F));
SelectPRGPage(1, (_prgReg1 & 0x0F));
SelectPRGPage(2, ((-2) & 0x0F));
SelectPRGPage(3, ((-1) & 0x0F));
} else {
SelectPRGPage(0, ((-2) & 0x0F));
SelectPRGPage(1, (_prgReg1 & 0x0F));
SelectPRGPage(2, (_prgReg0 & 0x0F));
SelectPRGPage(3, ((-1) & 0x0F));
}
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_outerBank = addr & 0xFF;
addr &= 0xF003;
if(addr >= 0x8000 && addr <= 0x8003) {
_prgReg0 = value;
} else if(addr >= 0x9000 && addr <= 0x9003) {
switch(addr & 0x03) {
case 0:
case 1:
switch(value & 0x03) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
break;
case 2:
case 3:
_prgMode = (value >> 1) & 0x01;
break;
}
} else if(addr >= 0xA000 && addr <= 0xA003) {
_prgReg1 = value;
} else {
if(addr >= 0xB000 && addr <= 0xE003) {
uint8_t regNumber = ((((addr >> 12) & 0x07) - 3) << 1) + ((addr >> 1) & 0x01);
bool lowBits = (addr & 0x01) == 0x00;
if(lowBits) {
//The other reg contains the low 4 bits
_loCHRRegs[regNumber] = value & 0x0F;
} else {
//One reg contains the high 5 bits
_hiCHRRegs[regNumber] = value & 0x1F;
}
} else if(addr == 0xF000) {
_irq->SetReloadValueNibble(value, false);
} else if(addr == 0xF001) {
_irq->SetReloadValueNibble(value, true);
} else if(addr == 0xF002) {
_irq->SetControlValue(value);
} else if(addr == 0xF003) {
_irq->AcknowledgeIrq();
}
}
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> loChrRegs = { _loCHRRegs, 8 };
ArrayInfo<uint8_t> hiChrRegs = { _hiCHRRegs, 8 };
SnapshotInfo irq{ _irq.get() };
Stream(_prgReg0, _prgReg1, _prgMode, loChrRegs, hiChrRegs, irq, _outerBank, _currentChrBank);
}
uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType type) override
{
uint8_t bank = addr >> 10;
if(!(addr & 0x2000) && (_outerBank & 0x80) && (_currentChrBank != bank)) {
_currentChrBank = bank;
UpdateState();
}
return BaseMapper::MapperReadVRAM(addr, type);
}
};

65
Core/Mapper400.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper400 : public BaseMapper
{
private:
uint8_t _regs[2];
uint8_t _led;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
memset(_regs, 0x00, sizeof(_regs));
_regs[0] = 0x80;
AddRegisterRange(0x7800, 0x7FFF, MemoryOperation::Write);
WriteRegister(0xC000, 0);
}
virtual void Reset(bool softReset) override
{
_regs[0] = 0x80;
WriteRegister(0xC000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _led);
}
void UpdateState()
{
SelectPRGPage(0, (_regs[0] & 0xF8) | (_regs[1] & 0x07));
SelectPRGPage(1, (_regs[0] & 0xF8) | 0x07);
SelectCHRPage(0, _regs[1] >> 5);
if(!(_regs[0] == 0x80)) {
SetMirroringType((_regs[0] & 0x20) ? MirroringType::Horizontal : MirroringType::Vertical);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[0] = value;
UpdateState();
} else {
if(addr < 0xC000) {
_led = value;
} else {
_regs[1] = value;
UpdateState();
}
}
}
};

43
Core/Mapper402.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// J-2282 - 22-in-1 Olympic Games multicart
class Mapper402 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
virtual void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr & 0x0800)
SetCpuMemoryMapping(0x6000, 0x7FFF, (((addr & 0x1F) << 1) | 0x03), PrgMemoryType::PrgRom);
else {
RemoveCpuMemoryMapping(0x6000, 0x7FFF);
}
if (addr & 0x0040) {
SelectPrgPage2x(0, (addr & 0x1F) << 1);
SelectPrgPage2x(1, (addr & 0x1F) << 1);
} else {
SelectPrgPage4x(0, (addr & 0x1E) << 1);
}
SelectCHRPage(0, 0);
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, !(addr & 0x0400) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SetMirroringType((addr & 0x80) ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

75
Core/Mapper403.h Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// 89433
class Mapper403 : public BaseMapper
{
private:
uint8_t _regs[4];
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
virtual bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
memset(_regs, 0, sizeof(_regs));
AddRegisterRange(0x4100, 0x5FFF, MemoryOperation::Write);
// TODO: Hack for TetrisA from Tetris Family 19-in-1 NO 1683 to work.
AddRegisterRange(0x6000, 0x7FFF, MemoryOperation::Read);
UpdateState();
}
virtual void Reset(bool softReset) override
{
memset(_regs, 0, sizeof(_regs));
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _regs[2], _regs[3]);
}
virtual void UpdateState()
{
if (_regs[2] & 0x01) {
SelectPRGPage(0, (_regs[0] >> 1) & 0x3F);
SelectPRGPage(1, (_regs[0] >> 1) & 0x3F);
} else {
SelectPrgPage2x(0, (_regs[0] >> 1) & 0x3E);
}
SelectCHRPage(0, _regs[1] & 0x03);
SetMirroringType((_regs[2] & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
}
uint8_t ReadRegister(uint16_t addr) override
{
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr < 0x8000) {
if (addr & 0x0100) {
_regs[(addr & 0x03)] = value;
UpdateState();
}
} else {
if (_regs[2] & 0x04) {
_regs[1] = value;
UpdateState();
}
}
}
};

33
Core/Mapper409.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// retroUSB DPCMcart
class Mapper409 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
RemoveRegisterRange(0x0000, 0xFFFF, MemoryOperation::Any);
AddRegisterRange(0xC000, 0xCFFF, MemoryOperation::Write);
SelectPRGPage(1, -1);
SelectCHRPage(0, 0);
WriteRegister(0xC000, 0);
}
virtual void Reset(bool softReset) override
{
WriteRegister(0xC000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, addr & 0x0FFF);
}
};

140
Core/Mapper413.h Normal file
View File

@ -0,0 +1,140 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
#include "A12Watcher.h"
class Mapper413 : public BaseMapper
{
private:
uint32_t _serialAddress;
uint8_t _serialControl;
uint8_t _irqCounter;
uint8_t _irqReloadValue;
bool _irqEnabled;
A12Watcher _a12Watcher;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x1000; }
virtual uint16_t GetCHRPageSize() override { return 0x1000; }
virtual bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
_serialAddress = 0;
_serialControl = 0;
_irqCounter = 0;
_irqReloadValue = 0;
_irqEnabled = false;
SetCpuMemoryMapping(0x5000, 0x5FFF, 1, PrgMemoryType::PrgRom);
SetCpuMemoryMapping(0x6000, 0x6FFF, 0, PrgMemoryType::PrgRom);
SetCpuMemoryMapping(0x7000, 0x7FFF, 1, PrgMemoryType::PrgRom);
SelectPrgPage2x(0, 0);
SelectPrgPage2x(1, 0);
SelectPRGPage(5, 0x07);
SelectPRGPage(6, 0x08);
SelectPRGPage(7, 0x09);
SelectCHRPage(0, 0);
SelectCHRPage(1, -3);
AddRegisterRange(0x4800, 0x4FFF, MemoryOperation::Read);
RemoveRegisterRange(0x8000, 0xBFFF, MemoryOperation::Read);
RemoveRegisterRange(0xD000, 0xFFFF, MemoryOperation::Read);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_serialAddress, _serialControl,
_irqCounter, _irqReloadValue, _irqEnabled);
}
void NotifyVRAMAddressChange(uint16_t addr) override
{
switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) {
case A12StateChange::None:
case A12StateChange::Fall:
break;
case A12StateChange::Rise:
if(_irqCounter == 0) {
_irqCounter = _irqReloadValue;
} else {
_irqCounter--;
}
if (_irqCounter == 0 && _irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
uint8_t ReadRegister(uint16_t addr) override
{
if(_serialControl & 0x02) {
return (_miscRom[_serialAddress++ & (_miscRomSize - 1)]);
} else {
return (_miscRom[_serialAddress & (_miscRomSize - 1)]);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch (addr & 0xF000) {
case 0x8000:
_irqReloadValue = value;
break;
case 0x9000:
_irqCounter = 0;
break;
case 0xA000:
case 0xB000:
_irqEnabled = (addr & 0x1000) != 0;
if (_irqEnabled == 0) {
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
break;
case 0xC000:
_serialAddress = (_serialAddress << 1) | (value >> 7);
break;
case 0xD000:
_serialControl = value;
break;
case 0xE000:
case 0xF000:
switch(value >> 6) {
case 0:
value <<= 1;
SetCpuMemoryMapping(0x6000, 0x7FFF, value, PrgMemoryType::PrgRom);
break;
case 1:
value <<= 1;
SelectPRGPage(0, value);
SelectPRGPage(1, value + 1);
break;
case 2:
value <<= 1;
SelectPRGPage(2, value);
SelectPRGPage(3, value + 1);
break;
case 3:
SelectCHRPage(0, value);
break;
}
break;
}
}
};

63
Core/Mapper414.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
class Mapper414 : public BaseMapper
{
private:
const uint16_t _dpswlut[5] = { 0x0010, 0x0000, 0x0030, 0x0070, 0x00F0 };
uint16_t _dipSwitch;
uint16_t _latch;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
virtual bool AllowRegisterRead() override { return true; }
virtual bool HasBusConflicts() override { return true; }
virtual uint32_t GetDipSwitchCount() override { return 3; }
void InitMapper() override
{
uint32_t index = GetDipSwitches();
if (index > 4)
index = 0;
_dipSwitch = _dpswlut[index];
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_latch);
}
uint8_t ReadRegister(uint16_t addr) override
{
if (addr >= 0xC000) {
if (!(_latch & 0x100) && (_latch & _dipSwitch)) {
return _console->GetMemoryManager()->GetOpenBus();
}
}
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_latch = addr;
if (_latch & 0x2000)
SelectPrgPage2x(0, (_latch >> 1) & 0xFE);
else {
SelectPRGPage(0, _latch >> 1);
SelectPRGPage(1, _latch >> 1);
}
SelectCHRPage(0, value & 0x03);
SetMirroringType((_latch & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

29
Core/Mapper415.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
// FDS Conversion of Roger Rabbit to Lucky Rabbit
class Mapper415 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, -4);
SelectPRGPage(1, -3);
SelectPRGPage(2, -2);
SelectPRGPage(3, -1);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SetCpuMemoryMapping(0x6000, 0x7FFF, value & 0x0F, PrgMemoryType::PrgRom);
SetMirroringType((value & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

98
Core/Mapper416.h Normal file
View File

@ -0,0 +1,98 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "CPU.h"
// N-32 cartridge conversion 4-in-1
class Mapper416 : public BaseMapper
{
private:
uint8_t _regs[2];
uint16_t _irqCounter;
bool _irqEnabled;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
AddRegisterRange(0x4000, 0x5FFF, MemoryOperation::Write);
RemoveRegisterRange(0xA000, 0xFFFF, MemoryOperation::Write);
SetCpuMemoryMapping(0x6000, 0x7FFF, 0x07, PrgMemoryType::PrgRom);
_regs[0] = _regs[1] = 0;
_irqEnabled = false;
_irqCounter = 0;
UpdateState();
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _irqEnabled, _irqCounter);
}
void UpdateState()
{
if (_regs[1] & 0x08) {
uint8_t page = ((_regs[1] >> 1) & 0x04) | ((_regs[1] >> 6) & 0x02) | ((_regs[1] >> 5) & 0x01);
page <<= 1;
if (_regs[1] & 0x80) {
SelectPrgPage4x(0, page & 0xFE);
} else {
if (_regs[1] & 0x40) {
SelectPrgPage2x(0, page);
SelectPrgPage2x(1, page);
} else {
SelectPRGPage(0, page);
SelectPRGPage(1, page);
SelectPRGPage(2, page);
SelectPRGPage(3, page);
}
}
} else {
SelectPRGPage(0, 0x00);
SelectPRGPage(1, 0x01);
SelectPRGPage(2, (_regs[0] & 0x08) | ((_regs[0] << 2) & 0x04) | ((_regs[0] >> 1) & 0x03));
SelectPRGPage(3, 0x03);
}
SelectCHRPage(0x0, _regs[1] >> 1 & 0x03);
SetMirroringType((_regs[1] & 0x04) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr < 0x8000) { // 0x4000-0x5FFF
if (addr & 0x20) {
if (addr & 0x100) {
_irqEnabled = (value & 0x01) == 0x01;
} else {
_regs[0] = value;
}
}
} else { // 0x8000 - 0x9FFF
_regs[1] = value;
}
UpdateState();
}
void ProcessCpuClock() override
{
if (_irqEnabled) {
_irqCounter++;
if (_irqCounter & 0x1000) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
} else {
_irqCounter = 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
}
}
};

71
Core/Mapper417.h Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// Bootleg copy of Batman: The Video Game
class Mapper417 : public BaseMapper
{
private:
uint16_t _irqCounter;
bool _irqEnabled;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x0400; }
void InitMapper() override
{
_irqEnabled = false;
_irqCounter = 0;
SelectPRGPage(3, -1);
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_irqEnabled, _irqCounter);
}
void ProcessCpuClock() override
{
_irqCounter++;
if (_irqCounter & 0x400) {
if (_irqEnabled) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch ((addr >> 4) & 0x07) {
case 0:
SelectPRGPage(addr & 0x03, value);
break;
case 1:
SelectCHRPage(addr & 0x03, value);
break;
case 2:
SelectCHRPage((addr & 0x03) | 0x04, value);
break;
case 3:
_irqEnabled = true;
_irqCounter = 0;
break;
case 4:
_irqEnabled = false;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
case 5:
SetNametable(addr & 0x03, value & 0x01);
break;
}
}
};

74
Core/Mapper428.h Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper428 : public BaseMapper
{
private:
uint8_t _regs[4];
uint8_t _chrLatch;
protected:
uint32_t GetDipSwitchCount() override { return 2; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
_chrLatch = 0;
memset(_regs, 0, sizeof(_regs));
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _regs[2], _regs[3], _chrLatch);
if(!saving) {
UpdateState();
}
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
_chrLatch = 0;
memset(_regs, 0, sizeof(_regs));
UpdateState();
}
void UpdateState()
{
if (_regs[1] & 0x10) {
SelectPrgPage2x(0, (_regs[1] >> 5) & 0xFE);
} else {
SelectPRGPage(0, _regs[1] >> 5);
SelectPRGPage(1, _regs[1] >> 5);
}
SelectCHRPage(0, ((_regs[1] & 0x07) & ~(_regs[2] >> 6)) | (_chrLatch & (_regs[2] >> 6)));
SetMirroringType((_regs[1] & 0x08) ? MirroringType::Horizontal : MirroringType::Vertical);
}
uint8_t ReadRegister(uint16_t addr) override
{
return (_console->GetMemoryManager()->GetOpenBus() & 0xFC) | (GetDipSwitches() & 0x03);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[addr & 0x03] = value;
} else {
_chrLatch = value;
}
UpdateState();
}
};

26
Core/Mapper429.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper429 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0x04);
}
virtual void Reset(bool softReset) override
{
WriteRegister(0x8000, 0x04);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, value >> 2);
SelectCHRPage(0, value & 0x03);
}
};

51
Core/Mapper431.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// Realtec GN-91B multicart.
class Mapper431 : public BaseMapper
{
private:
uint8_t _regs[2];
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_regs[0] = _regs[1] = 0;
SelectCHRPage(0, 0);
UpdateState();
}
virtual void Reset(bool softReset) override
{
_regs[0] = _regs[1] = 0;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1]);
}
void UpdateState()
{
SelectPRGPage(0, ((_regs[0] >> 2) & 0x08) | (_regs[1] & 0x07));
SelectPRGPage(1, ((_regs[0] >> 2) & 0x08) | 0x07);
SetMirroringType((_regs[0] & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (addr < 0xC000) {
_regs[0] = value;
} else {
_regs[1] = value;
}
UpdateState();
}
};

35
Core/Mapper433.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// NC-20MB, 20-in-1 (CA-006) multicart.
class Mapper433 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
virtual void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectCHRPage(0, 0);
if(value & 0x20) {
SelectPRGPage(0, value & 0x1F);
SelectPRGPage(1, value & 0x1F);
} else {
SelectPRGPage(0, value & 0x1E);
SelectPRGPage(1, (value & 0x1E) + 1);
}
SetMirroringType(value & 0x40 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

47
Core/Mapper437.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// NTDEC TH2348 8-in-1 multicart
class Mapper437 : public BaseMapper
{
private:
uint8_t _regs[2];
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
WriteRegister(0x5000, 0);
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
WriteRegister(0x5000, 0);
WriteRegister(0x8000, 0);
}
void UpdateState()
{
SelectCHRPage(0, 0);
SelectPRGPage(0, _regs[0] << 3 | (_regs[1] & 0x07));
SelectPRGPage(1, _regs[0] << 3 | 0x07);
SetMirroringType((_regs[0] & 0x08) ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[0] = addr & 0x0F;
} else {
_regs[1] = value;
}
UpdateState();
}
};

36
Core/Mapper438.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// K-3071
class Mapper438 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SetMirroringType((value & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
if (addr & 0x01) {
SelectPrgPage2x(0, (addr >> 1) & 0xFE);
} else {
SelectPRGPage(0, addr >> 1);
SelectPRGPage(1, addr >> 1);
}
SelectCHRPage(0, value >> 1);
}
};

65
Core/Mapper449.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include "BaseMapper.h"
class Mapper449 : public BaseMapper
{
protected:
uint32_t GetDipSwitchCount() override { return 4; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
bool _mFlag;
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void Reset(bool softreset) override
{
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_mFlag);
}
uint8_t ReadRegister(uint16_t addr) override
{
if(_mFlag) {
addr |= GetDipSwitches();
}
return InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint16_t prgBank = ((addr >> 2) & 0x1F) | ((addr >> 3) & 0x20);
bool sFlag = (addr & 0x01) == 0x01;
bool prgMode = ((addr >> 7) & 0x01) == 0x01;
_mFlag = ((addr >> 9) & 0x01) == 0x01;
if(prgMode) {
if(sFlag) {
SelectPrgPage2x(0, prgBank & 0xFE);
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank);
}
} else {
SelectPRGPage(0, prgBank);
SelectPRGPage(1, prgBank | 0x07);
}
// protect CHR-RAM on nrom modes
SetPpuMemoryMapping(0, 0x1FFF, 0, ChrMemoryType::Default, (!HasBattery() && prgMode) ? MemoryAccessType::Read : MemoryAccessType::ReadWrite);
SelectCHRPage(0, value);
SetMirroringType((addr & 0x02) == 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

59
Core/Mapper452.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper452 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint32_t GetWorkRamSize() override { return 0x2000; }
uint32_t GetWorkRamPageSize() override { return 0x2000; }
bool ForceWorkRamSize() override { return true; }
void InitMapper() override
{
// https://www.nesdev.org/wiki/NES_2.0_Mapper_452
// $E000 - $FFFF does not respond to latch to allow writing to
// PRG RAM mapped to that range
RemoveRegisterRange(0xE000, 0xFFFF, MemoryOperation::Any);
SelectCHRPage(0, 0);
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint16_t wramBank = 0x8000 | ((value << 9) & 0x6000);
uint8_t prgPage = addr >> 1;
if(value & 0x02) {
SelectPRGPage(0, prgPage);
SelectPRGPage(1, prgPage);
SelectPRGPage(2, prgPage);
SelectPRGPage(3, prgPage);
SetCpuMemoryMapping((wramBank ^ 0x4000), (wramBank ^ 0x4000) + 0x1FFF, 0, PrgMemoryType::WorkRam);
} else {
if (value & 0x08) {
SelectPRGPage(0, prgPage | 0);
SelectPRGPage(1, prgPage | 1);
SelectPRGPage(2, prgPage | 2);
SelectPRGPage(3, prgPage | 3 | (value & 0x04));
} else {
SelectPrgPage2x(0, prgPage & 0xFE);
SelectPrgPage2x(1, 0);
}
}
SetMirroringType((value & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical);
SetCpuMemoryMapping(wramBank, wramBank + 0x1FFF, 0, PrgMemoryType::WorkRam);
}
};

47
Core/Mapper453.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
// Realtec 8042
class Mapper453 : public BaseMapper
{
private:
uint8_t _reg;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_reg = 0;
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_reg);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if (_reg & 0xE0) {
_reg &= 0xE0;
_reg |= value & ~0xE0;
} else {
_reg = value;
}
SelectCHRPage(0, 0);
if (_reg & 0x40) {
SelectPrgPage2x(0, (((_reg >> 3) & 0x18) | (_reg & 0x07)) << 1);
SetMirroringType((_reg & 0x10) ? MirroringType::ScreenAOnly : MirroringType::ScreenBOnly);
} else {
SelectPRGPage(0, ((_reg >> 2) & 0x38) | (_reg & 0x07));
SelectPRGPage(1, ((_reg >> 2) & 0x38) | 0x07);
SetMirroringType((_reg & 0x10) ? MirroringType::Horizontal : MirroringType::Vertical);
}
}
};

23
Core/Mapper81.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Mapper81 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectPRGPage(1, -1);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, addr >> 2);
SelectCHRPage(0, addr & 0x03);
}
};

View File

@ -6,39 +6,100 @@ class Mapper91 : public MMC3
{
protected:
virtual uint16_t RegisterStartAddress() override { return 0x6000; }
virtual uint16_t RegisterEndAddress() override { return 0x7FFF; }
virtual uint16_t RegisterEndAddress() override { return 0x9FFF; }
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x800; }
uint8_t _prgBanks[2];
uint8_t _chrBanks[4];
uint8_t _outerBank;
// for Submapper 1
bool _verticalMirroring;
void InitMapper() override
{
SelectPRGPage(2, -2);
SelectPRGPage(3, -1);
UpdateState();
}
void UpdateState() override
{
//Do nothing, we are only using MMC3 code to emulate the IRQs
SelectPRGPage(0, ((_outerBank << 3) & 0x30) | (_prgBanks[0] & 0x0F));
SelectPRGPage(1, ((_outerBank << 3) & 0x30) | (_prgBanks[1] & 0x0F));
SelectPRGPage(2, ((_outerBank << 3) & 0x30) | 0xE);
SelectPRGPage(3, ((_outerBank << 3) & 0x30) | 0xF);
SelectCHRPage(0, ((_outerBank << 8) & 0x100) | _chrBanks[0]);
SelectCHRPage(1, ((_outerBank << 8) & 0x100) | _chrBanks[1]);
SelectCHRPage(2, ((_outerBank << 8) & 0x100) | _chrBanks[2]);
SelectCHRPage(3, ((_outerBank << 8) & 0x100) | _chrBanks[3]);
if(_romInfo.SubMapperID == 1) {
SetMirroringType(_verticalMirroring ? MirroringType::Vertical : MirroringType::Horizontal);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x7003) {
case 0x6000: SelectCHRPage(0, value); break;
case 0x6001: SelectCHRPage(1, value); break;
case 0x6002: SelectCHRPage(2, value); break;
case 0x6003: SelectCHRPage(3, value); break;
case 0x7000: SelectPRGPage(0, value & 0x0F); break;
case 0x7001: SelectPRGPage(1, value & 0x0F); break;
case 0x7002:
MMC3::WriteRegister(0xE000, value);
break;
case 0x7003:
MMC3::WriteRegister(0xC000, 0x07);
MMC3::WriteRegister(0xC001, value);
MMC3::WriteRegister(0xE001, value);
break;
if(addr <= 0x6FFF) {
if(_romInfo.SubMapperID == 1) {
switch(addr & 0x07) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
_chrBanks[addr & 0x03] = value;
UpdateState();
return;
case 0x04:
case 0x05:
_verticalMirroring = (addr & 0x01) == 0x01;
UpdateState();
return;
case 0x06:
return;
case 0x07:
return;
default:
return;
}
} else {
_chrBanks[addr & 0x03] = value;
UpdateState();
return;
}
}
if(addr <= 0x7FFF) {
switch(addr & 0x03) {
case 0x00:
case 0x01:
_prgBanks[addr & 0x01] = value;
UpdateState();
return;
case 0x02:
MMC3::WriteRegister(0xE000, value);
return;
case 0x03:
MMC3::WriteRegister(0xC000, 0x07);
MMC3::WriteRegister(0xC001, value);
MMC3::WriteRegister(0xE001, value);
return;
default:
return;
}
}
_outerBank = (uint8_t)addr;
UpdateState();
}
};

View File

@ -34,6 +34,8 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
romData.Info.VsPpuModel = header.GetVsSystemPpuModel();
romData.Info.InputType = header.GetInputType();
romData.Info.HasTrainer = header.HasTrainer();
romData.Info.MiscRoms = header.GetMiscRoms();
romData.Info.NesHeader = header;
romData.ChrRamSize = header.GetChrRamSize();
@ -81,6 +83,12 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
romData.PrgRom.insert(romData.PrgRom.end(), buffer, buffer + prgSize);
buffer += prgSize;
romData.ChrRom.insert(romData.ChrRom.end(), buffer, buffer + chrSize);
buffer += chrSize;
if(romData.Info.MiscRoms) {
// Misc roms occupies the remaining bytes
romData.MiscRomsData.insert(romData.MiscRomsData.end(), buffer, buffer + (dataSize - (prgSize + chrSize)));
}
romData.Info.Hash.PrgCrc32 = CRC32::GetCRC(romData.PrgRom.data(), romData.PrgRom.size());
@ -125,6 +133,9 @@ void iNesLoader::LoadRom(RomData& romData, vector<uint8_t>& romFile, NESHeader *
if(romData.Info.HasTrainer) {
Log("[iNes] Trainer: Yes");
}
if(romData.MiscRomsData.size() > 0) {
Log("[iNes] Misc ROMS: " + std::to_string((romData.MiscRomsData.size() / 1024)) + " KB");
}
if(!_checkOnly) {
GameDatabase::SetGameInfo(romData.Info.Hash.PrgChrCrc32, romData, GameDatabase::IsEnabled(), preloadedHeader != nullptr);