openMSX
RomBlocks.cc
Go to the documentation of this file.
1 #include "RomBlocks.hh"
2 #include "SRAM.hh"
3 #include "MSXException.hh"
4 #include "Math.hh"
5 #include "StringOp.hh"
6 #include "serialize.hh"
7 #include "unreachable.hh"
8 
9 namespace openmsx {
10 
11 template<bool C, class T, class F> struct if_log2_ : F {};
12 template< class T, class F> struct if_log2_<true, T, F> : T {};
13 template<unsigned A, unsigned R = 0> struct log2
14  : if_log2_<A == 1, std::integral_constant<int, R>, log2<A / 2, R + 1>> {};
15 
16 template <unsigned BANK_SIZE>
18  const DeviceConfig& config, Rom&& rom_,
19  unsigned debugBankSizeShift)
20  : MSXRom(config, std::move(rom_))
21  , romBlockDebug(
22  *this, blockNr, 0x0000, 0x10000,
23  log2<BANK_SIZE>::value, debugBankSizeShift)
24 {
25  static_assert(Math::isPowerOfTwo(BANK_SIZE), "BANK_SIZE must be a power of two");
26  auto extendedSize = (rom.getSize() + BANK_SIZE - 1) & ~(BANK_SIZE - 1);
27  rom.addPadding(extendedSize);
28  nrBlocks = rom.getSize() / BANK_SIZE;
29  assert((nrBlocks * BANK_SIZE) == rom.getSize());
30 
31  // by default no extra mappable memory block
32  extraMem = nullptr;
33  extraSize = 0;
34 
35  // Default mask: wraps at end of ROM image.
36  blockMask = nrBlocks - 1;
37  for (unsigned i = 0; i < NUM_BANKS; i++) {
38  setRom(i, 0);
39  }
40 }
41 
42 template <unsigned BANK_SIZE>
44 
45 template <unsigned BANK_SIZE>
47 {
48  return bankPtr[address / BANK_SIZE][address & BANK_MASK];
49 }
50 
51 template <unsigned BANK_SIZE>
53 {
54  return &bankPtr[address / BANK_SIZE][address & BANK_MASK];
55 }
56 
57 template <unsigned BANK_SIZE>
58 void RomBlocks<BANK_SIZE>::setBank(byte region, const byte* adr, int block)
59 {
60  assert("address passed to setBank() is not serializable" &&
61  ((adr == unmappedRead) ||
62  ((&rom[0] <= adr) && (adr <= &rom[rom.getSize() - 1])) ||
63  (sram && (&(*sram)[0] <= adr) &&
64  (adr <= &(*sram)[sram->getSize() - 1])) ||
65  ((extraMem <= adr) && (adr <= &extraMem[extraSize - 1]))));
66  bankPtr[region] = adr;
67  blockNr[region] = block; // only for debuggable
68  invalidateMemCache(region * BANK_SIZE, BANK_SIZE);
69 }
70 
71 template <unsigned BANK_SIZE>
73 {
74  setBank(region, unmappedRead, 255);
75 }
76 
77 template <unsigned BANK_SIZE>
79 {
80  extraMem = mem;
81  extraSize = size;
82 }
83 
84 template <unsigned BANK_SIZE>
85 void RomBlocks<BANK_SIZE>::setRom(byte region, int block)
86 {
87  // Note: Some cartridges have a number of blocks that is not a power of 2,
88  // for those we have to make an exception for "block < nrBlocks".
89  block = (block < nrBlocks) ? block : block & blockMask;
90  if (block < nrBlocks) {
91  setBank(region, &rom[block * BANK_SIZE], block);
92  } else {
93  setBank(region, unmappedRead, 255);
94  }
95 }
96 
97 // version 1: initial version
98 // version 2: added blockNr
99 template <unsigned BANK_SIZE>
100 template<typename Archive>
101 void RomBlocks<BANK_SIZE>::serialize(Archive& ar, unsigned /*version*/)
102 {
103  // skip MSXRom base class
104  ar.template serializeBase<MSXDevice>(*this);
105 
106  if (sram) ar.serialize("sram", *sram);
107 
108  unsigned offsets[NUM_BANKS];
109  unsigned romSize = rom.getSize();
110  unsigned sramSize = sram ? sram->getSize() : 0;
111  if (ar.isLoader()) {
112  ar.serialize("banks", offsets);
113  for (unsigned i = 0; i < NUM_BANKS; ++i) {
114  if (offsets[i] == unsigned(-1)) {
115  bankPtr[i] = unmappedRead;
116  } else if (offsets[i] < romSize) {
117  bankPtr[i] = &rom[offsets[i]];
118  } else if (offsets[i] < (romSize + sramSize)) {
119  assert(sram);
120  bankPtr[i] = &(*sram)[offsets[i] - romSize];
121  } else if (offsets[i] < (romSize + sramSize + extraSize)) {
122  bankPtr[i] = &extraMem[offsets[i] - romSize - sramSize];
123  } else {
124  // TODO throw
125  UNREACHABLE;
126  }
127  }
128  } else {
129  for (unsigned i = 0; i < NUM_BANKS; ++i) {
130  if (bankPtr[i] == unmappedRead) {
131  offsets[i] = unsigned(-1);
132  } else if ((&rom[0] <= bankPtr[i]) &&
133  (bankPtr[i] <= &rom[romSize - 1])) {
134  offsets[i] = unsigned(bankPtr[i] - &rom[0]);
135  } else if (sram && (&(*sram)[0] <= bankPtr[i]) &&
136  (bankPtr[i] <= &(*sram)[sramSize - 1])) {
137  offsets[i] = unsigned(bankPtr[i] - &(*sram)[0] + romSize);
138  } else if ((extraMem <= bankPtr[i]) &&
139  (bankPtr[i] <= &extraMem[extraSize - 1])) {
140  offsets[i] = unsigned(bankPtr[i] - extraMem + romSize + sramSize);
141  } else {
142  UNREACHABLE;
143  }
144  }
145  ar.serialize("banks", offsets);
146  }
147 
148  // Commented out because versioning doesn't work correct on subclasses
149  // that don't override the serialize() method (e.g. RomPlain)
150  /*if (ar.versionAtLeast(version, 2)) {
151  ar.serialize("blockNr", blockNr);
152  } else {
153  assert(ar.isLoader());
154  // set dummy value, anyway only used for debuggable
155  for (unsigned i = 0; i < NUM_BANKS; ++i) {
156  blockNr[i] = 255;
157  }
158  }*/
159 }
160 
161 template class RomBlocks<0x1000>;
162 template class RomBlocks<0x2000>;
163 template class RomBlocks<0x4000>;
167 
168 } // namespace openmsx
void setRom(byte region, int block)
Selects a block of the ROM image for reading in a certain region.
Definition: RomBlocks.cc:85
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
STL namespace.
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
Definition: RomBlocks.cc:52
void setBank(byte region, const byte *adr, int block)
Sets the memory visible for reading in a certain region.
Definition: RomBlocks.cc:58
static const unsigned NUM_BANKS
Definition: RomBlocks.hh:17
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: RomBlocks.cc:46
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
const byte * bankPtr[NUM_BANKS]
Definition: RomBlocks.hh:80
byte blockNr[NUM_BANKS]
Definition: RomBlocks.hh:82
size_t size() const
unsigned getSize() const
Definition: Rom.hh:32
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void addPadding(unsigned newSize, byte filler=0xff)
Definition: Rom.cc:366
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:268
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
static const unsigned BANK_SIZE
Definition: RomBlocks.hh:16
void setUnmapped(byte region)
Select &#39;unmapped&#39; memory for this region.
Definition: RomBlocks.cc:72
void serialize(Archive &ar, unsigned version)
Definition: RomBlocks.cc:101
std::unique_ptr< SRAM > sram
Definition: RomBlocks.hh:81
static const unsigned BANK_MASK
Definition: RomBlocks.hh:18
void setExtraMemory(const byte *mem, unsigned size)
Inform this base class of extra mapable memory block.
Definition: RomBlocks.cc:78
RomBlocks(const DeviceConfig &config, Rom &&rom, unsigned debugBankSizeShift=0)
Constructor.
Definition: RomBlocks.cc:17
constexpr bool isPowerOfTwo(unsigned a)
Is the given number an integer power of 2? Not correct for zero (according to this test 0 is a power ...
Definition: Math.hh:20
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:452
#define UNREACHABLE
Definition: unreachable.hh:35