OXYGENHUB
◆ main openmsx / src / memory / RomBlocks.cc
5.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "RomBlocks.hh"
2
3
#include "MSXCliComm.hh"
4
#include "SRAM.hh"
5
6
#include "narrow.hh"
7
#include "serialize.hh"
8
#include "unreachable.hh"
9
#include "xrange.hh"
10
11
#include <bit>
12
13
namespace openmsx {
14
15
// minimal attempt to avoid seeing this warning too often
16
static Sha1Sum alreadyWarnedForSha1Sum;
17
18
template<unsigned BANK_SIZE>
19
RomBlocks<BANK_SIZE>::RomBlocks(
20
		const DeviceConfig& config, Rom&& rom_,
21
		unsigned debugBankSizeShift)
22
	: MSXRom(config, std::move(rom_))
23
	, romBlockDebug(
24
		*this,  blockNr, 0x0000, 0x10000,
25
		std::bit_width(BANK_SIZE) - 1, debugBankSizeShift)
26
{
27
	static_assert(std::has_single_bit(BANK_SIZE), "BANK_SIZE must be a power of two");
28
	auto extendedSize = (rom.size() + BANK_SIZE - 1) & ~(BANK_SIZE - 1);
29
	if (extendedSize != rom.size() && alreadyWarnedForSha1Sum != rom.getOriginalSHA1()) {
30
		config.getCliComm().printWarning(
31
			"(uncompressed) ROM image filesize was not a multiple "
32
			"of ", BANK_SIZE / 1024, "kB (which is required for mapper type ",
33
			config.findChild("mappertype")->getData(), "), so we "
34
			"padded it to be correct. But if the ROM you are "
35
			"running was just dumped, the dump is probably not "
36
			"complete/correct!");
37
		alreadyWarnedForSha1Sum = rom.getOriginalSHA1();
38
	}
39
	rom.addPadding(extendedSize);
40
	nrBlocks = narrow<decltype(nrBlocks)>(rom.size() / BANK_SIZE);
41
	assert((nrBlocks * BANK_SIZE) == rom.size());
42
43
	// by default no extra mappable memory block
44
	extraMem = {};
45
46
	// Default mask: wraps at end of ROM image.
47
	blockMask = nrBlocks - 1;
48
	for (auto i : xrange(NUM_BANKS)) {
49
		setRom(i, 0);
50
	}
51
}
52
53
template<unsigned BANK_SIZE>
54
RomBlocks<BANK_SIZE>::~RomBlocks() = default;
55
56
template<unsigned BANK_SIZE>
57
unsigned RomBlocks<BANK_SIZE>::getBaseSizeAlignment() const
58
{
59
	return BANK_SIZE;
60
}
61
62
template<unsigned BANK_SIZE>
63
byte RomBlocks<BANK_SIZE>::peekMem(uint16_t address, EmuTime /*time*/) const
64
{
65
	return bankPtr[address / BANK_SIZE][address & BANK_MASK];
66
}
67
68
template<unsigned BANK_SIZE>
69
byte RomBlocks<BANK_SIZE>::readMem(uint16_t address, EmuTime time)
70
{
71
	return RomBlocks<BANK_SIZE>::peekMem(address, time);
72
}
73
74
template<unsigned BANK_SIZE>
75
const byte* RomBlocks<BANK_SIZE>::getReadCacheLine(uint16_t address) const
76
{
77
	return &bankPtr[address / BANK_SIZE][address & BANK_MASK];
78
}
79
80
template<unsigned BANK_SIZE>
81
void RomBlocks<BANK_SIZE>::setBank(unsigned region, const byte* adr, byte block)
82
{
83
	assert("address passed to setBank() is not serializable" &&
84
	       ((adr == unmappedRead.data()) ||
85
	        ((&rom[0] <= adr) && (adr <= &rom[rom.size() - 1])) ||
86
	        (sram && (&(*sram)[0] <= adr) &&
87
	                       (adr <= &(*sram)[sram->size() - 1])) ||
88
	        (!extraMem.empty() && (&extraMem.front() <= adr) && (adr <= &extraMem.back()))));
89
	bankPtr[region] = adr;
90
	blockNr[region] = block; // only for debuggable
91
	fillDeviceRCache(region * BANK_SIZE, BANK_SIZE, adr);
92
}
93
94
template<unsigned BANK_SIZE>
95
void RomBlocks<BANK_SIZE>::setUnmapped(unsigned region)
96
{
97
	setBank(region, unmappedRead.data(), 255);
98
}
99
100
template<unsigned BANK_SIZE>
101
void RomBlocks<BANK_SIZE>::setExtraMemory(std::span<const byte> mem)
102
{
103
	extraMem = mem;
104
}
105
106
template<unsigned BANK_SIZE>
107
void RomBlocks<BANK_SIZE>::setRom(unsigned region, unsigned block)
108
{
109
	// Note: Some cartridges have a number of blocks that is not a power of 2,
110
	//       for those we have to make an exception for "block < nrBlocks".
111
	block = (block < nrBlocks) ? block : block & blockMask;
112
	if (block < nrBlocks) {
113
		setBank(region, &rom[block * BANK_SIZE],
114
		        narrow_cast<byte>(block)); // only used for debug, narrowing is fine
115
	} else {
116
		setBank(region, unmappedRead.data(), 255);
117
	}
118
}
119
120
// version 1: initial version
121
// version 2: added blockNr
122
template<unsigned BANK_SIZE>
123
template<typename Archive>
124
void RomBlocks<BANK_SIZE>::serialize(Archive& ar, unsigned /*version*/)
125
{
126
	// skip MSXRom base class
127
	ar.template serializeBase<MSXDevice>(*this);
128
129
	if (sram) ar.serialize("sram", *sram);
130
131
	std::array<size_t, NUM_BANKS> offsets;
132
	auto romSize = rom.size();
133
	auto sramSize = sram ? sram->size() : 0;
134
	if constexpr (Archive::IS_LOADER) {
135
		ar.serialize("banks", offsets);
136
		for (auto i : xrange(NUM_BANKS)) {
137
			if (offsets[i] == size_t(-1) ||
138
			    offsets[i] == unsigned(-1)) { // for bw-compat with old savestates
139
				bankPtr[i] = unmappedRead.data();
140
			} else if (offsets[i] < romSize) {
141
				bankPtr[i] = &rom[offsets[i]];
142
			} else if (offsets[i] < (romSize + sramSize)) {
143
				assert(sram);
144
				bankPtr[i] = &(*sram)[offsets[i] - romSize];
145
			} else if (offsets[i] < (romSize + sramSize + extraMem.size())) {
146
				bankPtr[i] = &extraMem[offsets[i] - romSize - sramSize];
147
			} else {
148
				// TODO throw
149
				UNREACHABLE;
150
			}
151
		}
152
	} else {
153
		for (auto i : xrange(NUM_BANKS)) {
154
			if (bankPtr[i] == unmappedRead.data()) {
155
				offsets[i] = size_t(-1);
156
			} else if ((&rom[0] <= bankPtr[i]) &&
157
			           (bankPtr[i] <= &rom[romSize - 1])) {
158
				offsets[i] = size_t(bankPtr[i] - &rom[0]);
159
			} else if (sram && (&(*sram)[0] <= bankPtr[i]) &&
160
			           (bankPtr[i] <= &(*sram)[sramSize - 1])) {
161
				offsets[i] = size_t(bankPtr[i] - &(*sram)[0] + romSize);
162
			} else if (!extraMem.empty() &&
163
				   (&extraMem.front() <= bankPtr[i]) &&
164
			           (bankPtr[i] <= &extraMem.back())) {
165
				offsets[i] = size_t(bankPtr[i] - extraMem.data() + romSize + sramSize);
166
			} else {
167
				UNREACHABLE;
168
			}
169
		}
170
		ar.serialize("banks", offsets);
171
	}
172
173
	// Commented out because versioning doesn't work correct on subclasses
174
	// that don't override the serialize() method (e.g. RomPlain)
175
	/*if (ar.versionAtLeast(version, 2)) {
176
		ar.serialize("blockNr", blockNr);
177
	} else {
178
		assert(Archive::IS_LOADER);
179
		// set dummy value, anyway only used for debuggable
180
		std::ranges::fill(blockNr, 255);
181
	}*/
182
}
183
184
template class RomBlocks<0x1000>;
185
template class RomBlocks<0x2000>;
186
template class RomBlocks<0x4000>;
187
INSTANTIATE_SERIALIZE_METHODS(Rom4kBBlocks);
188
INSTANTIATE_SERIALIZE_METHODS(Rom8kBBlocks);
189
INSTANTIATE_SERIALIZE_METHODS(Rom16kBBlocks);
190
191
} // namespace openmsx