OXYGENHUB
◆ main openmsx / src / cpu / MSXMultiMemDevice.cc
3.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "MSXMultiMemDevice.hh"
2
3
#include "DummyDevice.hh"
4
#include "MSXCPUInterface.hh"
5
#include "TclObject.hh"
6
7
#include "stl.hh"
8
#include "view.hh"
9
10
#include <algorithm>
11
#include <cassert>
12
#include <ranges>
13
14
namespace openmsx {
15
16
MSXMultiMemDevice::Range::Range(
17
		unsigned base_, unsigned size_, MSXDevice& device_)
18
	: base(base_), size(size_), device(&device_)
19
{
20
}
21
22
MSXMultiMemDevice::MSXMultiMemDevice(HardwareConfig& hwConf)
23
24
	: MSXMultiDevice(hwConf)
25
{
26
	// add sentinel at the end
27
	ranges.emplace_back(0x0000, 0x10000, getCPUInterface().getDummyDevice());
28
}
29
30
MSXMultiMemDevice::~MSXMultiMemDevice()
31
{
32
	assert(empty());
33
}
34
35
static constexpr bool isInside(unsigned x, unsigned start, unsigned size)
36
{
37
	return (x - start) < size;
38
}
39
40
static constexpr bool overlap(unsigned start1, unsigned size1,
41
                              unsigned start2, unsigned size2)
42
{
43
	return (isInside(start1,             start2, size2)) ||
44
	       (isInside(start1 + size1 - 1, start2, size2));
45
}
46
47
bool MSXMultiMemDevice::canAdd(unsigned base, unsigned size)
48
{
49
	return std::ranges::none_of(view::drop_back(ranges, 1), [&](auto& rn) {
50
		return overlap(base, size, rn.base, rn.size);
51
	});
52
}
53
54
void MSXMultiMemDevice::add(MSXDevice& device, unsigned base, unsigned size)
55
{
56
	assert(canAdd(base, size));
57
	ranges.insert(begin(ranges), Range(base, size, device));
58
}
59
60
void MSXMultiMemDevice::remove(MSXDevice& device, unsigned base, unsigned size)
61
{
62
	ranges.erase(rfind_unguarded(ranges, Range(base, size, device)));
63
}
64
65
std::vector<MSXDevice*> MSXMultiMemDevice::getDevices() const
66
{
67
	return to_vector(std::views::transform(view::drop_back(ranges, 1),
68
	                                 [](auto& rn) { return rn.device; }));
69
}
70
71
const std::string& MSXMultiMemDevice::getName() const
72
{
73
	TclObject list;
74
	getNameList(list);
75
	deviceName = list.getString();
76
	return deviceName;
77
}
78
void MSXMultiMemDevice::getNameList(TclObject& result) const
79
{
80
	for (const auto& r : ranges) {
81
		const auto& name = r.device->getName();
82
		if (!name.empty()) {
83
			result.addListElement(name);
84
		}
85
	}
86
}
87
88
const MSXMultiMemDevice::Range& MSXMultiMemDevice::searchRange(unsigned address) const
89
{
90
	auto it = std::ranges::find_if(ranges, [&](const auto& r) { return isInside(address, r.base, r.size); });
91
	assert(it != ranges.end());
92
	return *it;
93
}
94
95
MSXDevice* MSXMultiMemDevice::searchDevice(unsigned address) const
96
{
97
	return searchRange(address).device;
98
}
99
100
uint8_t MSXMultiMemDevice::readMem(uint16_t address, EmuTime time)
101
{
102
	return searchDevice(address)->readMem(address, time);
103
}
104
105
uint8_t MSXMultiMemDevice::peekMem(uint16_t address, EmuTime time) const
106
{
107
	return searchDevice(address)->peekMem(address, time);
108
}
109
110
void MSXMultiMemDevice::writeMem(uint16_t address, uint8_t value, EmuTime time)
111
{
112
	searchDevice(address)->writeMem(address, value, time);
113
}
114
115
const uint8_t* MSXMultiMemDevice::getReadCacheLine(uint16_t start) const
116
{
117
	assert((start & CacheLine::HIGH) == start); // start is aligned
118
	// Because start is aligned we don't need to worry about the begin
119
	// address of the range. But we must make sure the end of the range
120
	// doesn't only fill a partial cacheline.
121
	const auto& range = searchRange(start);
122
	if (((range.base + range.size) & CacheLine::HIGH) == start) [[unlikely]] {
123
		// The end of this memory device only fills a partial
124
		// cacheline. This can't be cached.
125
		return nullptr;
126
	}
127
	return range.device->getReadCacheLine(start);
128
}
129
130
uint8_t* MSXMultiMemDevice::getWriteCacheLine(uint16_t start)
131
{
132
	assert((start & CacheLine::HIGH) == start);
133
	const auto& range = searchRange(start);
134
	if (((range.base + range.size) & CacheLine::HIGH) == start) [[unlikely]] {
135
		return nullptr;
136
	}
137
	return range.device->getWriteCacheLine(start);
138
}
139
140
} // namespace openmsx