1 #pragma once 2 #include "stdafx.h" 3 #include "BaseMapper.h" 4 #include "CPU.h" 5 #include "A12Watcher.h" 6 7 class Mapper116 : public BaseMapper 8 { 9 private: 10 A12Watcher _a12Watcher; 11 uint8_t _mode; 12 13 uint8_t _vrc2Chr[8]; 14 uint8_t _vrc2Prg[2]; 15 uint8_t _vrc2Mirroring; 16 17 uint8_t _mmc3Regs[10]; 18 uint8_t _mmc3Ctrl; 19 uint8_t _mmc3Mirroring; 20 21 uint8_t _mmc1Regs[4]; 22 uint8_t _mmc1Buffer; 23 uint8_t _mmc1Shift; 24 25 uint8_t _irqCounter; 26 uint8_t _irqReloadValue; 27 bool _irqReload; 28 bool _irqEnabled; 29 30 protected: RegisterStartAddress()31 virtual uint16_t RegisterStartAddress() override { return 0x4100; } RegisterEndAddress()32 virtual uint16_t RegisterEndAddress() override { return 0xFFFF; } GetPRGPageSize()33 virtual uint16_t GetPRGPageSize() override { return 0x2000; } GetCHRPageSize()34 virtual uint16_t GetCHRPageSize() override { return 0x400; } 35 InitMapper()36 void InitMapper() override 37 { 38 _mode = 0; 39 40 _vrc2Chr[0] = -1; 41 _vrc2Chr[1] = -1; 42 _vrc2Chr[2] = -1; 43 _vrc2Chr[3] = -1; 44 _vrc2Chr[4] = 4; 45 _vrc2Chr[5] = 5; 46 _vrc2Chr[6] = 6; 47 _vrc2Chr[7] = 7; 48 _vrc2Prg[0] = 0; 49 _vrc2Prg[1] = 1; 50 _vrc2Mirroring = 0; 51 52 _mmc3Regs[0] = 0; 53 _mmc3Regs[1] = 2; 54 _mmc3Regs[2] = 4; 55 _mmc3Regs[3] = 5; 56 _mmc3Regs[4] = 6; 57 _mmc3Regs[5] = 7; 58 _mmc3Regs[6] = -4; 59 _mmc3Regs[7] = -3; 60 _mmc3Regs[8] = -2; 61 _mmc3Regs[9] = -1; 62 _mmc3Ctrl = 0; 63 _mmc3Mirroring = 0; 64 _irqCounter = 0; 65 _irqReloadValue = 0; 66 _irqEnabled = false; 67 _irqReload = false; 68 69 _mmc1Regs[0] = 0xc; 70 _mmc1Regs[1] = 0; 71 _mmc1Regs[2] = 0; 72 _mmc1Regs[3] = 0; 73 _mmc1Buffer = 0; 74 _mmc1Shift = 0; 75 76 UpdateState(); 77 } 78 StreamState(bool saving)79 void StreamState(bool saving) override 80 { 81 BaseMapper::StreamState(saving); 82 83 SnapshotInfo a12Watcher { &_a12Watcher }; 84 ArrayInfo<uint8_t> vrc2Chr { _vrc2Chr, 8 }; 85 ArrayInfo<uint8_t> vrc2Prg { _vrc2Prg, 2 }; 86 ArrayInfo<uint8_t> mmc3Regs { _mmc3Regs, 10 }; 87 ArrayInfo<uint8_t> mmc1Regs { _mmc1Regs, 4 }; 88 89 Stream(_mode, a12Watcher, 90 vrc2Chr, vrc2Prg, _vrc2Mirroring, 91 mmc3Regs, _mmc3Ctrl, _mmc3Mirroring, _irqCounter, _irqEnabled, _irqReload, _irqReloadValue, 92 mmc1Regs, _mmc1Buffer, _mmc1Shift 93 ); 94 } 95 NotifyVRAMAddressChange(uint16_t addr)96 virtual void NotifyVRAMAddressChange(uint16_t addr) override 97 { 98 if((_mode & 0x03) == 1) { 99 switch(_a12Watcher.UpdateVramAddress(addr, _console->GetPpu()->GetFrameCycle())) { 100 case A12StateChange::None: 101 case A12StateChange::Fall: 102 break; 103 104 case A12StateChange::Rise: 105 if(_irqCounter == 0 || _irqReload) { 106 _irqCounter = _irqReloadValue; 107 } else { 108 _irqCounter--; 109 } 110 111 if(_irqCounter == 0 && _irqEnabled) { 112 _console->GetCpu()->SetIrqSource(IRQSource::External); 113 } 114 _irqReload = false; 115 break; 116 } 117 } 118 } 119 UpdatePrg()120 void UpdatePrg() 121 { 122 switch(_mode & 0x03) { 123 case 0: 124 SelectPRGPage(0, _vrc2Prg[0]); 125 SelectPRGPage(1, _vrc2Prg[1]); 126 SelectPRGPage(2, -2); 127 SelectPRGPage(3, -1); 128 break; 129 130 case 1: { 131 uint32_t prgMode = (_mmc3Ctrl >> 5) & 0x02; 132 SelectPRGPage(0, _mmc3Regs[6 + prgMode]); 133 SelectPRGPage(1, _mmc3Regs[7]); 134 SelectPRGPage(2, _mmc3Regs[6 + (prgMode ^ 0x02)]); 135 SelectPRGPage(3, _mmc3Regs[9]); 136 break; 137 } 138 139 case 2: 140 case 3: { 141 uint8_t bank = _mmc1Regs[3] & 0x0F; 142 if(_mmc1Regs[0] & 0x08) { 143 if(_mmc1Regs[0] & 0x04) { 144 SelectPrgPage2x(0, bank << 1); 145 SelectPrgPage2x(1, 0x0F << 1); 146 } else { 147 SelectPrgPage2x(0, 0); 148 SelectPrgPage2x(1, bank << 1); 149 } 150 } else { 151 SelectPrgPage4x(0, (bank & 0xFE) << 1); 152 } 153 break; 154 } 155 } 156 } 157 UpdateChr()158 void UpdateChr() 159 { 160 uint32_t outerBank = (_mode & 0x04) << 6; 161 switch(_mode & 0x03) { 162 case 0: 163 for(int i = 0; i < 8; i++) { 164 SelectCHRPage(i, outerBank | _vrc2Chr[i]); 165 } 166 break; 167 168 case 1: { 169 uint32_t slotSwap = (_mmc3Ctrl & 0x80) ? 4 : 0; 170 SelectCHRPage(0 ^ slotSwap, outerBank | ((_mmc3Regs[0]) & 0xFE)); 171 SelectCHRPage(1 ^ slotSwap, outerBank | (_mmc3Regs[0] | 1)); 172 SelectCHRPage(2 ^ slotSwap, outerBank | ((_mmc3Regs[1]) & 0xFE)); 173 SelectCHRPage(3 ^ slotSwap, outerBank | (_mmc3Regs[1] | 1)); 174 SelectCHRPage(4 ^ slotSwap, outerBank | _mmc3Regs[2]); 175 SelectCHRPage(5 ^ slotSwap, outerBank | _mmc3Regs[3]); 176 SelectCHRPage(6 ^ slotSwap, outerBank | _mmc3Regs[4]); 177 SelectCHRPage(7 ^ slotSwap, outerBank | _mmc3Regs[5]); 178 break; 179 } 180 181 case 2: 182 case 3: { 183 if(_mmc1Regs[0] & 0x10) { 184 SelectChrPage4x(0, _mmc1Regs[1] << 2); 185 SelectChrPage4x(1, _mmc1Regs[2] << 2); 186 } else { 187 SelectChrPage8x(0, (_mmc1Regs[1] & 0xFE) << 2); 188 } 189 break; 190 } 191 } 192 } 193 UpdateMirroring()194 void UpdateMirroring() 195 { 196 switch(_mode & 0x03) { 197 case 0: SetMirroringType((_vrc2Mirroring & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical); break; 198 case 1: SetMirroringType((_mmc3Mirroring & 0x01) ? MirroringType::Horizontal : MirroringType::Vertical); break; 199 200 case 2: 201 case 3: 202 switch(_mmc1Regs[0] & 0x03) { 203 case 0: SetMirroringType(MirroringType::ScreenAOnly); break; 204 case 1: SetMirroringType(MirroringType::ScreenBOnly); break; 205 case 2: SetMirroringType(MirroringType::Vertical); break; 206 case 3: SetMirroringType(MirroringType::Horizontal); break; 207 } 208 break; 209 } 210 } 211 UpdateState()212 void UpdateState() 213 { 214 UpdatePrg(); 215 UpdateChr(); 216 UpdateMirroring(); 217 } 218 WriteVrc2Register(uint16_t addr,uint8_t value)219 void WriteVrc2Register(uint16_t addr, uint8_t value) 220 { 221 if(addr >= 0xB000 && addr <= 0xE003) { 222 int32_t regIndex = ((((addr & 0x02) | (addr >> 10)) >> 1) + 2) & 0x07; 223 int32_t lowHighNibble = ((addr & 1) << 2); 224 _vrc2Chr[regIndex] = (_vrc2Chr[regIndex] & (0xF0 >> lowHighNibble)) | ((value & 0x0F) << lowHighNibble); 225 UpdateChr(); 226 } else { 227 switch(addr & 0xF000) { 228 case 0x8000: _vrc2Prg[0] = value; UpdatePrg(); break; 229 case 0xA000: _vrc2Prg[1] = value; UpdatePrg(); break; 230 case 0x9000: _vrc2Mirroring = value; UpdateMirroring(); break; 231 } 232 } 233 } 234 WriteMmc3Register(uint16_t addr,uint8_t value)235 void WriteMmc3Register(uint16_t addr, uint8_t value) 236 { 237 switch(addr & 0xE001) { 238 case 0x8000: 239 _mmc3Ctrl = value; 240 UpdateState(); 241 break; 242 243 case 0x8001: 244 _mmc3Regs[_mmc3Ctrl & 0x07] = value; 245 UpdateState(); 246 break; 247 248 case 0xA000: 249 _mmc3Mirroring = value; 250 UpdateState(); 251 break; 252 253 case 0xC000: _irqReloadValue = value; break; 254 case 0xC001: _irqReload = true; break; 255 256 case 0xE000: 257 _console->GetCpu()->ClearIrqSource(IRQSource::External); 258 _irqEnabled = false; 259 break; 260 261 case 0xE001: _irqEnabled = true; break; 262 } 263 } 264 WriteMmc1Register(uint16_t addr,uint8_t value)265 void WriteMmc1Register(uint16_t addr, uint8_t value) 266 { 267 if(value & 0x80) { 268 _mmc1Regs[0] |= 0xc; 269 _mmc1Buffer = _mmc1Shift = 0; 270 UpdateState(); 271 } else { 272 uint8_t regIndex = (addr >> 13) - 4; 273 _mmc1Buffer |= (value & 0x01) << (_mmc1Shift++); 274 if(_mmc1Shift == 5) { 275 _mmc1Regs[regIndex] = _mmc1Buffer; 276 _mmc1Buffer = _mmc1Shift = 0; 277 UpdateState(); 278 } 279 } 280 } 281 WriteRegister(uint16_t addr,uint8_t value)282 void WriteRegister(uint16_t addr, uint8_t value) override 283 { 284 if(addr < 0x8000) { 285 if((addr & 0x4100) == 0x4100) { 286 _mode = value; 287 if(addr & 0x01) { 288 _mmc1Regs[0] = 0xc; 289 _mmc1Regs[3] = 0; 290 _mmc1Buffer = 0; 291 _mmc1Shift = 0; 292 } 293 UpdateState(); 294 } 295 } else { 296 switch(_mode & 0x03) { 297 case 0: WriteVrc2Register(addr, value); break; 298 case 1: WriteMmc3Register(addr, value); break; 299 300 case 2: 301 case 3: WriteMmc1Register(addr, value); break; 302 } 303 } 304 } 305 }; 306