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