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