1 #pragma once 2 #include "stdafx.h" 3 #include "CPU.h" 4 #include "BaseMapper.h" 5 6 class MMC1 : public BaseMapper 7 { 8 private: 9 enum class MMC1Registers 10 { 11 Reg8000 = 0, 12 RegA000 = 1, 13 RegC000 = 2, 14 RegE000 = 3 15 }; 16 17 enum class PrgMode 18 { 19 _16k = 16, 20 _32k = 32, 21 }; 22 23 enum class ChrMode 24 { 25 _4k = 4, 26 _8k = 8, 27 }; 28 29 enum class SlotSelect 30 { 31 x8000 = 0x8000, 32 xC000 = 0xC000, 33 }; 34 35 uint8_t _writeBuffer = 0; 36 uint8_t _shiftCount = 0; 37 38 bool _wramDisable; 39 ChrMode _chrMode; 40 PrgMode _prgMode; 41 SlotSelect _slotSelect; 42 43 uint8_t _chrReg0; 44 uint8_t _chrReg1; 45 uint8_t _prgReg; 46 47 uint64_t _lastWriteCycle = 0; 48 49 bool _forceWramOn; 50 MMC1Registers _lastChrReg; 51 52 private: HasResetFlag(uint8_t value)53 bool HasResetFlag(uint8_t value) 54 { 55 return (value & 0x80) == 0x80; 56 } 57 ResetBuffer()58 void ResetBuffer() 59 { 60 _shiftCount = 0; 61 _writeBuffer = 0; 62 } 63 IsBufferFull(uint8_t value)64 bool IsBufferFull(uint8_t value) 65 { 66 if(HasResetFlag(value)) { 67 //When 'r' is set: 68 // - 'd' is ignored 69 // - hidden temporary reg is reset (so that the next write is the "first" write) 70 // - bits 2,3 of reg $8000 are set (16k PRG mode, $8000 swappable) 71 // - other bits of $8000 (and other regs) are unchanged 72 ResetBuffer(); 73 _state.Reg8000 |= 0x0C; 74 UpdateState(); 75 return false; 76 } else { 77 _writeBuffer >>= 1; 78 _writeBuffer |= ((value << 4) & 0x10); 79 80 _shiftCount++; 81 82 return _shiftCount == 5; 83 } 84 } 85 86 protected: 87 struct 88 { 89 uint8_t Reg8000; 90 uint8_t RegA000; 91 uint8_t RegC000; 92 uint8_t RegE000; 93 } _state; 94 UpdateState()95 virtual void UpdateState() 96 { 97 switch(_state.Reg8000 & 0x03) { 98 case 0: SetMirroringType(MirroringType::ScreenAOnly); break; 99 case 1: SetMirroringType(MirroringType::ScreenBOnly); break; 100 case 2: SetMirroringType(MirroringType::Vertical); break; 101 case 3: SetMirroringType(MirroringType::Horizontal); break; 102 } 103 104 _wramDisable = (_state.RegE000 & 0x10) == 0x10; 105 106 _slotSelect = ((_state.Reg8000 & 0x04) == 0x04) ? SlotSelect::x8000 : SlotSelect::xC000; 107 _prgMode = ((_state.Reg8000 & 0x08) == 0x08) ? PrgMode::_16k : PrgMode::_32k; 108 _chrMode = ((_state.Reg8000 & 0x10) == 0x10) ? ChrMode::_4k : ChrMode::_8k; 109 110 _chrReg0 = _state.RegA000 & 0x1F; 111 _chrReg1 = _state.RegC000 & 0x1F; 112 _prgReg = _state.RegE000 & 0x0F; 113 114 uint8_t extraReg = _lastChrReg == MMC1Registers::RegC000 && _chrMode == ChrMode::_4k ? _chrReg1 : _chrReg0; 115 uint8_t prgBankSelect = 0; 116 if(_prgSize == 0x80000) { 117 //512kb carts use bit 7 of $A000/$C000 to select page 118 //This is used for SUROM (Dragon Warrior 3/4, Dragon Quest 4) 119 prgBankSelect = extraReg & 0x10; 120 } 121 122 if(_wramDisable && !_forceWramOn) { 123 RemoveCpuMemoryMapping(0x6000, 0x7FFF); 124 } else { 125 if(_saveRamSize + _workRamSize > 0x4000) { 126 //SXROM, 32kb of save ram 127 SetCpuMemoryMapping(0x6000, 0x7FFF, (extraReg >> 2) & 0x03, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam); 128 } else if(_saveRamSize + _workRamSize > 0x2000) { 129 if(_saveRamSize == 0x2000 && _workRamSize == 0x2000) { 130 //SOROM, half of the 16kb ram is battery backed 131 SetCpuMemoryMapping(0x6000, 0x7FFF, 0, (extraReg >> 3) & 0x01 ? PrgMemoryType::WorkRam : PrgMemoryType::SaveRam); 132 } else { 133 //Unknown, shouldn't happen 134 SetCpuMemoryMapping(0x6000, 0x7FFF, (extraReg >> 2) & 0x01, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam); 135 } 136 } else { 137 //Everything else - 8kb of work or save ram 138 SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam); 139 } 140 } 141 142 if(_romInfo.SubMapperID == 5) { 143 //SubMapper 5 144 //"001: 5 Fixed PRG SEROM, SHROM, SH1ROM use a fixed 32k PRG ROM with no banking support. 145 SelectPrgPage2x(0, 0); 146 } else { 147 if(_prgMode == PrgMode::_32k) { 148 SelectPrgPage2x(0, (_prgReg & 0xFE) | prgBankSelect); 149 } else if(_prgMode == PrgMode::_16k) { 150 if(_slotSelect == SlotSelect::x8000) { 151 SelectPRGPage(0, _prgReg | prgBankSelect); 152 SelectPRGPage(1, 0x0F | prgBankSelect); 153 } else if(_slotSelect == SlotSelect::xC000) { 154 SelectPRGPage(0, 0 | prgBankSelect); 155 SelectPRGPage(1, _prgReg | prgBankSelect); 156 } 157 } 158 } 159 160 if(_chrMode == ChrMode::_8k) { 161 SelectCHRPage(0, _chrReg0 & 0x1E); 162 SelectCHRPage(1, (_chrReg0 & 0x1E) + 1); 163 } else if(_chrMode == ChrMode::_4k) { 164 SelectCHRPage(0, _chrReg0); 165 SelectCHRPage(1, _chrReg1); 166 } 167 } 168 StreamState(bool saving)169 virtual void StreamState(bool saving) override 170 { 171 BaseMapper::StreamState(saving); 172 Stream(_state.Reg8000, _state.RegA000, _state.RegC000, _state.RegE000, _writeBuffer, _shiftCount, _lastWriteCycle, _lastChrReg); 173 if(!saving) { 174 UpdateState(); 175 } 176 } 177 GetPRGPageSize()178 virtual uint16_t GetPRGPageSize() override { return 0x4000; } GetCHRPageSize()179 virtual uint16_t GetCHRPageSize() override { return 0x1000; } 180 InitMapper()181 virtual void InitMapper() override 182 { 183 _state.Reg8000 = GetPowerOnByte() | 0x0C; //On powerup: bits 2,3 of $8000 are set (this ensures the $8000 is bank 0, and $C000 is the last bank - needed for SEROM/SHROM/SH1ROM which do no support banking) 184 _state.RegA000 = GetPowerOnByte(); 185 _state.RegC000 = GetPowerOnByte(); 186 _state.RegE000 = (_romInfo.DatabaseInfo.Board.find("MMC1B") != string::npos ? 0x10 : 0x00); //WRAM Disable: enabled by default for MMC1B 187 188 //"MMC1A: PRG RAM is always enabled" - Normally these roms should be classified as mapper 155 189 _forceWramOn = (_romInfo.DatabaseInfo.Board.compare("MMC1A") == 0); 190 191 _lastChrReg = MMC1Registers::RegA000; 192 193 UpdateState(); 194 } 195 WriteRegister(uint16_t addr,uint8_t value)196 virtual void WriteRegister(uint16_t addr, uint8_t value) override 197 { 198 uint64_t currentCycle = _console->GetCpu()->GetCycleCount(); 199 200 //Ignore write if within 2 cycles of another write (i.e the real write after a dummy write) 201 if(currentCycle - _lastWriteCycle >= 2) { 202 if(IsBufferFull(value)) { 203 switch((MMC1Registers)((addr & 0x6000) >> 13)) { 204 case MMC1Registers::Reg8000: _state.Reg8000 = _writeBuffer; break; 205 case MMC1Registers::RegA000: 206 _lastChrReg = MMC1Registers::RegA000; 207 _state.RegA000 = _writeBuffer; 208 break; 209 210 case MMC1Registers::RegC000: 211 _lastChrReg = MMC1Registers::RegC000; 212 _state.RegC000 = _writeBuffer; 213 break; 214 215 case MMC1Registers::RegE000: _state.RegE000 = _writeBuffer; break; 216 } 217 218 UpdateState(); 219 220 //Reset buffer after writing 5 bits 221 ResetBuffer(); 222 } 223 } 224 _lastWriteCycle = currentCycle; 225 } 226 }; 227