OXYGENHUB
◆ main openmsx / src / memory / MSXMapperIO.cc
4.2 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MSXMapperIO.hh"
2
3
#include "HardwareConfig.hh"
4
#include "MSXException.hh"
5
#include "MSXMotherBoard.hh"
6
#include "XMLElement.hh"
7
#include "serialize.hh"
8
9
#include "StringOp.hh"
10
#include "enumerate.hh"
11
#include "outer.hh"
12
#include "stl.hh"
13
14
namespace openmsx {
15
16
[[nodiscard]] static byte calcReadBackMask(MSXMotherBoard& motherBoard)
17
{
18
	std::string_view type = motherBoard.getMachineConfig()->getConfig().getChildData(
19
	                               "MapperReadBackBits", "largest");
20
	if (type == "largest") {
21
		return 0xff; // all bits can be read
22
	}
23
	auto bits = StringOp::stringTo<int>(type);
24
	if (!bits) {
25
		throw FatalError("Unknown mapper type: \"", type, "\".");
26
	}
27
	if (*bits < 0 || *bits > 8) {
28
		throw FatalError("MapperReadBackBits out of range: \"", type, "\".");
29
	}
30
	return byte(~(unsigned(-1) << *bits));
31
}
32
33
MSXMapperIO::MSXMapperIO(const DeviceConfig& config)
34
	: MSXDevice(config)
35
	, debuggable(getMotherBoard(), getName())
36
	, mask(calcReadBackMask(getMotherBoard()))
37
{
38
	reset(EmuTime::dummy());
39
}
40
41
void MSXMapperIO::setMode(Mode mode_, byte mask_, byte baseValue_)
42
{
43
	mode = mode_;
44
	mask = mask_;
45
	baseValue = baseValue_;
46
}
47
48
void MSXMapperIO::registerMapper(MSXMemoryMapperInterface* mapper)
49
{
50
	mappers.push_back(mapper);
51
}
52
53
void MSXMapperIO::unregisterMapper(MSXMemoryMapperInterface* mapper)
54
{
55
	mappers.erase(rfind_unguarded(mappers, mapper));
56
}
57
58
byte MSXMapperIO::readIO(uint16_t port, EmuTime time)
59
{
60
	byte value = [&] {
61
		if (mode == Mode::EXTERNAL) {
62
			byte result = 0xFF;
63
			for (auto* mapper : mappers) {
64
				result &= mapper->readIO(port, time);
65
			}
66
			return result;
67
		} else {
68
			return registers[port & 3];
69
		}
70
	}();
71
	return (value & mask) | (baseValue & ~mask);
72
}
73
74
byte MSXMapperIO::peekIO(uint16_t port, EmuTime time) const
75
{
76
	byte value = [&] {
77
		if (mode == Mode::EXTERNAL) {
78
			byte result = 0xFF;
79
			for (const auto* mapper : mappers) {
80
				result &= mapper->peekIO(port, time);
81
			}
82
			return result;
83
		} else {
84
			return registers[port & 3];
85
		}
86
	}();
87
	return (value & mask) | (baseValue & ~mask);
88
}
89
90
void MSXMapperIO::writeIO(uint16_t port, byte value, EmuTime time)
91
{
92
	registers[port & 3] = value;
93
94
	// Note: the mappers are responsible for invalidating/filling the CPU
95
	// cache-lines.
96
	for (auto* mapper : mappers) {
97
		mapper->writeIO(port, value, time);
98
	}
99
}
100
101
102
// SimpleDebuggable
103
// This debuggable is here for backwards compatibility. For more accurate
104
// results, use the debuggable belonging to a specific mapper.
105
106
MSXMapperIO::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
107
                                    const std::string& name_)
108
	: SimpleDebuggable(motherBoard_, name_, "Memory mapper registers", 4)
109
{
110
}
111
112
byte MSXMapperIO::Debuggable::read(unsigned address)
113
{
114
	// return the last written value, with full 8-bit precision.
115
	auto& mapperIO = OUTER(MSXMapperIO, debuggable);
116
	return mapperIO.registers[address & 3];
117
}
118
119
void MSXMapperIO::Debuggable::readBlock(unsigned start, std::span<byte> output)
120
{
121
	auto& mapperIO = OUTER(MSXMapperIO, debuggable);
122
	copy_to_range(std::span{mapperIO.registers}.subspan(start, output.size()), output);
123
}
124
125
void MSXMapperIO::Debuggable::write(unsigned address, byte value,
126
                                    EmuTime time)
127
{
128
	auto& mapperIO = OUTER(MSXMapperIO, debuggable);
129
	mapperIO.writeIO(narrow_cast<uint16_t>(address), value, time);
130
}
131
132
133
template<typename Archive>
134
void MSXMapperIO::serialize(Archive& ar, unsigned version)
135
{
136
	if (ar.versionBelow(version, 2)) {
137
		// In version 1 we stored the mapper state in MSXMapperIO instead of
138
		// in the individual mappers, so distribute the state to them.
139
		assert(Archive::IS_LOADER);
140
		ar.serialize("registers", registers);
141
		for (auto [page, reg] : enumerate(registers)) {
142
			writeIO(uint16_t(page), reg, EmuTime::dummy());
143
		}
144
	}
145
	if (ar.versionAtLeast(version, 3)) {
146
		// In version 3 we store the mapper state BOTH here and in the
147
		// individual mappers. The reason for this is:
148
		// - There are MSX machines with a S1985 but without a memory
149
		//   mapper. To support those we need to store the state here.
150
		// - In rare cases (e.g. musical memory mapper) the mapper state
151
		//   can be different for specific mappers.
152
		ar.serialize("registers", registers);
153
	}
154
}
155
INSTANTIATE_SERIALIZE_METHODS(MSXMapperIO);
156
157
} // namespace openmsx