OXYGENHUB
◆ main openmsx / src / memory / RomManbow2.cc
6.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "RomManbow2.hh"
2
3
#include "AY8910.hh"
4
#include "DummyAY8910Periphery.hh"
5
#include "MSXCPUInterface.hh"
6
#include "SCC.hh"
7
8
#include "narrow.hh"
9
#include "one_of.hh"
10
#include "serialize.hh"
11
#include "unreachable.hh"
12
#include "xrange.hh"
13
14
#include <array>
15
#include <cassert>
16
#include <memory>
17
18
namespace openmsx {
19
20
[[nodiscard]] static const AmdFlash::ValidatedChip& getFlashChip(RomType type)
21
{
22
	switch(type) {
23
	using enum RomType;
24
	case MANBOW2:
25
	case MANBOW2_2:
26
	case HAMARAJANIGHT:
27
	case MEGAFLASHROMSCC:
28
		return AmdFlashChip::AM29F040B;
29
	case RBSC_FLASH_KONAMI_SCC:
30
		return AmdFlashChip::AM29F016D;
31
	default:
32
		UNREACHABLE;
33
	}
34
}
35
36
[[nodiscard]] static std::span<const bool> getWriteProtectSectors(RomType type)
37
{
38
	switch (type) {
39
	using enum RomType;
40
	case MANBOW2:
41
	case MANBOW2_2:
42
		{
43
			static constexpr std::array<const bool, 8> writeProtectSectors =
44
				{true, true, true, true, true, true, true, false};
45
			return writeProtectSectors;  // only last 64kB writable
46
		}
47
	case HAMARAJANIGHT:
48
		{
49
			static constexpr std::array<const bool, 8> writeProtectSectors =
50
				{true, true, true, true, false, false, true, true};
51
			return writeProtectSectors;  // only 128kB writable
52
		}
53
	case MEGAFLASHROMSCC:
54
	case RBSC_FLASH_KONAMI_SCC:
55
		return {};  // fully writeable
56
	default:
57
		UNREACHABLE;
58
	}
59
}
60
61
RomManbow2::RomManbow2(DeviceConfig& config, Rom&& rom_, RomType type)
62
	: MSXRom(config, std::move(rom_))
63
	, scc((type != RomType::RBSC_FLASH_KONAMI_SCC)
64
		? std::make_unique<SCC>(
65
			getName() + " SCC", config, getCurrentTime())
66
		: nullptr)
67
	, psg((type == one_of(RomType::MANBOW2_2, RomType::HAMARAJANIGHT))
68
		? std::make_unique<AY8910>(
69
			getName() + " PSG", DummyAY8910Periphery::instance(),
70
			config, getCurrentTime())
71
		: nullptr)
72
	, flash(rom, getFlashChip(type), getWriteProtectSectors(type), config)
73
	, romBlockDebug(*this, bank, 0x4000, 0x8000, 13)
74
{
75
	powerUp(getCurrentTime());
76
77
	if (psg) {
78
		auto& cpuInterface = getCPUInterface();
79
		cpuInterface.register_IO_Out_range(0x10, 2, this);
80
		cpuInterface.register_IO_In(0x12, this);
81
82
		if (type == RomType::HAMARAJANIGHT) {
83
			// this cartridge seems to have a much louder PSG than normal
84
			// TODO: find out if it's the same for Manbow 2 2nd release
85
			psg->setSoftwareVolume(5.0f, getCurrentTime());
86
		} else {
87
			// adjust PSG volume, see details in
88
			// https://github.com/openMSX/openMSX/issues/1934
89
			// note: this is a theoretical value. The actual
90
			// relative volume should be measured!
91
			psg->setSoftwareVolume(21000.0f/9000.0f, getCurrentTime());
92
		}
93
	}
94
}
95
96
RomManbow2::~RomManbow2()
97
{
98
	if (psg) {
99
		auto& cpuInterface = getCPUInterface();
100
		cpuInterface.unregister_IO_Out_range(0x10, 2, this);
101
		cpuInterface.unregister_IO_In(0x12, this);
102
	}
103
}
104
105
void RomManbow2::powerUp(EmuTime time)
106
{
107
	if (scc) {
108
		scc->powerUp(time);
109
	}
110
	reset(time);
111
}
112
113
void RomManbow2::reset(EmuTime time)
114
{
115
	for (auto i : xrange(4)) {
116
		setRom(i, byte(i));
117
	}
118
119
	sccEnabled = false;
120
	if (scc) {
121
		scc->reset(time);
122
	}
123
124
	if (psg) {
125
		psgLatch = 0;
126
		psg->reset(time);
127
	}
128
129
	flash.reset();
130
}
131
132
void RomManbow2::setRom(unsigned region, byte block)
133
{
134
	assert(region < 4);
135
	auto nrBlocks = narrow<unsigned>(flash.size() / 0x2000);
136
	bank[region] = block & narrow<byte>(nrBlocks - 1);
137
	invalidateDeviceRCache(0x4000 + region * 0x2000, 0x2000);
138
}
139
140
byte RomManbow2::peekMem(uint16_t address, EmuTime time) const
141
{
142
	if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
143
		return scc->peekMem(narrow_cast<uint8_t>(address & 0xFF), time);
144
	} else if ((0x4000 <= address) && (address < 0xC000)) {
145
		unsigned page = (address - 0x4000) / 0x2000;
146
		unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
147
		return flash.peek(addr, time);
148
	} else {
149
		return 0xFF;
150
	}
151
}
152
153
byte RomManbow2::readMem(uint16_t address, EmuTime time)
154
{
155
	if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
156
		return scc->readMem(narrow_cast<uint8_t>(address & 0xFF), time);
157
	} else if ((0x4000 <= address) && (address < 0xC000)) {
158
		unsigned page = (address - 0x4000) / 0x2000;
159
		unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
160
		return flash.read(addr, time);
161
	} else {
162
		return 0xFF;
163
	}
164
}
165
166
const byte* RomManbow2::getReadCacheLine(uint16_t address) const
167
{
168
	if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
169
		return nullptr;
170
	} else if ((0x4000 <= address) && (address < 0xC000)) {
171
		unsigned page = (address - 0x4000) / 0x2000;
172
		unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
173
		return flash.getReadCacheLine(addr);
174
	} else {
175
		return unmappedRead.data();
176
	}
177
}
178
179
void RomManbow2::writeMem(uint16_t address, byte value, EmuTime time)
180
{
181
	if (sccEnabled && (0x9800 <= address) && (address < 0xA000)) {
182
		// write to SCC
183
		scc->writeMem(narrow_cast<uint8_t>(address & 0xff), value, time);
184
		// note: writes to SCC also go to flash
185
		//    thanks to 'enen' for testing this
186
	}
187
	if ((0x4000 <= address) && (address < 0xC000)) {
188
		unsigned page = (address - 0x4000) / 0x2000;
189
		unsigned addr = (address & 0x1FFF) + 0x2000 * bank[page];
190
		flash.write(addr, value, time);
191
192
		if (scc && ((address & 0xF800) == 0x9000)) {
193
			// SCC enable/disable
194
			sccEnabled = ((value & 0x3F) == 0x3F);
195
			invalidateDeviceRCache(0x9800, 0x0800);
196
		}
197
		if ((address & 0x1800) == 0x1000) {
198
			// page selection
199
			setRom(page, value);
200
		}
201
	}
202
}
203
204
byte* RomManbow2::getWriteCacheLine(uint16_t address)
205
{
206
	if ((0x4000 <= address) && (address < 0xC000)) {
207
		return nullptr;
208
	} else {
209
		return unmappedWrite.data();
210
	}
211
}
212
213
byte RomManbow2::readIO(uint16_t port, EmuTime time)
214
{
215
	assert((port & 0xFF) == 0x12); (void)port;
216
	return psg->readRegister(psgLatch, time);
217
}
218
219
byte RomManbow2::peekIO(uint16_t port, EmuTime time) const
220
{
221
	assert((port & 0xFF) == 0x12); (void)port;
222
	return psg->peekRegister(psgLatch, time);
223
}
224
225
void RomManbow2::writeIO(uint16_t port, byte value, EmuTime time)
226
{
227
	if ((port & 0xFF) == 0x10) {
228
		psgLatch = value & 0x0F;
229
	} else {
230
		assert((port & 0xFF) == 0x11);
231
		psg->writeRegister(psgLatch, value, time);
232
	}
233
}
234
235
236
// version 1: initial version
237
// version 2: added optional built-in PSG
238
// version 3: made SCC optional (for RomType::RBSC_FLASH_KONAMI_SCC)
239
template<typename Archive>
240
void RomManbow2::serialize(Archive& ar, unsigned version)
241
{
242
	// skip MSXRom base class
243
	ar.template serializeBase<MSXDevice>(*this);
244
245
	if (scc) {
246
		ar.serialize("scc",        *scc,
247
		             "sccEnabled", sccEnabled);
248
	}
249
	if ((ar.versionAtLeast(version, 2)) && psg) {
250
		ar.serialize("psg",      *psg,
251
		             "psgLatch", psgLatch);
252
	}
253
	ar.serialize("flash", flash,
254
	             "bank",  bank);
255
}
256
INSTANTIATE_SERIALIZE_METHODS(RomManbow2);
257
REGISTER_MSXDEVICE(RomManbow2, "RomManbow2");
258
259
} // namespace openmsx