openMSX
RomPlain.cc
Go to the documentation of this file.
1#include "RomPlain.hh"
2
3#include "MSXException.hh"
4#include "XMLElement.hh"
5#include "serialize.hh"
6
7#include "one_of.hh"
8#include "unreachable.hh"
9#include "xrange.hh"
10
11#include <array>
12
13namespace openmsx {
14
15// return x inside [start, start+len)
16[[nodiscard]] static constexpr bool isInside(unsigned x, unsigned start, unsigned len)
17{
18 unsigned tmp = x - start;
19 return tmp < len;
20}
21
22[[nodiscard]] static std::string toString(unsigned start, unsigned len)
23{
24 return strCat("[0x", hex_string<4>(start), ", "
25 "0x", hex_string<4>(start + len), ')');
26}
27
28RomPlain::RomPlain(const DeviceConfig& config, Rom&& rom_, RomType type)
29 : Rom8kBBlocks(config, std::move(rom_))
30{
31 unsigned windowBase = 0x0000;
32 unsigned windowSize = 0x10000;
33 if (const auto* mem = config.findChild("mem")) {
34 windowBase = mem->getAttributeValueAsInt("base", 0);
35 windowSize = mem->getAttributeValueAsInt("size", 0);
36 }
37
38 auto romSize_ = rom.size();
39 if ((romSize_ > 0x10000) || (romSize_ & 0x1FFF)) {
40 throw MSXException(rom.getName(),
41 ": invalid rom size: must be smaller than or equal to 64kB "
42 "and must be a multiple of 8kB.");
43 }
44 auto romSize = narrow<unsigned>(romSize_);
45
46 using enum RomType;
47 const int start = [&] {
48 switch (type) {
49 case MIRRORED: return -1;
50 case NORMAL: return -1;
51 case MIRRORED0000: return 0x0000;
52 case MIRRORED4000: return 0x4000;
53 case MIRRORED8000: return 0x8000;
54 case MIRROREDC000: return 0xC000;
55 case NORMAL0000: return 0x0000;
56 case NORMAL4000: return 0x4000;
57 case NORMAL8000: return 0x8000;
58 case NORMALC000: return 0xC000;
59 default: UNREACHABLE;
60 }
61 }();
62 const bool mirrored = type == one_of(MIRRORED,
65
66 unsigned romBase = (start == -1)
67 ? guessLocation(windowBase, windowSize)
68 : unsigned(start);
69 if ((start == -1) &&
70 (!isInside(romBase, windowBase, windowSize) ||
71 !isInside(romBase + romSize - 1, windowBase, windowSize))) {
72 // ROM must fall inside the boundaries given by the <mem>
73 // tag (this code only looks at one <mem> tag), but only
74 // check when the start address was not explicitly specified
75 throw MSXException(rom.getName(),
76 ": invalid rom position: interval ",
77 toString(romBase, romSize), " must fit in ",
78 toString(windowBase, windowSize), '.');
79 }
80 if (romBase & 0x1FFF) {
81 throw MSXException(rom.getName(),
82 ": invalid rom position: must start at a 8kB boundary.");
83 }
84
85 unsigned firstPage = romBase / 0x2000;
86 unsigned numPages = romSize / 0x2000;
87 for (auto page : xrange(8)) {
88 unsigned romPage = page - firstPage;
89 if (romPage < numPages) {
90 setRom(page, romPage);
91 } else {
92 if (mirrored) {
93 setRom(page, romPage & (numPages - 1));
94 } else {
95 setUnmapped(page);
96 }
97 }
98 }
99 // RomPlain is currently implemented as a subclass of Rom8kBBlocks,
100 // because of that it inherits a 8kB alignment requirement on the
101 // base/size attributes in the <mem> tag in the hardware description.
102 // But for example for the 'Boosted_audio' extension this requirement
103 // is too strict. Except for the calls to setRom() and setUnmapped() in
104 // this constructor, RomPlain doesn't dynamically change the memory
105 // layout. So if we undo the cache-pre-filling stuff done by setRom()
106 // it is OK to relax the alignment requirements again.
107 //
108 // An alternative is to not inherit from Rom8kBBlocks but from MSXRom.
109 // That would simplify the alignment stuff, but OTOH then we have to
110 // reimplement the debuggable stuff. Also the format of savestates will
111 // change (so it would require extra backwards compatibility code).
112 // Therefor, at least for now, I've not chosen this alternative.
114}
115
116void RomPlain::guessHelper(unsigned offset, std::span<int, 3> pages) const
117{
118 if ((rom[offset + 0] == 'A') && (rom[offset + 1] == 'B')) {
119 for (auto i : xrange(4)) {
120 if (auto addr = rom[offset + 2 + 2 * i + 0] +
121 rom[offset + 2 + 2 * i + 1] * 256) {
122 unsigned page = (addr >> 14) - (offset >> 14);
123 if (page <= 2) {
124 pages[page]++;
125 }
126 }
127 }
128 }
129}
130
131unsigned RomPlain::guessLocation(unsigned windowBase, unsigned windowSize) const
132{
133 std::array<int, 3> pages = {0, 0, 0};
134
135 // count number of possible routine pointers
136 if (rom.size() >= 0x0010) {
137 guessHelper(0x0000, pages);
138 }
139 if (rom.size() >= 0x4010) {
140 guessHelper(0x4000, pages);
141 }
142
143 // start address must be inside memory window
144 if (!isInside(0x0000, windowBase, windowSize)) pages[0] = 0;
145 if (!isInside(0x4000, windowBase, windowSize)) pages[1] = 0;
146 if (!isInside(0x8000, windowBase, windowSize)) pages[2] = 0;
147
148 // we prefer 0x4000, then 0x000 and then 0x8000
149 if (pages[1] && (pages[1] >= pages[0]) && (pages[1] >= pages[2])) {
150 return 0x4000;
151 } else if (pages[0] && pages[0] >= pages[2]) {
152 return 0x0000;
153 } else if (pages[2]) {
154 return 0x8000;
155 }
156
157 // heuristics didn't work, return start of window
158 return windowBase;
159}
160
162{
163 // Because this mapper has no switchable banks, we can relax the
164 // alignment requirements again. See also the comment at the end of the
165 // constructor.
167}
168
170
171} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
const XMLElement * findChild(std::string_view name) const
void invalidateDeviceRCache()
Definition MSXDevice.hh:215
virtual unsigned getBaseSizeAlignment() const
The 'base' and 'size' attribute values need to be at least aligned to CacheLine::SIZE.
Definition MSXDevice.cc:396
void setUnmapped(unsigned region)
Select 'unmapped' memory for this region.
Definition RomBlocks.cc:92
void setRom(unsigned region, unsigned block)
Selects a block of the ROM image for reading in a certain region.
Definition RomBlocks.cc:104
unsigned getBaseSizeAlignment() const override
The 'base' and 'size' attribute values need to be at least aligned to CacheLine::SIZE.
Definition RomPlain.cc:161
RomPlain(const DeviceConfig &config, Rom &&rom, RomType type)
Definition RomPlain.cc:28
auto size() const
Definition Rom.hh:37
const std::string & getName() const
Definition Rom.hh:42
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::string toString(const BooleanInput &input)
STL namespace.
std::string strCat()
Definition strCat.hh:703
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132