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