OXYGENHUB
◆ main openmsx / src / memory / MSXMemoryMapperBase.cc
4.2 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "MSXMemoryMapperBase.hh"
2
3
#include "MSXException.hh"
4
5
#include "outer.hh"
6
#include "serialize.hh"
7
8
#include <algorithm>
9
#include <bit>
10
11
namespace openmsx {
12
13
[[nodiscard]] static unsigned getRamSize(const DeviceConfig& config)
14
{
15
	int kSize = config.getChildDataAsInt("size", 0);
16
	if ((kSize % 16) != 0) {
17
		throw MSXException("Mapper size is not a multiple of 16K: ", kSize);
18
	}
19
	if (kSize <= 0) {
20
		throw MSXException("Mapper size must be at least 16kB: ", kSize);
21
	}
22
	if (kSize > 4096) {
23
		throw MSXException("Mapper size must not be larger than 4096kB: ", kSize);
24
	}
25
	return kSize * 1024; // in bytes
26
}
27
28
MSXMemoryMapperBase::MSXMemoryMapperBase(const DeviceConfig& config)
29
	: MSXDevice(config)
30
	, MSXMapperIOClient(getMotherBoard())
31
	, checkedRam(config, getName(), "memory mapper", getRamSize(config))
32
	, debuggable(getMotherBoard(), getName())
33
{
34
	powerUp(getCurrentTime());
35
}
36
37
unsigned MSXMemoryMapperBase::getBaseSizeAlignment() const
38
{
39
	return 0x4000;
40
}
41
42
void MSXMemoryMapperBase::powerUp(EmuTime time)
43
{
44
	checkedRam.clear();
45
	reset(time);
46
}
47
48
void MSXMemoryMapperBase::reset(EmuTime /*time*/)
49
{
50
	// Most mappers initialize to segment 0 for all pages.
51
	// On MSX2 and higher, the BIOS will select segments 3..0 for pages 0..3.
52
	std::ranges::fill(registers, 0);
53
}
54
55
byte MSXMemoryMapperBase::readIO(uint16_t port, EmuTime time)
56
{
57
	return peekIO(port, time);
58
}
59
60
byte MSXMemoryMapperBase::peekIO(uint16_t port, EmuTime /*time*/) const
61
{
62
	auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000);
63
	return registers[port & 0x03] | byte(~(std::bit_ceil(numSegments) - 1));
64
}
65
66
void MSXMemoryMapperBase::writeIOImpl(uint16_t port, byte value, EmuTime /*time*/)
67
{
68
	auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000);
69
	registers[port & 3] = value & byte(std::bit_ceil(numSegments) - 1);
70
	// Note: subclasses are responsible for handling CPU cacheline stuff
71
}
72
73
unsigned MSXMemoryMapperBase::segmentOffset(byte page) const
74
{
75
	unsigned segment = registers[page];
76
	auto numSegments = narrow<unsigned>(checkedRam.size() / 0x4000);
77
	segment = (segment < numSegments) ? segment : segment & (numSegments - 1);
78
	return segment * 0x4000;
79
}
80
81
unsigned MSXMemoryMapperBase::calcAddress(uint16_t address) const
82
{
83
	return segmentOffset(narrow<byte>(address / 0x4000)) | (address & 0x3fff);
84
}
85
86
byte MSXMemoryMapperBase::peekMem(uint16_t address, EmuTime /*time*/) const
87
{
88
	return checkedRam.peek(calcAddress(address));
89
}
90
91
byte MSXMemoryMapperBase::readMem(uint16_t address, EmuTime /*time*/)
92
{
93
	return checkedRam.read(calcAddress(address));
94
}
95
96
void MSXMemoryMapperBase::writeMem(uint16_t address, byte value, EmuTime /*time*/)
97
{
98
	checkedRam.write(calcAddress(address), value);
99
}
100
101
const byte* MSXMemoryMapperBase::getReadCacheLine(uint16_t start) const
102
{
103
	return checkedRam.getReadCacheLine(calcAddress(start));
104
}
105
106
byte* MSXMemoryMapperBase::getWriteCacheLine(uint16_t start)
107
{
108
	return checkedRam.getWriteCacheLine(calcAddress(start));
109
}
110
111
112
// SimpleDebuggable
113
114
MSXMemoryMapperBase::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
115
                                        const std::string& name_)
116
	: SimpleDebuggable(motherBoard_, name_ + " regs",
117
	                   "Memory mapper registers", 4)
118
{
119
}
120
121
byte MSXMemoryMapperBase::Debuggable::read(unsigned address)
122
{
123
	auto& mapper = OUTER(MSXMemoryMapperBase, debuggable);
124
	return mapper.registers[address];
125
}
126
127
void MSXMemoryMapperBase::Debuggable::readBlock(unsigned start, std::span<byte> output)
128
{
129
	auto& mapper = OUTER(MSXMemoryMapperBase, debuggable);
130
	copy_to_range(std::span{mapper.registers}.subspan(start, output.size()), output);
131
}
132
133
void MSXMemoryMapperBase::Debuggable::write(unsigned address, byte value)
134
{
135
	auto& mapper = OUTER(MSXMemoryMapperBase, debuggable);
136
	mapper.writeIO(narrow<uint16_t>(address), value, EmuTime::dummy());
137
}
138
139
140
template<typename Archive>
141
void MSXMemoryMapperBase::serialize(Archive& ar, unsigned version)
142
{
143
	ar.template serializeBase<MSXDevice>(*this);
144
	if (ar.versionAtLeast(version, 2)) {
145
		ar.serialize("registers", registers);
146
	}
147
	// TODO ar.serialize("checkedRam", checkedRam);
148
	ar.serialize("ram", checkedRam.getUncheckedRam());
149
}
150
INSTANTIATE_SERIALIZE_METHODS(MSXMemoryMapperBase);
151
//REGISTER_MSXDEVICE(MSXMemoryMapperBase, "MemoryMapper");
152
153
} // namespace openmsx