openMSX
MSXDevice.cc
Go to the documentation of this file.
1 #include "MSXDevice.hh"
2 #include "XMLElement.hh"
3 #include "MSXMotherBoard.hh"
4 #include "HardwareConfig.hh"
6 #include "MSXCPUInterface.hh"
7 #include "MSXCPU.hh"
8 #include "CacheLine.hh"
9 #include "TclObject.hh"
10 #include "StringOp.hh"
11 #include "MSXException.hh"
12 #include "serialize.hh"
13 #include "unreachable.hh"
14 #include <algorithm>
15 #include <cassert>
16 #include <cstring>
17 #include <iterator> // for back_inserter
18 
19 using std::string;
20 using std::vector;
21 
22 namespace openmsx {
23 
26 
27 
28 MSXDevice::MSXDevice(const DeviceConfig& config, const string& name)
29  : deviceConfig(config)
30 {
31  initName(name);
32 }
33 
35  : deviceConfig(config)
36 {
37  initName(getDeviceConfig().getAttribute("id"));
38 }
39 
40 void MSXDevice::initName(const string& name)
41 {
42  deviceName = name;
43  if (getMotherBoard().findDevice(deviceName)) {
44  unsigned n = 0;
45  do {
46  deviceName = StringOp::Builder() << name << " (" << ++n << ')';
47  } while (getMotherBoard().findDevice(deviceName));
48  }
49 }
50 
52 {
53  staticInit();
54 
55  lockDevices();
56  registerSlots();
57  registerPorts();
58 }
59 
61 {
62  unregisterPorts();
63  unregisterSlots();
64  unlockDevices();
65  assert(referencedBy.empty());
66 }
67 
68 void MSXDevice::staticInit()
69 {
70  static bool alreadyInit = false;
71  if (alreadyInit) return;
72  alreadyInit = true;
73 
74  memset(unmappedRead, 0xFF, sizeof(unmappedRead));
75 }
76 
78 {
79  return deviceConfig.getHardwareConfig();
80 }
81 
83 {
85 }
86 
88 {
89  return *deviceConfig.getXML();
90 }
91 
93 {
94  return deviceConfig;
95 }
96 
97 void MSXDevice::testRemove(Devices removed) const
98 {
99  auto all = referencedBy;
100  sort(all.begin(), all.end());
101  sort(removed.begin(), removed.end());
102  Devices rest;
103  set_difference(all.begin(), all.end(), removed.begin(), removed.end(),
104  back_inserter(rest));
105  if (!rest.empty()) {
106  StringOp::Builder msg;
107  msg << "Still in use by ";
108  for (auto& d : rest) {
109  msg << d->getName() << ' ';
110  }
111  throw MSXException(msg);
112  }
113 }
114 
115 void MSXDevice::lockDevices()
116 {
117  // This code can only handle backward references: the thing that is
118  // referenced must already be instantiated, we don't try to change the
119  // instantiation order. For the moment this is good enough (only ADVRAM
120  // (an extension) uses it to refer to the VDP (inside a machine)). If
121  // needed we can implement something more sophisticated later without
122  // changing the format of the config files.
123  for (auto& c : getDeviceConfig().getChildren("device")) {
124  const auto& name = c->getAttribute("idref");
125  auto* dev = getMotherBoard().findDevice(name);
126  if (!dev) {
127  throw MSXException(
128  "Unsatisfied dependency: '" + getName() +
129  "' depends on unavailable device '" +
130  name + "'.");
131  }
132  references.push_back(dev);
133  dev->referencedBy.push_back(this);
134  }
135 }
136 
137 void MSXDevice::unlockDevices()
138 {
139  for (auto& r : references) {
140  auto it = find(r->referencedBy.begin(),
141  r->referencedBy.end(),
142  this);
143  assert(it != r->referencedBy.end());
144  r->referencedBy.erase(it);
145  }
146 }
147 
149 {
150  // init() must already be called
151  return references;
152 }
153 
155 {
156  return getMotherBoard().getCurrentTime();
157 }
159 {
160  return getMotherBoard().getCPU();
161 }
163 {
164  return getMotherBoard().getCPUInterface();
165 }
167 {
168  return getMotherBoard().getScheduler();
169 }
171 {
172  return getMotherBoard().getMSXCliComm();
173 }
175 {
176  return getMotherBoard().getReactor();
177 }
179 {
181 }
183 {
184  return getMotherBoard().getLedStatus();
185 }
187 {
189 }
190 
191 void MSXDevice::registerSlots()
192 {
193  MemRegions tmpMemRegions;
194  for (auto& m : getDeviceConfig().getChildren("mem")) {
195  unsigned base = m->getAttributeAsInt("base");
196  unsigned size = m->getAttributeAsInt("size");
197  if ((base >= 0x10000) || (size > 0x10000)) {
198  throw MSXException(
199  "Invalid memory specification for device " +
200  getName() + " should be in range "
201  "[0x0000,0x10000).");
202  }
203  tmpMemRegions.emplace_back(base, size);
204  }
205  if (tmpMemRegions.empty()) {
206  return;
207  }
208 
209  // find primary and secondary slot specification
210  auto& slotManager = getMotherBoard().getSlotManager();
211  auto* primaryConfig = getDeviceConfig2().getPrimary();
212  auto* secondaryConfig = getDeviceConfig2().getSecondary();
213  if (primaryConfig) {
214  ps = slotManager.getSlotNum(primaryConfig->getAttribute("slot"));
215  } else {
216  throw MSXException("Invalid memory specification");
217  }
218  if (secondaryConfig) {
219  ss = slotManager.getSlotNum(secondaryConfig->getAttribute("slot"));
220  } else {
221  ss = 0;
222  }
223 
224  // This is only for backwards compatibility: in the past we added extra
225  // attributes "primary_slot" and "secondary_slot" (in each MSXDevice
226  // config) instead of changing the 'any' value of the slot attribute of
227  // the (possibly shared) <primary> and <secondary> tags. When loading
228  // an old savestate these tags can still occur, so keep this code. Also
229  // remove these attributes to convert to the new format.
230  const auto& config = getDeviceConfig();
231  if (config.hasAttribute("primary_slot")) {
232  auto& mutableConfig = const_cast<XMLElement&>(config);
233  const auto& primSlot = config.getAttribute("primary_slot");
234  ps = slotManager.getSlotNum(primSlot);
235  mutableConfig.removeAttribute("primary_slot");
236  if (config.hasAttribute("secondary_slot")) {
237  const auto& secondSlot = config.getAttribute("secondary_slot");
238  ss = slotManager.getSlotNum(secondSlot);
239  mutableConfig.removeAttribute("secondary_slot");
240  }
241  }
242 
243  // decode special values for 'ss'
244  if ((-128 <= ss) && (ss < 0)) {
245  if ((0 <= ps) && (ps < 4) &&
246  getCPUInterface().isExpanded(ps)) {
247  ss += 128;
248  } else {
249  ss = 0;
250  }
251  }
252 
253  // decode special values for 'ps'
254  if (ps == -256) {
255  slotManager.getAnyFreeSlot(ps, ss);
256  } else if (ps < 0) {
257  // specified slot by name (carta, cartb, ...)
258  slotManager.getSpecificSlot(-ps - 1, ps, ss);
259  } else {
260  // numerical specified slot (0, 1, 2, 3)
261  }
262  assert((0 <= ps) && (ps <= 3));
263 
264  if (!getCPUInterface().isExpanded(ps)) {
265  ss = -1;
266  }
267 
268  // Store actual slot in config. This has two purposes:
269  // - Make sure that devices that are grouped under the same
270  // <primary>/<secondary> tags actually use the same slot. (This
271  // matters when the value of some of the slot attributes is "any").
272  // - Fix the slot number so that it remains the same after a
273  // savestate/loadstate.
274  assert(primaryConfig);
275  primaryConfig->setAttribute("slot", StringOp::toString(ps));
276  if (secondaryConfig) {
277  string slot = (ss == -1) ? "X" : StringOp::toString(ss);
278  secondaryConfig->setAttribute("slot", slot);
279  } else {
280  if (ss != -1) {
281  throw MSXException(
282  "Missing <secondary> tag for device" +
283  getName());
284  }
285  }
286 
287  int logicalSS = (ss == -1) ? 0 : ss;
288  for (auto& r : tmpMemRegions) {
290  *this, ps, logicalSS, r.first, r.second);
291  memRegions.push_back(r);
292  }
293 
294  // Mark the slot as 'in-use' so that future searches for free external
295  // slots don't return this slot anymore. If the slot was not an
296  // external slot, this call has no effect. Multiple MSXDevices from the
297  // same extension (the same HardwareConfig) can all allocate the same
298  // slot (later they should also all free this slot).
299  slotManager.allocateSlot(ps, ss, getHardwareConfig());
300 }
301 
302 void MSXDevice::unregisterSlots()
303 {
304  if (memRegions.empty()) return;
305 
306  int logicalSS = (ss == -1) ? 0 : ss;
307  for (auto& r : memRegions) {
309  *this, ps, logicalSS, r.first, r.second);
310  }
311 
312  // See comments above about allocateSlot() for more details:
313  // - has no effect for non-external slots
314  // - can be called multiple times for the same slot
316 }
317 
318 void MSXDevice::getVisibleMemRegion(unsigned& base, unsigned& size) const
319 {
320  // init() must already be called
321  if (memRegions.empty()) {
322  base = 0;
323  size = 0;
324  return;
325  }
326  auto it = memRegions.begin();
327  unsigned lowest = it->first;
328  unsigned highest = it->first + it->second;
329  for (++it; it != memRegions.end(); ++it) {
330  lowest = std::min(lowest, it->first);
331  highest = std::max(highest, it->first + it->second);
332  }
333  assert(lowest <= highest);
334  base = lowest;
335  size = highest - lowest;
336 }
337 
338 void MSXDevice::registerPorts()
339 {
340  for (auto& i : getDeviceConfig().getChildren("io")) {
341  unsigned base = StringOp::stringToInt(i->getAttribute("base"));
342  unsigned num = StringOp::stringToInt(i->getAttribute("num"));
343  const auto& type = i->getAttribute("type", "IO");
344  if (((base + num) > 256) || (num == 0) ||
345  ((type != "I") && (type != "O") && (type != "IO"))) {
346  throw MSXException("Invalid IO port specification");
347  }
348  for (unsigned port = base; port < base + num; ++port) {
349  if ((type == "I") || (type == "IO")) {
350  getCPUInterface().register_IO_In(port, this);
351  inPorts.push_back(port);
352  }
353  if ((type == "O") || (type == "IO")) {
354  getCPUInterface().register_IO_Out(port, this);
355  outPorts.push_back(port);
356  }
357  }
358  }
359 }
360 
361 void MSXDevice::unregisterPorts()
362 {
363  for (auto& p : inPorts) {
365  }
366  for (auto& p : outPorts) {
368  }
369 }
370 
371 
373 {
374  // nothing
375 }
376 
378 {
379  return 0xFF;
380 }
381 
383 {
384  // nothing
385 }
386 
388 {
389  reset(time);
390 }
391 
392 string MSXDevice::getName() const
393 {
394  return deviceName;
395 }
396 
398 {
399  result.addListElement(getName());
400 }
401 
403 {
405  getExtraDeviceInfo(result);
406 }
407 
409 {
410  // nothing
411 }
412 
413 
415 {
416  (void)port;
417  PRT_DEBUG("MSXDevice::readIO (0x" << std::hex << int(port & 0xFF)
418  << std::dec << ") : No device implementation.");
419  return 0xFF;
420 }
421 
422 void MSXDevice::writeIO(word port, byte value, EmuTime::param /*time*/)
423 {
424  (void)port;
425  (void)value;
426  PRT_DEBUG("MSXDevice::writeIO(port 0x" << std::hex << int(port & 0xFF)
427  << std::dec << ",value " << int(value)
428  << ") : No device implementation.");
429  // do nothing
430 }
431 
432 byte MSXDevice::peekIO(word /*port*/, EmuTime::param /*time*/) const
433 {
434  return 0xFF;
435 }
436 
437 
439 {
440  (void)address;
441  PRT_DEBUG("MSXDevice: read from unmapped memory " << std::hex <<
442  int(address) << std::dec);
443  return 0xFF;
444 }
445 
446 const byte* MSXDevice::getReadCacheLine(word /*start*/) const
447 {
448  return nullptr; // uncacheable
449 }
450 
451 void MSXDevice::writeMem(word address, byte /*value*/,
452  EmuTime::param /*time*/)
453 {
454  (void)address;
455  PRT_DEBUG("MSXDevice: write to unmapped memory " << std::hex <<
456  int(address) << std::dec);
457  // do nothing
458 }
459 
460 byte MSXDevice::peekMem(word address, EmuTime::param /*time*/) const
461 {
462  word base = address & CacheLine::HIGH;
463  if (const byte* cache = getReadCacheLine(base)) {
464  word offset = address & CacheLine::LOW;
465  return cache[offset];
466  } else {
467  PRT_DEBUG("MSXDevice: peek not supported for this device");
468  return 0xFF;
469  }
470 }
471 
472 void MSXDevice::globalWrite(word /*address*/, byte /*value*/,
473  EmuTime::param /*time*/)
474 {
475  UNREACHABLE;
476 }
477 
479 {
480  return nullptr; // uncacheable
481 }
482 
483 void MSXDevice::invalidateMemCache(word start, unsigned size)
484 {
485  getCPU().invalidateMemCache(start, size);
486 }
487 
488 template<typename Archive>
489 void MSXDevice::serialize(Archive& ar, unsigned /*version*/)
490 {
491  // When this method is called, the method init() has already been
492  // called (thus also registerSlots() and registerPorts()).
493  ar.serialize("name", deviceName);
494 }
496 
497 } // namespace openmsx
virtual void init()
Definition: MSXDevice.cc:51
signed char offset
Definition: CPUCore.cc:252
void freeSlot(int ps, int ss, const HardwareConfig &hwConfig)
Contains the main loop of openMSX.
Definition: Reactor.hh:61
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:162
void registerMemDevice(MSXDevice &device, int primSl, int secSL, int base, int size)
Devices can register themself in the MSX slotstructure.
const XMLElement & getDeviceConfig() const
Get the configuration section for this device.
Definition: MSXDevice.cc:87
PluggingController & getPluggingController()
MSXCPUInterface & getCPUInterface()
string toString(long long a)
Definition: StringOp.cc:155
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:154
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:438
virtual void writeMem(word address, byte value, EmuTime::param time)
Write a given byte to a given location at a certain time to this device.
Definition: MSXDevice.cc:451
XMLElement * getPrimary() const
Definition: DeviceConfig.hh:58
virtual void globalWrite(word address, byte value, EmuTime::param time)
Global writes.
Definition: MSXDevice.cc:472
PluggingController & getPluggingController() const
Definition: MSXDevice.cc:186
virtual byte readIRQVector()
Gets IRQ vector used in IM2.
Definition: MSXDevice.cc:377
virtual byte readIO(word port, EmuTime::param time)
Read a byte from an IO port at a certain time from this device.
Definition: MSXDevice.cc:414
Central administration of Connectors and Pluggables.
void unregister_IO_In(byte port, MSXDevice *device)
void unregisterMemDevice(MSXDevice &device, int primSl, int secSL, int base, int size)
void getDeviceInfo(TclObject &result) const
Get device info.
Definition: MSXDevice.cc:402
LedStatus & getLedStatus() const
Definition: MSXDevice.cc:182
Reactor & getReactor() const
Definition: MSXDevice.cc:174
CommandController & getCommandController() const
Definition: MSXDevice.cc:178
void invalidateMemCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:196
void serialize(Archive &ar, unsigned version)
Definition: MSXDevice.cc:489
virtual void powerUp(EmuTime::param time)
This method is called when MSX is powered up.
Definition: MSXDevice.cc:387
MSXDevice * findDevice(string_ref name)
Find a MSXDevice by name.
virtual byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
Definition: MSXDevice.cc:478
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:82
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.cc:77
virtual std::string getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:392
virtual const byte * getReadCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
Definition: MSXDevice.cc:446
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
CliComm & getCliComm() const
Definition: MSXDevice.cc:170
MSXDevice(const DeviceConfig &config, const std::string &name)
Every MSXDevice has a config entry; this constructor gets some device properties from that config ent...
Definition: MSXDevice.cc:28
const XMLElement * getXML() const
Definition: DeviceConfig.hh:54
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition: MSXDevice.cc:148
void testRemove(Devices alreadyRemoved) const
Checks whether this device can be removed (no other device has a reference to it).
Definition: MSXDevice.cc:97
virtual void powerDown(EmuTime::param time)
This method is called when MSX is powered down.
Definition: MSXDevice.cc:382
void getVisibleMemRegion(unsigned &base, unsigned &size) const
Returns the range where this device is visible in memory.
Definition: MSXDevice.cc:318
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:32
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:38
virtual void writeIO(word port, byte value, EmuTime::param time)
Write a byte to a given IO port at a certain time to this device.
Definition: MSXDevice.cc:422
CommandController & getCommandController()
void unregister_IO_Out(byte port, MSXDevice *device)
virtual void getExtraDeviceInfo(TclObject &result) const
Definition: MSXDevice.cc:408
MSXCPU & getCPU() const
Definition: MSXDevice.cc:158
CartridgeSlotManager & getSlotManager()
std::vector< MSXDevice * > Devices
Definition: MSXDevice.hh:35
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:260
void addListElement(string_ref element)
Definition: TclObject.cc:154
const DeviceConfig & getDeviceConfig2() const
Definition: MSXDevice.cc:92
int stringToInt(const string &str)
Definition: StringOp.cc:186
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:261
virtual byte peekMem(word address, EmuTime::param time) const
Read a byte from a given memory location.
Definition: MSXDevice.cc:460
size_t size() const
virtual byte peekIO(word port, EmuTime::param time) const
Read a byte from a given IO port.
Definition: MSXDevice.cc:432
virtual void reset(EmuTime::param time)
This method is called on reset.
Definition: MSXDevice.cc:372
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
const HardwareConfig & getHardwareConfig() const
Definition: DeviceConfig.hh:49
Scheduler & getScheduler() const
Definition: MSXDevice.cc:166
XMLElement * getSecondary() const
Definition: DeviceConfig.hh:62
virtual void getNameList(TclObject &result) const
Returns list of name(s) of this device.
Definition: MSXDevice.cc:397
void invalidateMemCache(word start, unsigned size)
Invalidate CPU memory-mapping cache.
Definition: MSXDevice.cc:483
virtual ~MSXDevice()=0
Definition: MSXDevice.cc:60
#define PRT_DEBUG(mes)
Definition: openmsx.hh:69
#define UNREACHABLE
Definition: unreachable.hh:56
MSXMotherBoard & getMotherBoard() const