1 #pragma once 2 #include "stdafx.h" 3 #include "BaseMapper.h" 4 #include "Namco163Audio.h" 5 #include "Console.h" 6 #include "BatteryManager.h" 7 8 enum class NamcoVariant 9 { 10 Namco163, 11 Namco175, 12 Namco340, 13 Unknown, 14 }; 15 16 class Namco163 : public BaseMapper 17 { 18 private: 19 unique_ptr<Namco163Audio> _audio; 20 21 NamcoVariant _variant; 22 bool _notNamco340; 23 bool _autoDetectVariant; 24 uint8_t _writeProtect; 25 bool _lowChrNtMode; 26 bool _highChrNtMode; 27 uint16_t _irqCounter; 28 SetVariant(NamcoVariant variant)29 void SetVariant(NamcoVariant variant) 30 { 31 if(_autoDetectVariant) { 32 if(!_notNamco340 || variant != NamcoVariant::Namco340) { 33 _variant = variant; 34 } 35 } 36 } 37 UpdateSaveRamAccess()38 void UpdateSaveRamAccess() 39 { 40 PrgMemoryType memType = HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam; 41 if(_variant == NamcoVariant::Namco163) { 42 bool globalWriteEnable = (_writeProtect & 0x40) == 0x40; 43 SetCpuMemoryMapping(0x6000, 0x67FF, 0, memType, globalWriteEnable && (_writeProtect & 0x01) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 44 SetCpuMemoryMapping(0x6800, 0x6FFF, 1, memType, globalWriteEnable && (_writeProtect & 0x02) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 45 SetCpuMemoryMapping(0x7000, 0x77FF, 2, memType, globalWriteEnable && (_writeProtect & 0x04) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 46 SetCpuMemoryMapping(0x7800, 0x7FFF, 3, memType, globalWriteEnable && (_writeProtect & 0x08) == 0x00 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 47 } else if(_variant == NamcoVariant::Namco175) { 48 SetCpuMemoryMapping(0x6000, 0x7FFF, 0, memType, (_writeProtect & 0x01) == 0x01 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read); 49 } else { 50 SetCpuMemoryMapping(0x6000, 0x7FFF, 0, memType, MemoryAccessType::NoAccess); 51 } 52 } 53 54 protected: GetPRGPageSize()55 virtual uint16_t GetPRGPageSize() override { return 0x2000; } GetCHRPageSize()56 virtual uint16_t GetCHRPageSize() override { return 0x400; } GetSaveRamPageSize()57 virtual uint32_t GetSaveRamPageSize() override { return 0x800; } AllowRegisterRead()58 virtual bool AllowRegisterRead() override { return true; } 59 InitMapper()60 void InitMapper() override 61 { 62 _audio.reset(new Namco163Audio(_console)); 63 64 switch(_romInfo.MapperID) { 65 case 19: 66 _variant = NamcoVariant::Namco163; 67 if(_romInfo.DatabaseInfo.Board == "NAMCOT-163") { 68 _variant = NamcoVariant::Namco163; 69 _autoDetectVariant = false; 70 } else if(_romInfo.DatabaseInfo.Board == "NAMCOT-175") { 71 _variant = NamcoVariant::Namco175; 72 _autoDetectVariant = false; 73 } else if(_romInfo.DatabaseInfo.Board == "NAMCOT-340") { 74 _variant = NamcoVariant::Namco340; 75 _autoDetectVariant = false; 76 } else { 77 _autoDetectVariant = true; 78 } 79 break; 80 case 210: 81 switch(_romInfo.SubMapperID) { 82 case 0: _variant = NamcoVariant::Unknown; _autoDetectVariant = true; break; 83 case 1: _variant = NamcoVariant::Namco175; _autoDetectVariant = false; break; 84 case 2: _variant = NamcoVariant::Namco340; _autoDetectVariant = false; break; 85 } 86 break; 87 } 88 89 _notNamco340 = false; 90 91 _writeProtect = 0; 92 _lowChrNtMode = false; 93 _highChrNtMode = false; 94 _irqCounter = 0; 95 96 AddRegisterRange(0x4800, 0x5FFF, MemoryOperation::Any); 97 RemoveRegisterRange(0x6000, 0xFFFF, MemoryOperation::Read); 98 99 SelectPRGPage(3, -1); 100 UpdateSaveRamAccess(); 101 } 102 StreamState(bool saving)103 void StreamState(bool saving) override 104 { 105 BaseMapper::StreamState(saving); 106 107 SnapshotInfo audio{ _audio.get() }; 108 Stream(_variant, _notNamco340, _autoDetectVariant, _writeProtect, _lowChrNtMode, _highChrNtMode, _irqCounter, audio); 109 if(!saving) { 110 UpdateSaveRamAccess(); 111 } 112 } 113 LoadBattery()114 void LoadBattery() override 115 { 116 if(HasBattery()) { 117 vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0); 118 _console->GetBatteryManager()->LoadBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size()); 119 120 memcpy(_saveRam, batteryContent.data(), _saveRamSize); 121 memcpy(_audio->GetInternalRam(), batteryContent.data()+_saveRamSize, Namco163Audio::AudioRamSize); 122 } 123 } 124 SaveBattery()125 void SaveBattery() override 126 { 127 if(HasBattery()) { 128 vector<uint8_t> batteryContent(_saveRamSize + Namco163Audio::AudioRamSize, 0); 129 memcpy(batteryContent.data(), _saveRam, _saveRamSize); 130 memcpy(batteryContent.data() + _saveRamSize, _audio->GetInternalRam(), Namco163Audio::AudioRamSize); 131 132 _console->GetBatteryManager()->SaveBattery(".sav", batteryContent.data(), (uint32_t)batteryContent.size()); 133 } 134 } 135 ProcessCpuClock()136 void ProcessCpuClock() override 137 { 138 if(_irqCounter & 0x8000 && (_irqCounter & 0x7FFF) != 0x7FFF) { 139 _irqCounter++; 140 if((_irqCounter & 0x7FFF) == 0x7FFF) { 141 _console->GetCpu()->SetIrqSource(IRQSource::External); 142 } 143 } 144 145 if(_variant == NamcoVariant::Namco163) { 146 _audio->Clock(); 147 } 148 } 149 WriteRAM(uint16_t addr,uint8_t value)150 void WriteRAM(uint16_t addr, uint8_t value) override 151 { 152 if(addr >= 0x6000 && addr <= 0x7FFF) { 153 _notNamco340 = true; 154 if(_variant == NamcoVariant::Namco340) { 155 SetVariant(NamcoVariant::Unknown); 156 } 157 } 158 BaseMapper::WriteRAM(addr, value); 159 } 160 ReadRegister(uint16_t addr)161 uint8_t ReadRegister(uint16_t addr) override 162 { 163 switch(addr & 0xF800) { 164 case 0x4800: return _audio->ReadRegister(addr); 165 case 0x5000: return _irqCounter & 0xFF; 166 case 0x5800: return (_irqCounter >> 8); 167 default: return BaseMapper::ReadRegister(addr); 168 } 169 } 170 WriteRegister(uint16_t addr,uint8_t value)171 void WriteRegister(uint16_t addr, uint8_t value) override 172 { 173 addr &= 0xF800; 174 175 switch(addr) { 176 case 0x4800: 177 SetVariant(NamcoVariant::Namco163); 178 _audio->WriteRegister(addr, value); 179 break; 180 181 case 0x5000: 182 SetVariant(NamcoVariant::Namco163); 183 _irqCounter = (_irqCounter & 0xFF00) | value; 184 _console->GetCpu()->ClearIrqSource(IRQSource::External); 185 break; 186 187 case 0x5800: 188 SetVariant(NamcoVariant::Namco163); 189 _irqCounter = (_irqCounter & 0x00FF) | (value << 8); 190 _console->GetCpu()->ClearIrqSource(IRQSource::External); 191 break; 192 193 case 0x8000: case 0x8800: case 0x9000: case 0x9800: { 194 uint8_t bankNumber = (addr - 0x8000) >> 11; 195 if(!_lowChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) { 196 SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam); 197 } else { 198 SelectCHRPage(bankNumber, value); 199 } 200 break; 201 } 202 203 case 0xA000: case 0xA800: case 0xB000: case 0xB800: { 204 uint8_t bankNumber = ((addr - 0xA000) >> 11) + 4; 205 if(!_highChrNtMode && value >= 0xE0 && _variant == NamcoVariant::Namco163) { 206 SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam); 207 } else { 208 SelectCHRPage(bankNumber, value); 209 } 210 break; 211 } 212 213 case 0xC000: case 0xC800: case 0xD000: case 0xD800: 214 if(addr >= 0xC800) { 215 SetVariant(NamcoVariant::Namco163); 216 } else if(_variant != NamcoVariant::Namco163) { 217 SetVariant(NamcoVariant::Namco175); 218 } 219 220 if(_variant == NamcoVariant::Namco175) { 221 _writeProtect = value; 222 UpdateSaveRamAccess(); 223 } else { 224 uint8_t bankNumber = ((addr - 0xC000) >> 11) + 8; 225 if(value >= 0xE0) { 226 SelectCHRPage(bankNumber, value & 0x01, ChrMemoryType::NametableRam); 227 } else { 228 SelectCHRPage(bankNumber, value); 229 } 230 } 231 break; 232 233 case 0xE000: 234 if((value & 0x80) == 0x80) { 235 SetVariant(NamcoVariant::Namco340); 236 } else if((value & 0x40) == 0x40 && _variant != NamcoVariant::Namco163) { 237 SetVariant(NamcoVariant::Namco340); 238 } 239 240 SelectPRGPage(0, value & 0x3F); 241 242 if(_variant == NamcoVariant::Namco340) { 243 switch((value & 0xC0) >> 6) { 244 case 0: SetMirroringType(MirroringType::ScreenAOnly); break; 245 case 1: SetMirroringType(MirroringType::Vertical); break; 246 case 2: SetMirroringType(MirroringType::Horizontal); break; 247 case 3: SetMirroringType(MirroringType::ScreenBOnly); break; 248 } 249 } else if(_variant == NamcoVariant::Namco163) { 250 _audio->WriteRegister(addr, value); 251 } 252 break; 253 254 case 0xE800: 255 SelectPRGPage(1, value & 0x3F); 256 if(_variant == NamcoVariant::Namco163) { 257 _lowChrNtMode = (value & 0x40) == 0x40; 258 _highChrNtMode = (value & 0x80) == 0x80; 259 } 260 break; 261 262 case 0xF000: 263 SelectPRGPage(2, value & 0x3F); 264 break; 265 266 case 0xF800: 267 SetVariant(NamcoVariant::Namco163); 268 if(_variant == NamcoVariant::Namco163) { 269 _writeProtect = value; 270 UpdateSaveRamAccess(); 271 272 _audio->WriteRegister(addr, value); 273 } 274 break; 275 } 276 } 277 };