OXYGENHUB
◆ main openmsx / src / memory / RomHalnote.cc
4.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
/*
2
 * HALNOTE mapper
3
 *
4
 * This info is extracted from the romMapperHalnote.c source in blueMSX,
5
 * implemented by white_cat.
6
 *
7
 * This is a 1024kB mapper, it's divided in 128 pages of 8kB. The last 512kB
8
 * can also be mapped as 256 pages of 2kB. There is 16kB SRAM.
9
 *
10
 * Main bank-switch registers:
11
 *   bank 0,  region: [0x4000-0x5FFF],  switch addr: 0x4FFF
12
 *   bank 1,  region: [0x6000-0x7FFF],  switch addr: 0x6FFF
13
 *   bank 2,  region: [0x8000-0x9FFF],  switch addr: 0x8FFF
14
 *   bank 3,  region: [0xA000-0xBFFF],  switch addr: 0xAFFF
15
 * Sub-bank-switch registers:
16
 *   bank 0,  region: [0x7000-0x77FF],  switch addr: 0x77FF
17
 *   bank 1,  region: [0x7800-0x7FFF],  switch addr: 0x7FFF
18
 * Note that the two sub-banks overlap with main bank 1!
19
 *
20
 * The upper bit (0x80) of the first two main bank-switch registers are special:
21
 *   bank 0, bit7   SRAM      enabled in [0x0000-0x3FFF]  (1=enabled)
22
 *   bank 1, bit7   submapper enabled in [0x7000-0x7FFF]  (1=enabled)
23
 * If enabled, the submapper shadows (part of) main bank 1.
24
 */
25
26
#include "RomHalnote.hh"
27
28
#include "SRAM.hh"
29
30
#include "CacheLine.hh"
31
#include "MSXException.hh"
32
#include "serialize.hh"
33
34
#include "one_of.hh"
35
#include "xrange.hh"
36
37
#include <memory>
38
39
namespace openmsx {
40
41
RomHalnote::RomHalnote(const DeviceConfig& config, Rom&& rom_)
42
	: Rom8kBBlocks(config, std::move(rom_))
43
{
44
	if (rom.size() != 0x100000) {
45
		throw MSXException(
46
			"Rom for HALNOTE mapper must be exactly 1MB in size.");
47
	}
48
	sram = std::make_unique<SRAM>(getName() + " SRAM", 0x4000, config);
49
	reset(EmuTime::dummy());
50
}
51
52
void RomHalnote::reset(EmuTime /*time*/)
53
{
54
	subBanks[0] = subBanks[1] = 0;
55
	sramEnabled = false;
56
	subMapperEnabled = false;
57
58
	setUnmapped(0);
59
	setUnmapped(1);
60
	for (auto i : xrange(2, 6)) {
61
		setRom(i, 0);
62
	}
63
	setUnmapped(6);
64
	setUnmapped(7);
65
}
66
67
const byte* RomHalnote::getReadCacheLine(uint16_t address) const
68
{
69
	if (subMapperEnabled && (0x7000 <= address) && (address < 0x8000)) {
70
		// sub-mapper
71
		int subBank = address < 0x7800 ? 0 : 1;
72
		return &rom[0x80000 + subBanks[subBank] * 0x800 + (address & 0x7FF)];
73
	} else {
74
		// default mapper implementation
75
		return Rom8kBBlocks::getReadCacheLine(address);
76
	}
77
}
78
byte RomHalnote::readMem(uint16_t address, EmuTime /*time*/)
79
{
80
	// all reads are cacheable, reuse that implementation
81
	return *RomHalnote::getReadCacheLine(address);
82
}
83
84
void RomHalnote::writeMem(uint16_t address, byte value, EmuTime /*time*/)
85
{
86
	if (address < 0x4000) {
87
		// SRAM region
88
		if (sramEnabled) {
89
			sram->write(address, value);
90
		}
91
	} else if (address < 0xC000) {
92
		if (address == one_of(0x77FF, 0x7FFF)) {
93
			// sub-mapper bank switch region
94
			int subBank = address < 0x7800 ? 0 : 1;
95
			if (subBanks[subBank] != value) {
96
				subBanks[subBank] = value;
97
				if (subMapperEnabled) {
98
					invalidateDeviceRCache(
99
						0x7000 + subBank * 0x800, 0x800);
100
				}
101
			}
102
		} else if ((address & 0x1FFF) == 0x0FFF) {
103
			// normal bank switch region
104
			auto bank = address >> 13; // 2-5
105
			setRom(bank, value);
106
			if (bank == 2) {
107
				// sram enable/disable
108
				bool newSramEnabled = (value & 0x80) != 0;
109
				if (newSramEnabled != sramEnabled) {
110
					sramEnabled = newSramEnabled;
111
					if (sramEnabled) {
112
						setBank(0, &(*sram)[0x0000], value);
113
						setBank(1, &(*sram)[0x2000], value);
114
					} else {
115
						setUnmapped(0);
116
						setUnmapped(1);
117
					}
118
					// 'R' is already handled
119
					invalidateDeviceWCache(0x0000, 0x4000);
120
				}
121
			} else if (bank == 3) {
122
				// sub-mapper enable/disable
123
				subMapperEnabled = (value & 0x80) != 0;
124
				if (subMapperEnabled) {
125
					invalidateDeviceRCache(0x7000, 0x1000);
126
				}
127
			}
128
		}
129
	}
130
}
131
132
byte* RomHalnote::getWriteCacheLine(uint16_t address)
133
{
134
	if (address < 0x4000) {
135
		// SRAM region
136
		if (sramEnabled) {
137
			return nullptr;
138
		}
139
	} else if (address < 0xC000) {
140
		if ((address & CacheLine::HIGH) == one_of(0x77FF & CacheLine::HIGH,
141
		                                          0x7FFF & CacheLine::HIGH)) {
142
			// sub-mapper bank switch region
143
			return nullptr;
144
		} else if ((address & 0x1FFF & CacheLine::HIGH) ==
145
		           (0x0FFF & CacheLine::HIGH)) {
146
			// normal bank switch region
147
			return nullptr;
148
		}
149
	}
150
	return unmappedWrite.data();
151
}
152
153
template<typename Archive>
154
void RomHalnote::serialize(Archive& ar, unsigned /*version*/)
155
{
156
	ar.template serializeBase<Rom8kBBlocks>(*this);
157
	ar.serialize("subBanks",         subBanks,
158
	             "sramEnabled",      sramEnabled,
159
	             "subMapperEnabled", subMapperEnabled);
160
161
}
162
INSTANTIATE_SERIALIZE_METHODS(RomHalnote);
163
REGISTER_MSXDEVICE(RomHalnote, "RomHalnote");
164
165
} // namespace openmsx