openMSX
MSXMapperIO.cc
Go to the documentation of this file.
1 #include "MSXMapperIO.hh"
2 #include "MSXMotherBoard.hh"
3 #include "HardwareConfig.hh"
4 #include "XMLElement.hh"
5 #include "MSXException.hh"
6 #include "StringOp.hh"
7 #include "enumerate.hh"
8 #include "outer.hh"
9 #include "serialize.hh"
10 #include "stl.hh"
11 #include <algorithm>
12 
13 namespace openmsx {
14 
15 [[nodiscard]] static byte calcReadBackMask(MSXMotherBoard& motherBoard)
16 {
17  std::string_view type = motherBoard.getMachineConfig()->getConfig().getChildData(
18  "MapperReadBackBits", "largest");
19  if (type == "largest") {
20  return 0xff; // all bits can be read
21  }
22  auto bits = StringOp::stringTo<int>(type);
23  if (!bits) {
24  throw FatalError("Unknown mapper type: \"", type, "\".");
25  }
26  if (*bits < 0 || *bits > 8) {
27  throw FatalError("MapperReadBackBits out of range: \"", type, "\".");
28  }
29  return ~(unsigned(-1) << *bits);
30 }
31 
33  : MSXDevice(config)
34  , debuggable(getMotherBoard(), getName())
35  , mask(calcReadBackMask(getMotherBoard()))
36 {
37  reset(EmuTime::dummy());
38 }
39 
40 void MSXMapperIO::setMode(Mode mode_, byte mask_, byte baseValue_)
41 {
42  mode = mode_;
43  mask = mask_;
44  baseValue = baseValue_;
45 }
46 
48 {
49  mappers.push_back(mapper);
50 }
51 
53 {
54  mappers.erase(rfind_unguarded(mappers, mapper));
55 }
56 
57 byte MSXMapperIO::readIO(word port, EmuTime::param time)
58 {
59  byte value = [&] {
60  if (mode == Mode::EXTERNAL) {
61  byte result = 0xFF;
62  for (auto* mapper : mappers) {
63  result &= mapper->readIO(port, time);
64  }
65  return result;
66  } else {
67  return registers[port & 3];
68  }
69  }();
70  return (value & mask) | (baseValue & ~mask);
71 }
72 
73 byte MSXMapperIO::peekIO(word port, EmuTime::param time) const
74 {
75  byte value = [&] {
76  if (mode == Mode::EXTERNAL) {
77  byte result = 0xFF;
78  for (auto* mapper : mappers) {
79  result &= mapper->peekIO(port, time);
80  }
81  return result;
82  } else {
83  return registers[port & 3];
84  }
85  }();
86  return (value & mask) | (baseValue & ~mask);
87 }
88 
89 void MSXMapperIO::writeIO(word port, byte value, EmuTime::param time)
90 {
91  registers[port & 3] = value;
92 
93  // Note: the mappers are responsible for invalidating/filling the CPU
94  // cache-lines.
95  for (auto* mapper : mappers) {
96  mapper->writeIO(port, value, time);
97  }
98 }
99 
100 
101 // SimpleDebuggable
102 // This debuggable is here for backwards compatibility. For more accurate
103 // results, use the debuggable belonging to a specific mapper.
104 
105 MSXMapperIO::Debuggable::Debuggable(MSXMotherBoard& motherBoard_,
106  const std::string& name_)
107  : SimpleDebuggable(motherBoard_, name_, "Memory mapper registers", 4)
108 {
109 }
110 
111 byte MSXMapperIO::Debuggable::read(unsigned address)
112 {
113  // return the last written value, with full 8-bit precision.
114  auto& mapperIO = OUTER(MSXMapperIO, debuggable);
115  return mapperIO.registers[address & 3];
116 }
117 
118 void MSXMapperIO::Debuggable::write(unsigned address, byte value,
119  EmuTime::param time)
120 {
121  auto& mapperIO = OUTER(MSXMapperIO, debuggable);
122  mapperIO.writeIO(address, value, time);
123 }
124 
125 
126 template<typename Archive>
127 void MSXMapperIO::serialize(Archive& ar, unsigned version)
128 {
129  if (ar.versionBelow(version, 2)) {
130  // In version 1 we stored the mapper state in MSXMapperIO instead of
131  // in the individual mappers, so distribute the state to them.
132  assert(Archive::IS_LOADER);
133  ar.serialize("registers", registers);
134  for (auto [page, reg] : enumerate(registers)) {
135  writeIO(word(page), reg, EmuTime::dummy());
136  }
137  }
138  if (ar.versionAtLeast(version, 3)) {
139  // In version 3 we store the mapper state BOTH here and in the
140  // individual mappers. The reason for this is:
141  // - There are MSX machines with a S1985 but without a memory
142  // mapper. To support those we need to store the state here.
143  // - In rare cases (e.g. musical memory mapper) the mapper state
144  // can be different for specific mappers.
145  ar.serialize("registers", registers);
146  }
147 }
149 
150 } // namespace openmsx
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
virtual void reset(EmuTime::param time)
This method is called on reset.
Definition: MSXDevice.cc:356
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: MSXMapperIO.cc:73
void registerMapper(MSXMemoryMapperInterface *mapper)
Definition: MSXMapperIO.cc:47
MSXMapperIO(const DeviceConfig &config)
Definition: MSXMapperIO.cc:32
void serialize(Archive &ar, unsigned version)
Definition: MSXMapperIO.cc:127
void unregisterMapper(MSXMemoryMapperInterface *mapper)
Definition: MSXMapperIO.cc:52
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition: MSXMapperIO.cc:89
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: MSXMapperIO.cc:57
void setMode(Mode mode, byte mask, byte baseValue)
Definition: MSXMapperIO.cc:40
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition: enumerate.hh:28
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:741
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr nibble mask[4][13]
Definition: RP5C01.cc:34
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:108