1 #pragma once 2 #include "stdafx.h" 3 #include "BaseMapper.h" 4 #include "CPU.h" 5 6 class Sunsoft4 : public BaseMapper 7 { 8 private: 9 uint8_t _ntRegs[2]; 10 bool _useChrForNametables; 11 bool _prgRamEnabled; 12 uint32_t _licensingTimer; 13 bool _usingExternalRom; 14 uint8_t _externalPage = 0; 15 UpdateNametables()16 void UpdateNametables() 17 { 18 if(_useChrForNametables) { 19 for(int i = 0; i < 4; i++) { 20 uint8_t reg = 0; 21 switch(GetMirroringType()) { 22 case MirroringType::FourScreens: break; //4-screen mirroring is not supported by this mapper 23 case MirroringType::Vertical: reg = i & 0x01; break; 24 case MirroringType::Horizontal: reg = (i & 0x02) >> 1; break; 25 case MirroringType::ScreenAOnly: reg = 0; break; 26 case MirroringType::ScreenBOnly: reg = 1; break; 27 } 28 29 SetPpuMemoryMapping(0x2000+i*0x400, 0x2000+i*0x400+0x3FF, ChrMemoryType::Default, _ntRegs[reg] * 0x400, _chrRamSize > 0 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 30 } 31 } else { 32 //Reset to default mirroring 33 SetMirroringType(GetMirroringType()); 34 } 35 } 36 37 protected: GetPRGPageSize()38 virtual uint16_t GetPRGPageSize() override { return 0x4000; } GetCHRPageSize()39 virtual uint16_t GetCHRPageSize() override { return 0x800; } 40 InitMapper()41 void InitMapper() override 42 { 43 _useChrForNametables = false; 44 _ntRegs[0] = _ntRegs[1] = 0; 45 46 _licensingTimer = 0; 47 _usingExternalRom = false; 48 _prgRamEnabled = false; 49 50 //Bank 0's initial state is undefined, but some roms expect it to be the first page 51 SelectPRGPage(0, 0); 52 SelectPRGPage(1, 7); 53 54 UpdateState(); 55 } 56 StreamState(bool saving)57 void StreamState(bool saving) override 58 { 59 BaseMapper::StreamState(saving); 60 61 Stream(_ntRegs[0], _ntRegs[1], _useChrForNametables, _prgRamEnabled, _usingExternalRom, _externalPage); 62 } 63 UpdateState()64 void UpdateState() 65 { 66 if(!_prgRamEnabled) { 67 RemoveCpuMemoryMapping(0x6000, 0x7FFF); 68 } else { 69 SetupDefaultWorkRam(); 70 } 71 72 if(_usingExternalRom) { 73 if(_licensingTimer == 0) { 74 RemoveCpuMemoryMapping(0x8000, 0xBFFF); 75 } else { 76 SelectPRGPage(0, _externalPage); 77 } 78 } 79 } 80 ProcessCpuClock()81 void ProcessCpuClock() override 82 { 83 if(_licensingTimer) { 84 _licensingTimer--; 85 if(_licensingTimer == 0) { 86 UpdateState(); 87 } 88 } 89 } 90 WriteRAM(uint16_t addr,uint8_t value)91 void WriteRAM(uint16_t addr, uint8_t value) override 92 { 93 if(addr >= 0x6000 && addr <= 0x7FFF) { 94 _licensingTimer = 1024 * 105; 95 UpdateState(); 96 } 97 BaseMapper::WriteRAM(addr, value); 98 } 99 WriteRegister(uint16_t addr,uint8_t value)100 void WriteRegister(uint16_t addr, uint8_t value) override 101 { 102 switch(addr & 0xF000) { 103 case 0x8000: SelectCHRPage(0, value); break; 104 case 0x9000: SelectCHRPage(1, value); break; 105 case 0xA000: SelectCHRPage(2, value); break; 106 case 0xB000: SelectCHRPage(3, value); break; 107 case 0xC000: 108 _ntRegs[0] = value | 0x80; 109 UpdateNametables(); 110 break; 111 case 0xD000: 112 _ntRegs[1] = value | 0x80; 113 UpdateNametables(); 114 break; 115 case 0xE000: 116 switch(value & 0x03) { 117 case 0: SetMirroringType(MirroringType::Vertical); break; 118 case 1: SetMirroringType(MirroringType::Horizontal); break; 119 case 2: SetMirroringType(MirroringType::ScreenAOnly); break; 120 case 3: SetMirroringType(MirroringType::ScreenBOnly); break; 121 } 122 _useChrForNametables = (value & 0x10) == 0x10; 123 UpdateNametables(); 124 break; 125 case 0xF000: 126 bool externalPrg = (value & 0x08) == 0; 127 if(externalPrg && GetPRGPageCount() > 8) { 128 _usingExternalRom = true; 129 _externalPage = 0x08 | ((value & 0x07) % (GetPRGPageCount() - 0x08)); 130 SelectPRGPage(0, _externalPage); 131 } else { 132 _usingExternalRom = false; 133 SelectPRGPage(0, value & 0x07); 134 } 135 136 _prgRamEnabled = (value & 0x10) == 0x10; 137 UpdateState(); 138 139 break; 140 } 141 } 142 };