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 };