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