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