openMSX
RomBlocks.cc
Go to the documentation of this file.
1 #include "RomBlocks.hh"
2 #include "RomBlockDebuggable.hh"
3 #include "Rom.hh"
4 #include "SRAM.hh"
5 #include "SimpleDebuggable.hh"
6 #include "MSXException.hh"
7 #include "StringOp.hh"
8 #include "serialize.hh"
9 #include "unreachable.hh"
10 #include "memory.hh"
11 
12 namespace openmsx {
13 
14 template<bool C, class T, class F> struct if_log2_ : F {};
15 template< class T, class F> struct if_log2_<true, T, F> : T {};
16 template<unsigned A, unsigned R = 0> struct log2
17  : if_log2_<A == 1, std::integral_constant<int, R>, log2<A / 2, R + 1>> {};
18 
19 template <unsigned BANK_SIZE>
21  const DeviceConfig& config, std::unique_ptr<Rom> rom_,
22  unsigned debugBankSizeShift)
23  : MSXRom(config, std::move(rom_))
24  , romBlockDebug(make_unique<RomBlockDebuggable>(
25  *this, blockNr, 0x0000, 0x10000,
26  log2<BANK_SIZE>::value, debugBankSizeShift))
27  , nrBlocks(rom->getSize() / BANK_SIZE)
28 {
29  if ((nrBlocks * BANK_SIZE) != rom->getSize()) {
31  "(uncompressed) ROM image filesize must be a multiple of " <<
32  BANK_SIZE / 1024 << " kB (for this mapper type).");
33  }
34  // by default no extra mappable memory block
35  extraMem = nullptr;
36  extraSize = 0;
37 
38  // Default mask: wraps at end of ROM image.
39  blockMask = nrBlocks - 1;
40  for (unsigned i = 0; i < NUM_BANKS; i++) {
41  setRom(i, 0);
42  }
43 }
44 
45 template <unsigned BANK_SIZE>
47 {
48 }
49 
50 template <unsigned BANK_SIZE>
52 {
53  return bank[address / BANK_SIZE][address & BANK_MASK];
54 }
55 
56 template <unsigned BANK_SIZE>
58 {
59  return &bank[address / BANK_SIZE][address & BANK_MASK];
60 }
61 
62 template <unsigned BANK_SIZE>
63 void RomBlocks<BANK_SIZE>::setBank(byte region, const byte* adr, int block)
64 {
65  assert("address passed to setBank() is not serializable" &&
66  ((adr == unmappedRead) ||
67  ((&(*rom)[0] <= adr) && (adr <= &(*rom)[rom->getSize() - 1])) ||
68  (sram.get() && (&(*sram)[0] <= adr) &&
69  (adr <= &(*sram)[sram->getSize() - 1])) ||
70  ((extraMem <= adr) && (adr <= &extraMem[extraSize - 1]))));
71  bank[region] = adr;
72  blockNr[region] = block; // only for debuggable
73  invalidateMemCache(region * BANK_SIZE, BANK_SIZE);
74 }
75 
76 template <unsigned BANK_SIZE>
78 {
79  setBank(region, unmappedRead, 255);
80 }
81 
82 template <unsigned BANK_SIZE>
84 {
85  blockMask = mask;
86 }
87 
88 template <unsigned BANK_SIZE>
90 {
91  extraMem = mem;
92  extraSize = size;
93 }
94 
95 template <unsigned BANK_SIZE>
96 void RomBlocks<BANK_SIZE>::setRom(byte region, int block)
97 {
98  // Note: Some cartridges have a number of blocks that is not a power of 2,
99  // for those we have to make an exception for "block < nrBlocks".
100  block = (block < nrBlocks) ? block : block & blockMask;
101  if (block < nrBlocks) {
102  setBank(region, &(*rom)[block * BANK_SIZE], block);
103  } else {
104  setBank(region, unmappedRead, 255);
105  }
106 }
107 
108 // version 1: initial version
109 // version 2: added blockNr
110 template <unsigned BANK_SIZE>
111 template<typename Archive>
112 void RomBlocks<BANK_SIZE>::serialize(Archive& ar, unsigned /*version*/)
113 {
114  // skip MSXRom base class
115  ar.template serializeBase<MSXDevice>(*this);
116 
117  if (sram.get()) {
118  ar.serialize("sram", *sram);
119  }
120 
121  unsigned offsets[NUM_BANKS];
122  unsigned romSize = rom->getSize();
123  unsigned sramSize = sram.get() ? sram->getSize() : 0;
124  if (ar.isLoader()) {
125  ar.serialize("banks", offsets);
126  for (unsigned i = 0; i < NUM_BANKS; ++i) {
127  if (offsets[i] == unsigned(-1)) {
128  bank[i] = unmappedRead;
129  } else if (offsets[i] < romSize) {
130  bank[i] = &(*rom)[offsets[i]];
131  } else if (offsets[i] < (romSize + sramSize)) {
132  assert(sram.get());
133  bank[i] = &(*sram)[offsets[i] - romSize];
134  } else if (offsets[i] < (romSize + sramSize + extraSize)) {
135  bank[i] = &extraMem[offsets[i] - romSize - sramSize];
136  } else {
137  // TODO throw
138  UNREACHABLE;
139  }
140  }
141  } else {
142  for (unsigned i = 0; i < NUM_BANKS; ++i) {
143  if (bank[i] == unmappedRead) {
144  offsets[i] = unsigned(-1);
145  } else if ((&(*rom)[0] <= bank[i]) &&
146  (bank[i] <= &(*rom)[romSize - 1])) {
147  offsets[i] = unsigned(bank[i] - &(*rom)[0]);
148  } else if (sram.get() &&
149  (&(*sram)[0] <= bank[i]) &&
150  (bank[i] <= &(*sram)[sramSize - 1])) {
151  offsets[i] = unsigned(bank[i] - &(*sram)[0] + romSize);
152  } else if ((extraMem <= bank[i]) &&
153  (bank[i] <= &extraMem[extraSize - 1])) {
154  offsets[i] = unsigned(bank[i] - extraMem + romSize + sramSize);
155  } else {
156  UNREACHABLE;
157  }
158  }
159  ar.serialize("banks", offsets);
160  }
161 
162  // Commented out because versioning doesn't work correct on subclasses
163  // that don't override the serialize() method (e.g. RomPlain)
164  /*if (ar.versionAtLeast(version, 2)) {
165  ar.serialize("blockNr", blockNr);
166  } else {
167  assert(ar.isLoader());
168  // set dummy value, anyway only used for debuggable
169  for (unsigned i = 0; i < NUM_BANKS; ++i) {
170  blockNr[i] = 255;
171  }
172  }*/
173 }
174 
175 template class RomBlocks<0x1000>;
176 template class RomBlocks<0x2000>;
177 template class RomBlocks<0x4000>;
181 
182 } // namespace openmsx