1 #pragma once
2 #include "stdafx.h"
3 #include "MMC3.h"
4 
5 class BmcHpxx : public MMC3
6 {
7 private:
8 	uint8_t _exRegs[5];
9 	bool _locked;
10 
11 protected:
GetDipSwitchCount()12 	uint32_t GetDipSwitchCount() override { return 4; }
AllowRegisterRead()13 	bool AllowRegisterRead() override { return true; }
14 
InitMapper()15 	void InitMapper() override
16 	{
17 		memset(_exRegs, 0, sizeof(_exRegs));
18 		_locked = false;
19 
20 		MMC3::InitMapper();
21 		AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Any);
22 		RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
23 	}
24 
Reset(bool softReset)25 	void Reset(bool softReset) override
26 	{
27 		MMC3::Reset(softReset);
28 		memset(_exRegs, 0, sizeof(_exRegs));
29 		_locked = false;
30 		MMC3::ResetMmc3();
31 		UpdateState();
32 	}
33 
StreamState(bool saving)34 	void StreamState(bool saving) override
35 	{
36 		MMC3::StreamState(saving);
37 		Stream(_exRegs[0], _exRegs[1], _exRegs[2], _exRegs[3], _exRegs[4], _locked);
38 	}
39 
40 	void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
41 	{
42 		if(_exRegs[0] & 0x04) {
43 			switch(_exRegs[0] & 0x03) {
44 				case 0:
45 				case 1: SelectChrPage8x(0, (_exRegs[2] & 0x3F) << 3); break;
46 				case 2: SelectChrPage8x(0, ((_exRegs[2] & 0x3E) | (_exRegs[4] & 0x01)) << 3); break;
47 				case 3: SelectChrPage8x(0, ((_exRegs[2] & 0x3C) | (_exRegs[4] & 0x03)) << 3); break;
48 			}
49 		} else {
50 			uint8_t base, mask;
51 			if(_exRegs[0] & 0x01) {
52 				base = _exRegs[2] & 0x30;
53 				mask = 0x7F;
54 			} else {
55 				base = _exRegs[2] & 0x20;
56 				mask = 0xFF;
57 			}
58 			MMC3::SelectCHRPage(slot, (page & mask) | (base << 3));
59 		}
60 	}
61 
62 	void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom) override
63 	{
64 		if(_exRegs[0] & 0x04) {
65 			if((_exRegs[0] & 0x0F) == 0x04) {
66 				SelectPrgPage2x(0, (_exRegs[1] & 0x1F) << 1);
67 				SelectPrgPage2x(1, (_exRegs[1] & 0x1F) << 1);
68 			} else {
69 				SelectPrgPage4x(0, (_exRegs[1] & 0x1E) << 1);
70 			}
71 		} else {
72 			uint8_t base, mask;
73 			if(_exRegs[0] & 0x02) {
74 				base = _exRegs[1] & 0x18;
75 				mask = 0x0F;
76 			} else {
77 				base = _exRegs[1] & 0x10;
78 				mask = 0x1F;
79 			}
80 			MMC3::SelectPRGPage(slot, (page & mask) | (base << 1));
81 		}
82 	}
83 
UpdateMirroring()84 	void UpdateMirroring() override
85 	{
86 		if(_exRegs[0] & 0x04) {
87 			SetMirroringType(_exRegs[4] & 0x04 ? MirroringType::Vertical : MirroringType::Horizontal);
88 		} else {
89 			MMC3::UpdateMirroring();
90 		}
91 	}
92 
ReadRegister(uint16_t addr)93 	uint8_t ReadRegister(uint16_t addr) override
94 	{
95 		return GetDipSwitches();
96 	}
97 
WriteRegister(uint16_t addr,uint8_t value)98 	void WriteRegister(uint16_t addr, uint8_t value) override
99 	{
100 		if(addr < 0x8000) {
101 			if(!_locked) {
102 				_exRegs[addr & 0x03] = value;
103 				_locked = (value & 0x80) != 0;
104 				UpdatePrgMapping();
105 				UpdateChrMapping();
106 			}
107 		} else {
108 			if(_exRegs[0] & 0x04) {
109 				_exRegs[4] = value;
110 				UpdateChrMapping();
111 			} else {
112 				MMC3::WriteRegister(addr, value);
113 			}
114 		}
115 	}
116 };