diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 13a99294..3687eaa2 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -114,6 +114,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 95984b74..8107ea53 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -185,6 +185,9 @@ Header Files\Interfaces + + Header Files\Mappers + diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index e8ebac1a..d8f35f7e 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -9,6 +9,7 @@ #include "MMC1.h" #include "MMC2.h" #include "MMC3.h" +#include "Nanjing.h" #include "NROM.h" #include "UNROM.h" #include "VRC2_4.h" @@ -33,6 +34,7 @@ BaseMapper* MapperFactory::GetMapperFromID(uint8_t mapperID) case 25: return new VRC2_4(VRCVariant::VRC4b); //Conflicts: VRC2c, VRC4d case 27: return new VRC2_4(VRCVariant::VRC4_27); //Untested case 71: return new UNROM(); //TODO: "It's largely a clone of UNROM, and Camerica games were initially emulated under iNES Mapper 002 before 071 was assigned." + case 163: return new Nanjing(); default: Console::DisplayMessage(L"Unsupported mapper, cannot load file."); } diff --git a/Core/Nanjing.h b/Core/Nanjing.h new file mode 100644 index 00000000..dcea8888 --- /dev/null +++ b/Core/Nanjing.h @@ -0,0 +1,122 @@ +#pragma once +#include "stdafx.h" +#include "BaseMapper.h" +#include "PPU.h" + +class Nanjing : public BaseMapper +{ +private: + uint8_t _registers[5]; + bool _toggle; + bool _autoSwitchCHR; + + void UpdateState() + { + uint8_t prgPage = _registers[0] & 0x0F | ((_registers[2] & 0x0F) << 4); + + _autoSwitchCHR = (_registers[0] & 0x80) == 0x80; + + SelectPRGPage(0, prgPage); + } + +protected: + virtual uint32_t GetPRGPageSize() { return 0x8000; } + virtual uint32_t GetCHRPageSize() { return 0x1000; } + + virtual void StreamState(bool saving) + { + BaseMapper::StreamState(saving); + + StreamArray(_registers, 5); + Stream(_toggle); + Stream(_autoSwitchCHR); + } + + void InitMapper() + { + memset(_registers, 0, sizeof(_registers)); + _autoSwitchCHR = false; + + //"Initial value of this register is 1, initial value of "trigger" is 0." + _toggle = false; + _registers[4] = 1; + + SelectPRGPage(0, 0); + SelectCHRPage(0, 0); + SelectCHRPage(1, 0); + } + + uint16_t RegisterStartAddress() { return 0x5000; } + uint16_t RegisterEndAddress() { return 0x5FFF; } + void WriteRegister(uint16_t addr, uint8_t value) + { + if(addr >= 0x5000 && addr <= 0x5FFF) { + //"(Address is masked with 0x7300, except for 5101)" + if(addr == 0x5101) { + if(_registers[4] != 0 && value == 0) { + //"If the value of this register is changed from nonzero to zero, "trigger" is toggled (XORed with 1)" + _toggle = !_toggle; + } + _registers[4] = value; + } else { + switch(addr & 0x7300) { + case 0x5000: + _registers[0] = value; + if(!(_registers[0] & 0x80) && PPU::GetCurrentScanline() < 128) { + SelectCHRPage(0, 0); + SelectCHRPage(1, 1); + } + UpdateState(); + break; + case 0x5100: + _registers[1] = value; + if(value == 6) { + SelectPRGPage(0, 3); + } + break; + case 0x5200: + _registers[2] = value; + UpdateState(); + break; + case 0x5300: _registers[3] = value; break; + } + } + } + } + +public: + uint8_t ReadRAM(uint16_t addr) + { + if(addr >= 0x5000 && addr <= 0x5FFF) { + //"Reading: (Address is masked with 0x7700)" + switch(addr & 0x7700) { + case 0x5100: + //"5100 = Returns value of 5300" + return _registers[3]; + + case 0x5500: + //"5500 = If "trigger" is 1, returns value of 5300, otherwise returns 0 " + if(_toggle) { + return _registers[3]; + } + break; + } + return 0; + } else { + return BaseMapper::ReadRAM(addr); + } + } + + virtual void NotifyVRAMAddressChange(uint16_t addr) + { + if(_autoSwitchCHR) { + if(PPU::GetCurrentScanline() == 239) { + SelectCHRPage(0, 0); + SelectCHRPage(1, 0); + } else if(PPU::GetCurrentScanline() == 127) { + SelectCHRPage(0, 1); + SelectCHRPage(1, 1); + } + } + } +}; \ No newline at end of file