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