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  }
73 }
74 
75 void RomPlain::guessHelper(unsigned offset, int* pages)
76 {
77  if ((rom[offset++] == 'A') && (rom[offset++] =='B')) {
78  for (int i = 0; i < 4; i++) {
79  word addr = rom[offset + 0] +
80  rom[offset + 1] * 256;
81  offset += 2;
82  if (addr) {
83  int page = (addr >> 14) - (offset >> 14);
84  if ((0 <= page) && (page <= 2)) {
85  pages[page]++;
86  }
87  }
88  }
89  }
90 }
91 
92 unsigned RomPlain::guessLocation(unsigned windowBase, unsigned windowSize)
93 {
94  int pages[3] = { 0, 0, 0 };
95 
96  // count number of possible routine pointers
97  if (rom.getSize() >= 0x0010) {
98  guessHelper(0x0000, pages);
99  }
100  if (rom.getSize() >= 0x4010) {
101  guessHelper(0x4000, pages);
102  }
103 
104  // start address must be inside memory window
105  if (!isInside(0x0000, windowBase, windowSize)) pages[0] = 0;
106  if (!isInside(0x4000, windowBase, windowSize)) pages[1] = 0;
107  if (!isInside(0x8000, windowBase, windowSize)) pages[2] = 0;
108 
109  // we prefer 0x4000, then 0x000 and then 0x8000
110  if (pages[1] && (pages[1] >= pages[0]) && (pages[1] >= pages[2])) {
111  return 0x4000;
112  } else if (pages[0] && pages[0] >= pages[2]) {
113  return 0x0000;
114  } else if (pages[2]) {
115  return 0x8000;
116  }
117 
118  // heuristics didn't work, return start of window
119  return windowBase;
120 }
121 
122 REGISTER_MSXDEVICE(RomPlain, "RomPlain");
123 
124 } // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition: MSXDevice.hh:307