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