1 #pragma once
2 #include "stdafx.h"
3 #include "BaseMapper.h"
4 #include "MemoryManager.h"
5 #include "CPU.h"
6 
7 class CityFighter : public BaseMapper
8 {
9 private:
10 	uint8_t _prgReg;
11 	uint8_t _prgMode;
12 	uint8_t _mirroring;
13 	uint8_t _chrRegs[8];
14 	bool _irqEnabled;
15 	uint16_t _irqCounter;
16 
17 protected:
GetPRGPageSize()18 	uint16_t GetPRGPageSize() override { return 0x2000; }
GetCHRPageSize()19 	uint16_t GetCHRPageSize() override { return 0x400; }
20 
InitMapper()21 	void InitMapper() override
22 	{
23 		_prgReg = 0;
24 		_prgMode = 0;
25 		_mirroring = 0;
26 		_irqCounter = 0;
27 		_irqEnabled = false;
28 		memset(_chrRegs, 0, sizeof(_chrRegs));
29 
30 		UpdateState();
31 	}
32 
StreamState(bool saving)33 	void StreamState(bool saving) override
34 	{
35 		BaseMapper::StreamState(saving);
36 		ArrayInfo<uint8_t> chrRegs { _chrRegs, 8 };
37 		Stream(_prgReg, _prgMode, _mirroring, _irqEnabled, _irqCounter, chrRegs);
38 	}
39 
UpdateState()40 	void UpdateState()
41 	{
42 		SelectPrgPage4x(0x8000, _prgReg);
43 		if(!_prgMode) {
44 			SelectPRGPage(2, _prgReg);
45 		}
46 
47 		for(int i = 0; i < 8; i++) {
48 			SelectCHRPage(i, _chrRegs[i]);
49 		}
50 
51 		switch(_mirroring) {
52 			case 0: SetMirroringType(MirroringType::Vertical); break;
53 			case 1: SetMirroringType(MirroringType::Horizontal); break;
54 			case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
55 			case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
56 		}
57 	}
58 
ProcessCpuClock()59 	void ProcessCpuClock() override
60 	{
61 		if(_irqEnabled) {
62 			_irqCounter--;
63 			if(_irqCounter == 0) {
64 				_console->GetCpu()->SetIrqSource(IRQSource::External);
65 			}
66 		}
67 	}
68 
WriteRegister(uint16_t addr,uint8_t value)69 	void WriteRegister(uint16_t addr, uint8_t value) override
70 	{
71 		switch(addr & 0xF00C) {
72 			case 0x9000:
73 				_prgReg = value & 0x0C;
74 				_mirroring = value & 0x03;
75 				break;
76 
77 			case 0x9004: case 0x9008: case 0x900C:
78 				if(addr & 0x800) {
79 					_console->GetMemoryManager()->Write(0x4011, (value & 0x0F) << 3, MemoryOperationType::Write);
80 				} else {
81 					_prgReg = value & 0x0C;
82 				}
83 				break;
84 
85 			case 0xC000: case 0xC004: case 0xC008: case 0xC00C:
86 				_prgMode = value & 0x01;
87 				break;
88 
89 			case 0xD000: _chrRegs[0] = (_chrRegs[0] & 0xF0) | (value & 0x0F); break;
90 			case 0xD004: _chrRegs[0] = (_chrRegs[0] & 0x0F) | (value << 4); break;
91 			case 0xD008: _chrRegs[1] = (_chrRegs[1] & 0xF0) | (value & 0x0F); break;
92 			case 0xD00C: _chrRegs[1] = (_chrRegs[1] & 0x0F) | (value << 4); break;
93 			case 0xA000: _chrRegs[2] = (_chrRegs[2] & 0xF0) | (value & 0x0F); break;
94 			case 0xA004: _chrRegs[2] = (_chrRegs[2] & 0x0F) | (value << 4); break;
95 			case 0xA008: _chrRegs[3] = (_chrRegs[3] & 0xF0) | (value & 0x0F); break;
96 			case 0xA00C: _chrRegs[3] = (_chrRegs[3] & 0x0F) | (value << 4); break;
97 			case 0xB000: _chrRegs[4] = (_chrRegs[4] & 0xF0) | (value & 0x0F); break;
98 			case 0xB004: _chrRegs[4] = (_chrRegs[4] & 0x0F) | (value << 4); break;
99 			case 0xB008: _chrRegs[5] = (_chrRegs[5] & 0xF0) | (value & 0x0F); break;
100 			case 0xB00C: _chrRegs[5] = (_chrRegs[5] & 0x0F) | (value << 4); break;
101 			case 0xE000: _chrRegs[6] = (_chrRegs[6] & 0xF0) | (value & 0x0F); break;
102 			case 0xE004: _chrRegs[6] = (_chrRegs[6] & 0x0F) | (value << 4); break;
103 			case 0xE008: _chrRegs[7] = (_chrRegs[7] & 0xF0) | (value & 0x0F); break;
104 			case 0xE00C: _chrRegs[7] = (_chrRegs[7] & 0x0F) | (value << 4); break;
105 			case 0xF000: _irqCounter = ((_irqCounter & 0x1E0) | ((value & 0x0F) << 1)); break;
106 			case 0xF004: _irqCounter = ((_irqCounter & 0x1E) | ((value & 0x0F) << 5)); break;
107 			case 0xF008:
108 				_irqEnabled = (value & 0x02) != 0;
109 				_console->GetCpu()->ClearIrqSource(IRQSource::External);
110 				break;
111 		}
112 
113 		UpdateState();
114 	}
115 };