1 #pragma once
2 #include "stdafx.h"
3 #include "BaseMapper.h"
4 #include "CPU.h"
5 
6 class Sunsoft4 : public BaseMapper
7 {
8 private:
9 	uint8_t _ntRegs[2];
10 	bool _useChrForNametables;
11 	bool _prgRamEnabled;
12 	uint32_t _licensingTimer;
13 	bool _usingExternalRom;
14 	uint8_t _externalPage = 0;
15 
UpdateNametables()16 	void UpdateNametables()
17 	{
18 		if(_useChrForNametables) {
19 			for(int i = 0; i < 4; i++) {
20 				uint8_t reg = 0;
21 				switch(GetMirroringType()) {
22 					case MirroringType::FourScreens: break; //4-screen mirroring is not supported by this mapper
23 					case MirroringType::Vertical: reg = i & 0x01; break;
24 					case MirroringType::Horizontal: reg = (i & 0x02) >> 1; break;
25 					case MirroringType::ScreenAOnly: reg = 0; break;
26 					case MirroringType::ScreenBOnly: reg = 1; break;
27 				}
28 
29 				SetPpuMemoryMapping(0x2000+i*0x400, 0x2000+i*0x400+0x3FF, ChrMemoryType::Default, _ntRegs[reg] * 0x400, _chrRamSize > 0 ? MemoryAccessType::ReadWrite : MemoryAccessType::Read);
30 			}
31 		} else {
32 			//Reset to default mirroring
33 			SetMirroringType(GetMirroringType());
34 		}
35 	}
36 
37 protected:
GetPRGPageSize()38 	virtual uint16_t GetPRGPageSize() override { return 0x4000; }
GetCHRPageSize()39 	virtual uint16_t GetCHRPageSize() override { return 0x800; }
40 
InitMapper()41 	void InitMapper() override
42 	{
43 		_useChrForNametables = false;
44 		_ntRegs[0] = _ntRegs[1] = 0;
45 
46 		_licensingTimer = 0;
47 		_usingExternalRom = false;
48 		_prgRamEnabled = false;
49 
50 		//Bank 0's initial state is undefined, but some roms expect it to be the first page
51 		SelectPRGPage(0, 0);
52 		SelectPRGPage(1, 7);
53 
54 		UpdateState();
55 	}
56 
StreamState(bool saving)57 	void StreamState(bool saving) override
58 	{
59 		BaseMapper::StreamState(saving);
60 
61 		Stream(_ntRegs[0], _ntRegs[1], _useChrForNametables, _prgRamEnabled, _usingExternalRom, _externalPage);
62 	}
63 
UpdateState()64 	void UpdateState()
65 	{
66 		if(!_prgRamEnabled) {
67 			RemoveCpuMemoryMapping(0x6000, 0x7FFF);
68 		} else {
69 			SetupDefaultWorkRam();
70 		}
71 
72 		if(_usingExternalRom) {
73 			if(_licensingTimer == 0) {
74 				RemoveCpuMemoryMapping(0x8000, 0xBFFF);
75 			} else {
76 				SelectPRGPage(0, _externalPage);
77 			}
78 		}
79 	}
80 
ProcessCpuClock()81 	void ProcessCpuClock() override
82 	{
83 		if(_licensingTimer) {
84 			_licensingTimer--;
85 			if(_licensingTimer == 0) {
86 				UpdateState();
87 			}
88 		}
89 	}
90 
WriteRAM(uint16_t addr,uint8_t value)91 	void WriteRAM(uint16_t addr, uint8_t value) override
92 	{
93 		if(addr >= 0x6000 && addr <= 0x7FFF) {
94 			_licensingTimer = 1024 * 105;
95 			UpdateState();
96 		}
97 		BaseMapper::WriteRAM(addr, value);
98 	}
99 
WriteRegister(uint16_t addr,uint8_t value)100 	void WriteRegister(uint16_t addr, uint8_t value) override
101 	{
102 		switch(addr & 0xF000) {
103 			case 0x8000: SelectCHRPage(0, value); break;
104 			case 0x9000: SelectCHRPage(1, value); break;
105 			case 0xA000: SelectCHRPage(2, value); break;
106 			case 0xB000: SelectCHRPage(3, value); break;
107 			case 0xC000:
108 				_ntRegs[0] = value | 0x80;
109 				UpdateNametables();
110 				break;
111 			case 0xD000:
112 				_ntRegs[1] = value | 0x80;
113 				UpdateNametables();
114 				break;
115 			case 0xE000:
116 				switch(value & 0x03) {
117 					case 0: SetMirroringType(MirroringType::Vertical); break;
118 					case 1: SetMirroringType(MirroringType::Horizontal); break;
119 					case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
120 					case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
121 				}
122 				_useChrForNametables = (value & 0x10) == 0x10;
123 				UpdateNametables();
124 				break;
125 			case 0xF000:
126 				bool externalPrg = (value & 0x08) == 0;
127 				if(externalPrg && GetPRGPageCount() > 8) {
128 					_usingExternalRom = true;
129 					_externalPage = 0x08 | ((value & 0x07) % (GetPRGPageCount() - 0x08));
130 					SelectPRGPage(0, _externalPage);
131 				} else {
132 					_usingExternalRom = false;
133 					SelectPRGPage(0, value & 0x07);
134 				}
135 
136 				_prgRamEnabled = (value & 0x10) == 0x10;
137 				UpdateState();
138 
139 				break;
140 		}
141 	}
142 };