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