diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp
index 5e462d47..80d747f0 100644
--- a/Core/BaseMapper.cpp
+++ b/Core/BaseMapper.cpp
@@ -134,6 +134,18 @@ uint8_t BaseMapper::InternalReadRam(uint16_t addr)
return _prgPages[addr >> 8] ? _prgPages[addr >> 8][addr & 0xFF] : 0;
}
+void BaseMapper::SelectPrgPage4x(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
+{
+ SelectPrgPage2x(slot*2, page, memoryType);
+ SelectPrgPage2x(slot*2+1, page+2, memoryType);
+}
+
+void BaseMapper::SelectPrgPage2x(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
+{
+ SelectPRGPage(slot*2, page, memoryType);
+ SelectPRGPage(slot*2+1, page+1, memoryType);
+}
+
void BaseMapper::SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType)
{
_prgPageNumbers[slot] = page;
diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h
index 3cae696f..c7f0f4d6 100644
--- a/Core/BaseMapper.h
+++ b/Core/BaseMapper.h
@@ -104,6 +104,8 @@ protected:
virtual void WriteRegister(uint16_t addr, uint8_t value) { }
virtual uint8_t ReadRegister(uint16_t addr) { return 0; }
+ void SelectPrgPage4x(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
+ void SelectPrgPage2x(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
virtual void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
void SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, int16_t pageNumber, PrgMemoryType type, int8_t accessType = -1);
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 04f9ff8e..128db89f 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -491,7 +491,7 @@
-
+
@@ -499,14 +499,17 @@
+
+
+
-
+
@@ -549,7 +552,6 @@
-
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 878ba3e4..ccb7e7b4 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -467,10 +467,19 @@
Nes\Mappers
-
+
Nes\Mappers
-
+
+ Header Files
+
+
+ Nes\Mappers
+
+
+ Header Files
+
+
Nes\Mappers
@@ -508,9 +517,6 @@
Source Files
-
- Nes\Mappers
-
VideoDecoder
@@ -586,8 +592,8 @@
Nes\APU
-
- Nes\Mappers
+
+ Source Files
\ No newline at end of file
diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp
index 87aadcc1..8857abd3 100644
--- a/Core/MapperFactory.cpp
+++ b/Core/MapperFactory.cpp
@@ -84,6 +84,7 @@
#include "VRC1.h"
#include "VRC2_4.h"
#include "VRC3.h"
+#include "VRC6.h"
BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
{
@@ -110,7 +111,9 @@ BaseMapper* MapperFactory::GetMapperFromID(ROMLoader &romLoader)
case 21: return new VRC2_4(VRCVariant::VRC4a); //Conflicts: VRC4c
case 22: return new VRC2_4(VRCVariant::VRC2a);
case 23: return new VRC2_4(VRCVariant::VRC2b); //Conflicts: VRC4e
+ case 24: return new VRC6(VRCVariant::VRC6a);
case 25: return new VRC2_4(VRCVariant::VRC4b); //Conflicts: VRC2c, VRC4d
+ case 26: return new VRC6(VRCVariant::VRC6b);
case 27: return new VRC2_4(VRCVariant::VRC4_27); //Untested
case 32: return new IremG101();
case 33: return new TaitoTc0190();
diff --git a/Core/VRC2_4.h b/Core/VRC2_4.h
index e96647f9..54dd3d4d 100644
--- a/Core/VRC2_4.h
+++ b/Core/VRC2_4.h
@@ -13,6 +13,8 @@ enum class VRCVariant
VRC4d, //25
VRC4e, //23
VRC4_27, //27
+ VRC6a,
+ VRC6b
};
class VRC2_4 : public BaseMapper
diff --git a/Core/VRC6.h b/Core/VRC6.h
new file mode 100644
index 00000000..646fce59
--- /dev/null
+++ b/Core/VRC6.h
@@ -0,0 +1,140 @@
+#pragma once
+#include "stdafx.h"
+#include "BaseMapper.h"
+#include "VrcIrq.h"
+enum class VRCVariant;
+
+//incomplete - missing audio and more
+class VRC6 : public BaseMapper
+{
+private:
+ VrcIrq _irq;
+ VRCVariant _model;
+ uint8_t _bankingMode;
+ uint8_t _chrRegisters[8];
+
+ void UpdatePrgRamAccess()
+ {
+ SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam, (_bankingMode & 0x80) ? MemoryAccessType::ReadWrite : MemoryAccessType::NoAccess);
+ }
+
+protected:
+ virtual uint16_t GetPRGPageSize() { return 0x2000; }
+ virtual uint16_t GetCHRPageSize() { return 0x0400; }
+
+ void InitMapper()
+ {
+ _irq.Reset();
+ _bankingMode = 0;
+ memset(_chrRegisters, 0, sizeof(_chrRegisters));
+ SelectPRGPage(3, -1);
+ }
+
+ virtual void StreamState(bool saving)
+ {
+ BaseMapper::StreamState(saving);
+ Stream(_irq);
+ Stream(_bankingMode);
+ StreamArray(_chrRegisters, 8);
+
+ if(!saving) {
+ UpdatePrgRamAccess();
+ }
+ }
+
+ void ProcessCpuClock()
+ {
+ _irq.ProcessCpuClock();
+ }
+
+ void UpdatePpuBanking()
+ {
+ switch(_bankingMode & 0x03) {
+ case 0:
+ SelectCHRPage(0, _chrRegisters[0]);
+ SelectCHRPage(1, _chrRegisters[1]);
+ SelectCHRPage(2, _chrRegisters[2]);
+ SelectCHRPage(3, _chrRegisters[3]);
+ SelectCHRPage(4, _chrRegisters[4]);
+ SelectCHRPage(5, _chrRegisters[5]);
+ SelectCHRPage(6, _chrRegisters[6]);
+ SelectCHRPage(7, _chrRegisters[7]);
+ break;
+
+ case 1:
+ SelectChrPage2x(0, _chrRegisters[0]);
+ SelectChrPage2x(1, _chrRegisters[1]);
+ SelectChrPage2x(2, _chrRegisters[2]);
+ SelectChrPage2x(3, _chrRegisters[3]);
+ break;
+
+ case 2: case 3:
+ SelectCHRPage(0, _chrRegisters[0]);
+ SelectCHRPage(1, _chrRegisters[1]);
+ SelectCHRPage(2, _chrRegisters[2]);
+ SelectCHRPage(3, _chrRegisters[3]);
+ SelectChrPage2x(2, _chrRegisters[4]);
+ SelectChrPage2x(3, _chrRegisters[5]);
+ break;
+ }
+
+ //This is incorrect, but seems ok for all commercial games? (Based on old Disch documents)
+ switch((_bankingMode >> 2) & 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;
+ }
+
+ UpdatePrgRamAccess();
+ }
+
+ void WriteRegister(uint16_t addr, uint8_t value)
+ {
+ if(_model == VRCVariant::VRC6b) {
+ addr = (addr & 0xFFFC) | ((addr & 0x01) << 1) | ((addr & 0x02) >> 1);
+ }
+
+ switch(addr & 0xF003) {
+ case 0x8000: case 0x8001: case 0x8002: case 0x8003:
+ SelectPrgPage2x(0, (value & 0x0F) << 1);
+ break;
+
+ case 0xB003:
+ _bankingMode = value;
+ UpdatePpuBanking();
+ break;
+
+ case 0xC000: case 0xC001: case 0xC002: case 0xC003:
+ SelectPRGPage(2, value & 0x1F);
+ break;
+
+ case 0xD000: case 0xD001: case 0xD002: case 0xD003:
+ _chrRegisters[addr & 0x03] = value;
+ UpdatePpuBanking();
+ break;
+
+ case 0xE000: case 0xE001: case 0xE002: case 0xE003:
+ _chrRegisters[4 + (addr & 0x03)] = value;
+ UpdatePpuBanking();
+ break;
+
+ case 0xF000:
+ _irq.SetReloadValue(value);
+ break;
+
+ case 0xF001:
+ _irq.SetControlValue(value);
+ break;
+
+ case 0xF002:
+ _irq.AcknowledgeIrq();
+ break;
+ }
+ }
+
+public:
+ VRC6(VRCVariant model) : _model(model)
+ {
+ }
+};
\ No newline at end of file
diff --git a/Core/VrcIrq.h b/Core/VrcIrq.h
new file mode 100644
index 00000000..db88a2e4
--- /dev/null
+++ b/Core/VrcIrq.h
@@ -0,0 +1,77 @@
+#pragma once
+#include "Snapshotable.h"
+#include "CPU.h"
+
+class VrcIrq : Snapshotable
+{
+private:
+ uint8_t _irqReloadValue;
+ uint8_t _irqCounter;
+ int16_t _irqPrescalerCounter;
+ bool _irqEnabled;
+ bool _irqEnabledAfterAck;
+ bool _irqCycleMode;
+
+protected:
+ void StreamState(bool saving)
+ {
+ Stream(_irqReloadValue);
+ Stream(_irqCounter);
+ Stream(_irqPrescalerCounter);
+ Stream(_irqEnabled);
+ Stream(_irqEnabledAfterAck);
+ Stream(_irqCycleMode);
+ }
+
+public:
+ void Reset()
+ {
+ _irqPrescalerCounter = 0;
+ _irqReloadValue = 0;
+ _irqCounter = 0;
+ _irqEnabled = false;
+ _irqEnabledAfterAck = false;
+ _irqCycleMode = false;
+ }
+
+ void ProcessCpuClock()
+ {
+ if(_irqEnabled) {
+ _irqPrescalerCounter -= 3;
+
+ if(_irqCycleMode || (_irqPrescalerCounter <= 0 && !_irqCycleMode)) {
+ if(_irqCounter == 0xFF) {
+ _irqCounter = _irqReloadValue;
+ CPU::SetIRQSource(IRQSource::External);
+ } else {
+ _irqCounter++;
+ }
+ _irqPrescalerCounter += 341;
+ }
+ }
+ }
+
+ void SetReloadValue(uint8_t value)
+ {
+ _irqReloadValue = value;
+ }
+
+ void SetControlValue(uint8_t value)
+ {
+ _irqEnabledAfterAck = (value & 0x01) == 0x01;
+ _irqEnabled = (value & 0x02) == 0x02;
+ _irqCycleMode = (value & 0x04) == 0x04;
+
+ if(_irqEnabled) {
+ _irqCounter = _irqReloadValue;
+ _irqPrescalerCounter = 341;
+ }
+ }
+
+ void AcknowledgeIrq()
+ {
+ _irqEnabled = _irqEnabledAfterAck;
+ CPU::ClearIRQSource(IRQSource::External);
+ }
+
+};
\ No newline at end of file