#pragma once #include "stdafx.h" #include "BaseMapper.h" #include "VrcIrq.h" #include "Vrc6Audio.h" enum class VRCVariant; //incomplete - missing audio and more class VRC6 : public BaseMapper { private: VrcIrq _irq; Vrc6Audio _audio; 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() override { return 0x2000; } virtual uint16_t GetCHRPageSize() override { return 0x0400; } void InitMapper() override { _irq.Reset(); _audio.Reset(); memset(_chrRegisters, 0, sizeof(_chrRegisters)); SelectPRGPage(3, -1); } virtual void StreamState(bool saving) override { BaseMapper::StreamState(saving); ArrayInfo chrRegisters = { _chrRegisters, 8 }; SnapshotInfo irq{ &_irq }; SnapshotInfo audio{ &_audio }; Stream(_bankingMode, chrRegisters, irq, audio); if(!saving) { UpdatePrgRamAccess(); } } void ProcessCpuClock() override { _irq.ProcessCpuClock(); _audio.Clock(); } 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) override { 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 0x9000: case 0x9001: case 0x9002: case 0x9003: case 0xA000: case 0xA001: case 0xA002: case 0xB000: case 0xB001: case 0xB002: _audio.WriteRegister(addr, value); 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) { } };