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