openMSX
MSXCPUInterface.cc
Go to the documentation of this file.
1 #include "MSXCPUInterface.hh"
2 #include "DummyDevice.hh"
3 #include "CommandException.hh"
4 #include "TclObject.hh"
5 #include "Interpreter.hh"
6 #include "Reactor.hh"
7 #include "RealTime.hh"
8 #include "MSXMotherBoard.hh"
9 #include "MSXCPU.hh"
10 #include "VDPIODelay.hh"
11 #include "CliComm.hh"
12 #include "MSXMultiIODevice.hh"
13 #include "MSXMultiMemDevice.hh"
14 #include "MSXWatchIODevice.hh"
15 #include "MSXException.hh"
16 #include "CartridgeSlotManager.hh"
17 #include "EventDistributor.hh"
18 #include "Event.hh"
19 #include "HardwareConfig.hh"
20 #include "DeviceFactory.hh"
21 #include "ReadOnlySetting.hh"
22 #include "serialize.hh"
23 #include "checked_cast.hh"
24 #include "outer.hh"
25 #include "ranges.hh"
26 #include "stl.hh"
27 #include "unreachable.hh"
28 #include <cstring>
29 #include <iomanip>
30 #include <iostream>
31 #include <iterator>
32 #include <memory>
33 
34 using std::string;
35 using std::vector;
36 using std::min;
37 using std::shared_ptr;
38 
39 namespace openmsx {
40 
41 static std::unique_ptr<ReadOnlySetting> breakedSetting;
42 static unsigned breakedSettingCount = 0;
43 
44 
45 // Bitfields used in the disallowReadCache and disallowWriteCache arrays
46 constexpr byte SECONDARY_SLOT_BIT = 0x01;
47 constexpr byte MEMORY_WATCH_BIT = 0x02;
48 constexpr byte GLOBAL_RW_BIT = 0x04;
49 
50 
52  : memoryDebug (motherBoard_)
53  , slottedMemoryDebug(motherBoard_)
54  , ioDebug (motherBoard_)
55  , slotInfo(motherBoard_.getMachineInfoCommand())
56  , subSlottedInfo(motherBoard_.getMachineInfoCommand())
57  , externalSlotInfo(motherBoard_.getMachineInfoCommand())
58  , inputPortInfo (motherBoard_.getMachineInfoCommand())
59  , outputPortInfo(motherBoard_.getMachineInfoCommand())
60  , dummyDevice(DeviceFactory::createDummyDevice(
61  *motherBoard_.getMachineConfig()))
62  , msxcpu(motherBoard_.getCPU())
63  , cliComm(motherBoard_.getMSXCliComm())
64  , motherBoard(motherBoard_)
65  , fastForward(false)
66 {
67  ranges::fill(primarySlotState, 0);
68  ranges::fill(secondarySlotState, 0);
69  ranges::fill(expanded, 0);
70  ranges::fill(subSlotRegister, 0);
71  ranges::fill(IO_In, dummyDevice.get());
72  ranges::fill(IO_Out, dummyDevice.get());
73  ranges::fill(visibleDevices, dummyDevice.get());
74  for (int primSlot = 0; primSlot < 4; ++primSlot) {
75  for (int secSlot = 0; secSlot < 4; ++secSlot) {
76  for (int page = 0; page < 4; ++page) {
77  slotLayout[primSlot][secSlot][page] = dummyDevice.get();
78  }
79  }
80  }
81 
82  // initially allow all regions to be cached
83  memset(disallowReadCache, 0, sizeof(disallowReadCache));
84  memset(disallowWriteCache, 0, sizeof(disallowWriteCache));
85 
86  initialPrimarySlots = motherBoard.getMachineConfig()->parseSlotMap();
87  // Note: SlotState is initialised at reset
88 
89  msxcpu.setInterface(this);
90 
91  if (motherBoard.isTurboR()) {
92  // TODO also MSX2+ needs (slightly different) VDPIODelay
93  delayDevice = DeviceFactory::createVDPIODelay(
94  *motherBoard.getMachineConfig(), *this);
95  for (int port = 0x98; port <= 0x9B; ++port) {
96  assert(IO_In [port] == dummyDevice.get());
97  assert(IO_Out[port] == dummyDevice.get());
98  IO_In [port] = delayDevice.get();
99  IO_Out[port] = delayDevice.get();
100  }
101  }
102 
103  if (breakedSettingCount++ == 0) {
104  assert(!breakedSetting);
105  breakedSetting = std::make_unique<ReadOnlySetting>(
106  motherBoard.getReactor().getCommandController(),
107  "breaked", "Similar to 'debug breaked'",
108  TclObject("false"));
109  }
110  reset();
111 }
112 
114 {
115  if (--breakedSettingCount == 0) {
116  assert(breakedSetting);
117  breakedSetting = nullptr;
118  }
119 
120  removeAllWatchPoints();
121 
122  if (delayDevice) {
123  for (int port = 0x98; port <= 0x9B; ++port) {
124  assert(IO_In [port] == delayDevice.get());
125  assert(IO_Out[port] == delayDevice.get());
126  IO_In [port] = dummyDevice.get();
127  IO_Out[port] = dummyDevice.get();
128  }
129  }
130 
131  msxcpu.setInterface(nullptr);
132 
133  #ifndef NDEBUG
134  for (int port = 0; port < 256; ++port) {
135  if (IO_In[port] != dummyDevice.get()) {
136  std::cout << "In-port " << port << " still registered "
137  << IO_In[port]->getName() << '\n';
138  UNREACHABLE;
139  }
140  if (IO_Out[port] != dummyDevice.get()) {
141  std::cout << "Out-port " << port << " still registered "
142  << IO_Out[port]->getName() << '\n';
143  UNREACHABLE;
144  }
145  }
146  for (int primSlot = 0; primSlot < 4; ++primSlot) {
147  assert(!isExpanded(primSlot));
148  for (int secSlot = 0; secSlot < 4; ++secSlot) {
149  for (int page = 0; page < 4; ++page) {
150  assert(slotLayout[primSlot][secSlot][page] == dummyDevice.get());
151  }
152  }
153  }
154  #endif
155 }
156 
157 void MSXCPUInterface::removeAllWatchPoints()
158 {
159  while (!watchPoints.empty()) {
160  removeWatchPoint(watchPoints.back());
161  }
162 
163 }
164 
165 byte MSXCPUInterface::readMemSlow(word address, EmuTime::param time)
166 {
167  // something special in this region?
168  if (unlikely(disallowReadCache[address >> CacheLine::BITS])) {
169  // slot-select-ignore reads (e.g. used in 'Carnivore2')
170  for (auto& g : globalReads) {
171  // very primitive address selection mechanism,
172  // but more than enough for now
173  if (unlikely(g.addr == address)) {
174  g.device->globalRead(address, time);
175  }
176  }
177  // execute read watches before actual read
178  if (readWatchSet[address >> CacheLine::BITS]
179  [address & CacheLine::LOW]) {
180  executeMemWatch(WatchPoint::READ_MEM, address);
181  }
182  }
183  if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) {
184  return 0xFF ^ subSlotRegister[primarySlotState[3]];
185  } else {
186  return visibleDevices[address >> 14]->readMem(address, time);
187  }
188 }
189 
190 void MSXCPUInterface::writeMemSlow(word address, byte value, EmuTime::param time)
191 {
192  if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) {
193  setSubSlot(primarySlotState[3], value);
194  // Confirmed on turboR GT machine: write does _not_ also go to
195  // the underlying (hidden) device. But it's theoretically
196  // possible other slotexpanders behave different.
197  } else {
198  visibleDevices[address>>14]->writeMem(address, value, time);
199  }
200  // something special in this region?
201  if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
202  // slot-select-ignore writes (Super Lode Runner)
203  for (auto& g : globalWrites) {
204  // very primitive address selection mechanism,
205  // but more than enough for now
206  if (unlikely(g.addr == address)) {
207  g.device->globalWrite(address, value, time);
208  }
209  }
210  // execute write watches after actual write
211  if (writeWatchSet[address >> CacheLine::BITS]
212  [address & CacheLine::LOW]) {
213  executeMemWatch(WatchPoint::WRITE_MEM, address, value);
214  }
215  }
216 }
217 
219 {
220  if (expanded[ps] == 0) {
221  for (int page = 0; page < 4; ++page) {
222  if (slotLayout[ps][0][page] != dummyDevice.get()) {
223  throw MSXException("Can't expand slot because "
224  "it's already in use.");
225  }
226  }
227  }
228  expanded[ps]++;
229  changeExpanded(isExpanded(primarySlotState[3]));
230 }
231 
233  int ps, vector<MSXDevice*> allowed) const
234 {
235  // TODO handle multi-devices
236  allowed.push_back(dummyDevice.get());
237  ranges::sort(allowed); // for set_difference()
238  assert(isExpanded(ps));
239  if (expanded[ps] != 1) return; // ok, still expanded after this
240 
241  std::vector<MSXDevice*> inUse;
242  for (int ss = 0; ss < 4; ++ss) {
243  for (int page = 0; page < 4; ++page) {
244  MSXDevice* device = slotLayout[ps][ss][page];
245  std::vector<MSXDevice*> devices;
246  std::vector<MSXDevice*>::iterator end_devices;
247  if (auto memDev = dynamic_cast<MSXMultiMemDevice*>(device)) {
248  devices = memDev->getDevices();
249  ranges::sort(devices); // for set_difference()
250  end_devices = ranges::unique(devices);
251  } else {
252  devices.push_back(device);
253  end_devices = end(devices);
254  }
255  std::set_difference(begin(devices), end_devices,
256  begin(allowed), end(allowed),
257  std::inserter(inUse, end(inUse)));
258 
259  }
260  }
261  if (inUse.empty()) return; // ok, no more devices in use
262 
263  string msg = strCat("Can't remove slot expander from slot ", ps,
264  " because the following devices are still inserted:");
265  for (auto& d : inUse) {
266  strAppend(msg, ' ', d->getName());
267  }
268  strAppend(msg, '.');
269  throw MSXException(std::move(msg));
270 }
271 
273 {
274 #ifndef NDEBUG
275  try {
276  vector<MSXDevice*> dummy;
277  testUnsetExpanded(ps, dummy);
278  } catch (...) {
279  UNREACHABLE;
280  }
281 #endif
282  expanded[ps]--;
283  changeExpanded(isExpanded(primarySlotState[3]));
284 }
285 
286 void MSXCPUInterface::changeExpanded(bool newExpanded)
287 {
288  if (newExpanded) {
289  disallowReadCache [0xFF] |= SECONDARY_SLOT_BIT;
290  disallowWriteCache[0xFF] |= SECONDARY_SLOT_BIT;
291  } else {
292  disallowReadCache [0xFF] &= ~SECONDARY_SLOT_BIT;
293  disallowWriteCache[0xFF] &= ~SECONDARY_SLOT_BIT;
294  }
295  msxcpu.invalidateMemCache(0xFFFF & CacheLine::HIGH, 0x100);
296 }
297 
298 MSXDevice*& MSXCPUInterface::getDevicePtr(byte port, bool isIn)
299 {
300  MSXDevice** devicePtr = isIn ? &IO_In[port] : &IO_Out[port];
301  while (auto watch = dynamic_cast<MSXWatchIODevice*>(*devicePtr)) {
302  devicePtr = &watch->getDevicePtr();
303  }
304  if (*devicePtr == delayDevice.get()) {
305  devicePtr = isIn ? &delayDevice->getInDevicePtr (port)
306  : &delayDevice->getOutDevicePtr(port);
307  }
308  return *devicePtr;
309 }
310 
312 {
313  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
314  register_IO(port, true, devicePtr, device); // in
315 }
316 
318 {
319  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
320  unregister_IO(devicePtr, device);
321 }
322 
324 {
325  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
326  register_IO(port, false, devicePtr, device); // out
327 }
328 
330 {
331  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
332  unregister_IO(devicePtr, device);
333 }
334 
335 void MSXCPUInterface::register_IO(int port, bool isIn,
336  MSXDevice*& devicePtr, MSXDevice* device)
337 {
338  if (devicePtr == dummyDevice.get()) {
339  // first, replace DummyDevice
340  devicePtr = device;
341  } else {
342  if (auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
343  // third or more, add to existing MultiIO device
344  multi->addDevice(device);
345  } else {
346  // second, create a MultiIO device
347  multi = new MSXMultiIODevice(device->getHardwareConfig());
348  multi->addDevice(devicePtr);
349  multi->addDevice(device);
350  devicePtr = multi;
351  }
352  if (isIn) {
353  cliComm.printWarning(
354  "Conflicting input port 0x",
355  hex_string<2>(port),
356  " for devices ", devicePtr->getName());
357  }
358  }
359 }
360 
361 void MSXCPUInterface::unregister_IO(MSXDevice*& devicePtr, MSXDevice* device)
362 {
363  if (auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
364  // remove from MultiIO device
365  multi->removeDevice(device);
366  auto& devices = multi->getDevices();
367  if (devices.size() == 1) {
368  // only one remaining, remove MultiIO device
369  devicePtr = devices.front();
370  devices.pop_back();
371  delete multi;
372  }
373  } else {
374  // remove last, put back DummyDevice
375  assert(devicePtr == device);
376  devicePtr = dummyDevice.get();
377  }
378 }
379 
381  byte port, MSXDevice* oldDevice, MSXDevice* newDevice)
382 {
383  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
384  if (devicePtr != oldDevice) {
385  // error, this was not the expected device
386  return false;
387  }
388  devicePtr = newDevice;
389  return true;
390 }
392  byte port, MSXDevice* oldDevice, MSXDevice* newDevice)
393 {
394  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
395  if (devicePtr != oldDevice) {
396  // error, this was not the expected device
397  return false;
398  }
399  devicePtr = newDevice;
400  return true;
401 }
402 
403 static void reportMemOverlap(int ps, int ss, MSXDevice& dev1, MSXDevice& dev2)
404 {
405  throw MSXException(
406  "Overlapping memory devices in slot ", ps, '.', ss,
407  ": ", dev1.getName(), " and ", dev2.getName(), '.');
408 }
409 
410 void MSXCPUInterface::testRegisterSlot(
411  MSXDevice& device, int ps, int ss, int base, int size)
412 {
413  int page = base >> 14;
414  MSXDevice*& slot = slotLayout[ps][ss][page];
415  if (size == 0x4000) {
416  // full 16kb, directly register device (no multiplexer)
417  if (slot != dummyDevice.get()) {
418  reportMemOverlap(ps, ss, *slot, device);
419  }
420  } else {
421  // partial page
422  if (slot == dummyDevice.get()) {
423  // first, ok
424  } else if (auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
425  // second (or more), check for overlap
426  if (!multi->canAdd(base, size)) {
427  reportMemOverlap(ps, ss, *slot, device);
428  }
429  } else {
430  // conflict with 'full ranged' device
431  reportMemOverlap(ps, ss, *slot, device);
432  }
433  }
434 }
435 
436 void MSXCPUInterface::registerSlot(
437  MSXDevice& device, int ps, int ss, int base, int size)
438 {
439  int page = base >> 14;
440  MSXDevice*& slot = slotLayout[ps][ss][page];
441  if (size == 0x4000) {
442  // full 16kb, directly register device (no multiplexer)
443  assert(slot == dummyDevice.get());
444  slot = &device;
445  } else {
446  // partial page
447  if (slot == dummyDevice.get()) {
448  // first
449  auto* multi = new MSXMultiMemDevice(device.getHardwareConfig());
450  multi->add(device, base, size);
451  slot = multi;
452  } else if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
453  // second or more
454  assert(multi->canAdd(base, size));
455  multi->add(device, base, size);
456  } else {
457  // conflict with 'full ranged' device
458  assert(false);
459  }
460  }
461  updateVisible(page);
462 }
463 
464 void MSXCPUInterface::unregisterSlot(
465  MSXDevice& device, int ps, int ss, int base, int size)
466 {
467  int page = base >> 14;
468  MSXDevice*& slot = slotLayout[ps][ss][page];
469  if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
470  // partial range
471  multi->remove(device, base, size);
472  if (multi->empty()) {
473  delete multi;
474  slot = dummyDevice.get();
475  }
476  } else {
477  // full 16kb range
478  assert(slot == &device);
479  slot = dummyDevice.get();
480  }
481  updateVisible(page);
482 }
483 
485  MSXDevice& device, int ps, int ss, int base_, int size_)
486 {
487  if (!isExpanded(ps) && (ss != 0)) {
488  throw MSXException(
489  "Slot ", ps, '.', ss,
490  " does not exist because slot is not expanded.");
491  }
492 
493  // split range on 16kb borders
494  // first check if registration is possible
495  int base = base_;
496  int size = size_;
497  while (size > 0) {
498  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
499  testRegisterSlot(device, ps, ss, base, partialSize);
500  base += partialSize;
501  size -= partialSize;
502  }
503  // if all checks are successful, only then actually register
504  base = base_;
505  size = size_;
506  while (size > 0) {
507  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
508  registerSlot(device, ps, ss, base, partialSize);
509  base += partialSize;
510  size -= partialSize;
511  }
512 }
513 
515  MSXDevice& device, int ps, int ss, int base, int size)
516 {
517  // split range on 16kb borders
518  while (size > 0) {
519  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
520  unregisterSlot(device, ps, ss, base, partialSize);
521  base += partialSize;
522  size -= partialSize;
523  }
524 }
525 
527 {
528  globalWrites.push_back({&device, address});
529 
530  disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
531  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
532 }
533 
535 {
536  GlobalRwInfo info = { &device, address };
537  move_pop_back(globalWrites, rfind_unguarded(globalWrites, info));
538 
539  for (auto& g : globalWrites) {
540  if ((g.addr >> CacheLine::BITS) ==
541  (address >> CacheLine::BITS)) {
542  // there is still a global write in this region
543  return;
544  }
545  }
546  disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
547  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
548 }
549 
551 {
552  globalReads.push_back({&device, address});
553 
554  disallowReadCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
555  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
556 }
557 
559 {
560  GlobalRwInfo info = { &device, address };
561  move_pop_back(globalReads, rfind_unguarded(globalReads, info));
562 
563  for (auto& g : globalReads) {
564  if ((g.addr >> CacheLine::BITS) ==
565  (address >> CacheLine::BITS)) {
566  // there is still a global write in this region
567  return;
568  }
569  }
570  disallowReadCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
571  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
572 }
573 
574 ALWAYS_INLINE void MSXCPUInterface::updateVisible(int page, int ps, int ss)
575 {
576  MSXDevice* newDevice = slotLayout[ps][ss][page];
577  if (visibleDevices[page] != newDevice) {
578  visibleDevices[page] = newDevice;
579  msxcpu.updateVisiblePage(page, ps, ss);
580  }
581 }
582 void MSXCPUInterface::updateVisible(int page)
583 {
584  updateVisible(page, primarySlotState[page], secondarySlotState[page]);
585 }
586 
588 {
589  for (int i = 0; i < 4; ++i) {
590  setSubSlot(i, 0);
591  }
592  setPrimarySlots(initialPrimarySlots);
593 }
594 
596 {
597  return motherBoard.readIRQVector();
598 }
599 
601 {
602  // Change the slot structure.
603  // Originally the code below was a loop over the 4 pages, and the check
604  // for (un)expanded-slot was done unconditionally at the end. I've
605  // completely unrolled the loop and only check for (un)expanded slot
606  // when the slot in page 3 has changed. I've also added checks for slot
607  // changes for the other 3 pages. Usually when this register is written
608  // only one of the 4 pages actually changes, so these extra checks do
609  // pay off. This does make the code a bit more complex (and the
610  // generated code slightly bigger), but it does make a measurable speed
611  // difference. Changing the slots several hundreds of times per
612  // (EmuTime) is not unusual. So this routine ended up quite high
613  // (top-10) in some profile results.
614  int ps0 = (value >> 0) & 3;
615  if (unlikely(primarySlotState[0] != ps0)) {
616  primarySlotState[0] = ps0;
617  int ss0 = (subSlotRegister[ps0] >> 0) & 3;
618  secondarySlotState[0] = ss0;
619  updateVisible(0, ps0, ss0);
620  }
621  int ps1 = (value >> 2) & 3;
622  if (unlikely(primarySlotState[1] != ps1)) {
623  primarySlotState[1] = ps1;
624  int ss1 = (subSlotRegister[ps1] >> 2) & 3;
625  secondarySlotState[1] = ss1;
626  updateVisible(1, ps1, ss1);
627  }
628  int ps2 = (value >> 4) & 3;
629  if (unlikely(primarySlotState[2] != ps2)) {
630  primarySlotState[2] = ps2;
631  int ss2 = (subSlotRegister[ps2] >> 4) & 3;
632  secondarySlotState[2] = ss2;
633  updateVisible(2, ps2, ss2);
634  }
635  int ps3 = (value >> 6) & 3;
636  if (unlikely(primarySlotState[3] != ps3)) {
637  bool oldExpanded = isExpanded(primarySlotState[3]);
638  bool newExpanded = isExpanded(ps3);
639  primarySlotState[3] = ps3;
640  int ss3 = (subSlotRegister[ps3] >> 6) & 3;
641  secondarySlotState[3] = ss3;
642  updateVisible(3, ps3, ss3);
643  if (unlikely(oldExpanded != newExpanded)) {
644  changeExpanded(newExpanded);
645  }
646  }
647 }
648 
649 void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
650 {
651  subSlotRegister[primSlot] = value;
652  for (int page = 0; page < 4; ++page, value >>= 2) {
653  if (primSlot == primarySlotState[page]) {
654  secondarySlotState[page] = value & 3;
655  // Change the visible devices
656  updateVisible(page);
657  }
658  }
659 }
660 
661 byte MSXCPUInterface::peekMem(word address, EmuTime::param time) const
662 {
663  if ((address == 0xFFFF) && isExpanded(primarySlotState[3])) {
664  return 0xFF ^ subSlotRegister[primarySlotState[3]];
665  } else {
666  return visibleDevices[address >> 14]->peekMem(address, time);
667  }
668 }
669 
670 byte MSXCPUInterface::peekSlottedMem(unsigned address, EmuTime::param time) const
671 {
672  byte primSlot = (address & 0xC0000) >> 18;
673  byte subSlot = (address & 0x30000) >> 16;
674  byte page = (address & 0x0C000) >> 14;
675  word offset = (address & 0xFFFF); // includes page
676  if (!isExpanded(primSlot)) {
677  subSlot = 0;
678  }
679 
680  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
681  return 0xFF ^ subSlotRegister[primSlot];
682  } else {
683  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
684  }
685 }
686 
687 byte MSXCPUInterface::readSlottedMem(unsigned address, EmuTime::param time)
688 {
689  byte primSlot = (address & 0xC0000) >> 18;
690  byte subSlot = (address & 0x30000) >> 16;
691  byte page = (address & 0x0C000) >> 14;
692  word offset = (address & 0xFFFF); // includes page
693  if (!isExpanded(primSlot)) {
694  subSlot = 0;
695  }
696 
697  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
698  return 0xFF ^ subSlotRegister[primSlot];
699  } else {
700  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
701  }
702 }
703 
704 void MSXCPUInterface::writeSlottedMem(unsigned address, byte value,
705  EmuTime::param time)
706 {
707  byte primSlot = (address & 0xC0000) >> 18;
708  byte subSlot = (address & 0x30000) >> 16;
709  byte page = (address & 0x0C000) >> 14;
710  word offset = (address & 0xFFFF); // includes page
711  if (!isExpanded(primSlot)) {
712  subSlot = 0;
713  }
714 
715  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
716  setSubSlot(primSlot, value);
717  } else {
718  slotLayout[primSlot][subSlot][page]->writeMem(offset, value, time);
719  }
720 }
721 
723 {
724  auto it = ranges::upper_bound(breakPoints, bp, CompareBreakpoints());
725  breakPoints.insert(it, std::move(bp));
726 }
727 
729 {
730  auto [first, last] = ranges::equal_range(breakPoints, bp.getAddress(), CompareBreakpoints());
731  breakPoints.erase(find_if_unguarded(first, last,
732  [&](const BreakPoint& i) { return &i == &bp; }));
733 }
734 void MSXCPUInterface::removeBreakPoint(unsigned id)
735 {
736  auto it = ranges::find_if(breakPoints,
737  [&](const BreakPoint& i) { return i.getId() == id; });
738  // could be ==end for a breakpoint that removes itself AND has the -once flag set
739  if (it != breakPoints.end()) {
740  breakPoints.erase(it);
741  }
742 }
743 
745  std::pair<BreakPoints::const_iterator,
746  BreakPoints::const_iterator> range,
747  MSXMotherBoard& motherBoard)
748 {
749  // create copy for the case that breakpoint/condition removes itself
750  // - keeps object alive by holding a shared_ptr to it
751  // - avoids iterating over a changing collection
752  BreakPoints bpCopy(range.first, range.second);
753  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
754  auto& interp = motherBoard.getReactor().getInterpreter();
755  for (auto& p : bpCopy) {
756  p.checkAndExecute(globalCliComm, interp);
757  if (p.onlyOnce()) {
758  removeBreakPoint(p.getId());
759  }
760  }
761  auto condCopy = conditions;
762  for (auto& c : condCopy) {
763  c.checkAndExecute(globalCliComm, interp);
764  if (c.onlyOnce()) {
765  removeCondition(c.getId());
766  }
767  }
768 }
769 
770 
771 void MSXCPUInterface::setWatchPoint(const shared_ptr<WatchPoint>& watchPoint)
772 {
773  watchPoints.push_back(watchPoint);
774  WatchPoint::Type type = watchPoint->getType();
775  switch (type) {
776  case WatchPoint::READ_IO:
777  registerIOWatch(*watchPoint, IO_In);
778  break;
780  registerIOWatch(*watchPoint, IO_Out);
781  break;
784  updateMemWatch(type);
785  break;
786  default:
787  UNREACHABLE; break;
788  }
789 }
790 
791 void MSXCPUInterface::removeWatchPoint(shared_ptr<WatchPoint> watchPoint)
792 {
793  // Pass shared_ptr by value to keep the object alive for the duration
794  // of this function, otherwise it gets deleted as soon as it's removed
795  // from the watchPoints collection.
796  auto it = ranges::find(watchPoints, watchPoint);
797  if (it != end(watchPoints)) {
798  // remove before calling updateMemWatch()
799  watchPoints.erase(it);
800  WatchPoint::Type type = watchPoint->getType();
801  switch (type) {
802  case WatchPoint::READ_IO:
803  unregisterIOWatch(*watchPoint, IO_In);
804  break;
806  unregisterIOWatch(*watchPoint, IO_Out);
807  break;
810  updateMemWatch(type);
811  break;
812  default:
813  UNREACHABLE; break;
814  }
815  }
816 }
817 
819 {
820  conditions.push_back(std::move(cond));
821 }
822 
824 {
825  conditions.erase(rfind_if_unguarded(conditions,
826  [&](DebugCondition& e) { return &e == &cond; }));
827 }
828 
829 void MSXCPUInterface::removeCondition(unsigned id)
830 {
831  auto it = ranges::find_if(conditions,
832  [&](DebugCondition& e) { return e.getId() == id; });
833  // could be ==end for a condition that removes itself AND has the -once flag set
834  if (it != conditions.end()) {
835  conditions.erase(it);
836  }
837 }
838 
839 void MSXCPUInterface::registerIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
840 {
841  assert(dynamic_cast<WatchIO*>(&watchPoint));
842  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
843  unsigned beginPort = ioWatch.getBeginAddress();
844  unsigned endPort = ioWatch.getEndAddress();
845  assert(beginPort <= endPort);
846  assert(endPort < 0x100);
847  for (unsigned port = beginPort; port <= endPort; ++port) {
848  ioWatch.getDevice(port).getDevicePtr() = devices[port];
849  devices[port] = &ioWatch.getDevice(port);
850  }
851 }
852 
853 void MSXCPUInterface::unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
854 {
855  assert(dynamic_cast<WatchIO*>(&watchPoint));
856  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
857  unsigned beginPort = ioWatch.getBeginAddress();
858  unsigned endPort = ioWatch.getEndAddress();
859  assert(beginPort <= endPort);
860  assert(endPort < 0x100);
861 
862  for (unsigned port = beginPort; port <= endPort; ++port) {
863  // find pointer to watchpoint
864  MSXDevice** prev = &devices[port];
865  while (*prev != &ioWatch.getDevice(port)) {
866  prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
867  }
868  // remove watchpoint from chain
869  *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
870  }
871 }
872 
873 void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
874 {
875  std::bitset<CacheLine::SIZE>* watchSet =
876  (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
877  for (unsigned i = 0; i < CacheLine::NUM; ++i) {
878  watchSet[i].reset();
879  }
880  for (auto& w : watchPoints) {
881  if (w->getType() == type) {
882  unsigned beginAddr = w->getBeginAddress();
883  unsigned endAddr = w->getEndAddress();
884  assert(beginAddr <= endAddr);
885  assert(endAddr < 0x10000);
886  for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
887  watchSet[addr >> CacheLine::BITS].set(
888  addr & CacheLine::LOW);
889  }
890  }
891  }
892  for (unsigned i = 0; i < CacheLine::NUM; ++i) {
893  if (readWatchSet [i].any()) {
894  disallowReadCache [i] |= MEMORY_WATCH_BIT;
895  } else {
896  disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
897  }
898  if (writeWatchSet[i].any()) {
899  disallowWriteCache[i] |= MEMORY_WATCH_BIT;
900  } else {
901  disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
902  }
903  }
904  msxcpu.invalidateMemCache(0x0000, 0x10000);
905 }
906 
907 void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
908  unsigned address, unsigned value)
909 {
910  assert(!watchPoints.empty());
911  if (isFastForward()) return;
912 
913  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
914  auto& interp = motherBoard.getReactor().getInterpreter();
915  interp.setVariable(TclObject("wp_last_address"),
916  TclObject(int(address)));
917  if (value != ~0u) {
918  interp.setVariable(TclObject("wp_last_value"),
919  TclObject(int(value)));
920  }
921 
922  auto wpCopy = watchPoints;
923  for (auto& w : wpCopy) {
924  if ((w->getBeginAddress() <= address) &&
925  (w->getEndAddress() >= address) &&
926  (w->getType() == type)) {
927  w->checkAndExecute(globalCliComm, interp);
928  if (w->onlyOnce()) {
929  removeWatchPoint(w);
930  }
931  }
932  }
933 
934  interp.unsetVariable("wp_last_address");
935  interp.unsetVariable("wp_last_value");
936 }
937 
938 
940 {
941  assert(!isFastForward());
942  if (breaked) return;
943  breaked = true;
944  msxcpu.exitCPULoopSync();
945 
946  Reactor& reactor = motherBoard.getReactor();
947  reactor.block();
948  breakedSetting->setReadOnlyValue(TclObject("true"));
949  reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
951  std::make_shared<SimpleEvent>(OPENMSX_BREAK_EVENT));
952 }
953 
955 {
956  assert(!isFastForward());
958  TclObject("debug break"), TclObject(), true));
959  doContinue();
960 }
961 
963 {
964  assert(!isFastForward());
965  if (breaked) {
966  breaked = false;
967 
968  Reactor& reactor = motherBoard.getReactor();
969  breakedSetting->setReadOnlyValue(TclObject("false"));
970  reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
971  reactor.unblock();
972  motherBoard.getRealTime().resync();
973  }
974 }
975 
977 {
978  // before the Tcl interpreter is destroyed, we must delete all
979  // TclObjects. Breakpoints and conditions contain such objects
980  // for the condition and action.
981  // TODO it would be nicer if breakpoints and conditions were not
982  // global objects.
983  breakPoints.clear();
984  conditions.clear();
985 }
986 
987 
988 // class MemoryDebug
989 
990 MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
991  : SimpleDebuggable(motherBoard_, "memory",
992  "The memory currently visible for the CPU.", 0x10000)
993 {
994 }
995 
996 byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
997 {
998  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
999  return interface.peekMem(address, time);
1000 }
1001 
1002 void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1003  EmuTime::param time)
1004 {
1005  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1006  return interface.writeMem(address, value, time);
1007 }
1008 
1009 
1010 // class SlottedMemoryDebug
1011 
1012 MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1013  MSXMotherBoard& motherBoard_)
1014  : SimpleDebuggable(motherBoard_, "slotted memory",
1015  "The memory in slots and subslots.", 0x10000 * 4 * 4)
1016 {
1017 }
1018 
1019 byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1020 {
1021  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1022  return interface.peekSlottedMem(address, time);
1023 }
1024 
1025 void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1026  EmuTime::param time)
1027 {
1028  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1029  return interface.writeSlottedMem(address, value, time);
1030 }
1031 
1032 
1033 // class SlotInfo
1034 
1035 static unsigned getSlot(
1036  Interpreter& interp, const TclObject& token, const string& itemName)
1037 {
1038  unsigned slot = token.getInt(interp);
1039  if (slot >= 4) {
1040  throw CommandException(itemName, " must be in range 0..3");
1041  }
1042  return slot;
1043 }
1044 
1045 MSXCPUInterface::SlotInfo::SlotInfo(
1046  InfoCommand& machineInfoCommand)
1047  : InfoTopic(machineInfoCommand, "slot")
1048 {
1049 }
1050 
1051 void MSXCPUInterface::SlotInfo::execute(span<const TclObject> tokens,
1052  TclObject& result) const
1053 {
1054  checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1055  auto& interp = getInterpreter();
1056  unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1057  unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1058  unsigned page = getSlot(interp, tokens[4], "Page");
1059  auto& interface = OUTER(MSXCPUInterface, slotInfo);
1060  if (!interface.isExpanded(ps)) {
1061  ss = 0;
1062  }
1063  interface.slotLayout[ps][ss][page]->getNameList(result);
1064 }
1065 
1066 string MSXCPUInterface::SlotInfo::help(const vector<string>& /*tokens*/) const
1067 {
1068  return "Retrieve name of the device inserted in given "
1069  "primary slot / secondary slot / page.";
1070 }
1071 
1072 
1073 // class SubSlottedInfo
1074 
1075 MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1076  InfoCommand& machineInfoCommand)
1077  : InfoTopic(machineInfoCommand, "issubslotted")
1078 {
1079 }
1080 
1081 void MSXCPUInterface::SubSlottedInfo::execute(span<const TclObject> tokens,
1082  TclObject& result) const
1083 {
1084  checkNumArgs(tokens, 3, "primary");
1085  auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1086  result = interface.isExpanded(
1087  getSlot(getInterpreter(), tokens[2], "Slot"));
1088 }
1089 
1090 string MSXCPUInterface::SubSlottedInfo::help(
1091  const vector<string>& /*tokens*/) const
1092 {
1093  return "Indicates whether a certain primary slot is expanded.";
1094 }
1095 
1096 
1097 // class ExternalSlotInfo
1098 
1099 MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1100  InfoCommand& machineInfoCommand)
1101  : InfoTopic(machineInfoCommand, "isexternalslot")
1102 {
1103 }
1104 
1105 void MSXCPUInterface::ExternalSlotInfo::execute(
1106  span<const TclObject> tokens, TclObject& result) const
1107 {
1108  checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1109  int ps = 0;
1110  int ss = 0;
1111  auto& interp = getInterpreter();
1112  switch (tokens.size()) {
1113  case 4:
1114  ss = getSlot(interp, tokens[3], "Secondary slot");
1115  // Fall-through
1116  case 3:
1117  ps = getSlot(interp, tokens[2], "Primary slot");
1118  break;
1119  }
1120  auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1121  auto& manager = interface.motherBoard.getSlotManager();
1122  result = manager.isExternalSlot(ps, ss, true);
1123 }
1124 
1125 string MSXCPUInterface::ExternalSlotInfo::help(
1126  const vector<string>& /*tokens*/) const
1127 {
1128  return "Indicates whether a certain slot is external or internal.";
1129 }
1130 
1131 
1132 // class IODebug
1133 
1134 MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1135  : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1136 {
1137 }
1138 
1139 byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1140 {
1141  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1142  return interface.IO_In[address & 0xFF]->peekIO(address, time);
1143 }
1144 
1145 void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1146 {
1147  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1148  interface.writeIO(word(address), value, time);
1149 }
1150 
1151 
1152 // class IOInfo
1153 
1154 MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1155  : InfoTopic(machineInfoCommand, name_)
1156 {
1157 }
1158 
1159 void MSXCPUInterface::IOInfo::helper(
1160  span<const TclObject> tokens, TclObject& result, MSXDevice** devices) const
1161 {
1162  checkNumArgs(tokens, 3, "port");
1163  unsigned port = tokens[2].getInt(getInterpreter());
1164  if (port >= 256) {
1165  throw CommandException("Port must be in range 0..255");
1166  }
1167  result = devices[port]->getName();
1168 }
1169 void MSXCPUInterface::IInfo::execute(
1170  span<const TclObject> tokens, TclObject& result) const
1171 {
1172  auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1173  helper(tokens, result, interface.IO_In);
1174 }
1175 void MSXCPUInterface::OInfo::execute(
1176  span<const TclObject> tokens, TclObject& result) const
1177 {
1178  auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1179  helper(tokens, result, interface.IO_Out);
1180 }
1181 
1182 string MSXCPUInterface::IOInfo::help(const vector<string>& /*tokens*/) const
1183 {
1184  return "Return the name of the device connected to the given IO port.";
1185 }
1186 
1187 
1188 template<typename Archive>
1189 void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1190 {
1191  // TODO watchPoints ???
1192 
1193  // primary and 4 secondary slot select registers
1194  byte prim = 0;
1195  if (!ar.isLoader()) {
1196  for (int i = 0; i < 4; ++i) {
1197  prim |= primarySlotState[i] << (2 * i);
1198  }
1199  }
1200  ar.serialize("primarySlots", prim,
1201  "subSlotRegs", subSlotRegister);
1202  if (ar.isLoader()) {
1203  setPrimarySlots(prim);
1204  for (int i = 0; i < 4; ++i) {
1205  setSubSlot(i, subSlotRegister[i]);
1206  }
1207  }
1208 
1209  if (delayDevice) {
1210  ar.serialize("vdpDelay", *delayDevice);
1211  }
1212 }
1214 
1215 } // namespace openmsx
Contains the main loop of openMSX.
Definition: Reactor.hh:66
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:107
void registerMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
Devices can register themself in the MSX slotstructure.
std::vector< BreakPoint > BreakPoints
static bool checkBreakPoints(unsigned pc, MSXMotherBoard &motherBoard)
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
Definition: MSXCPU.cc:141
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
void unregisterGlobalWrite(MSXDevice &device, word address)
#define ALWAYS_INLINE
Definition: inline.hh:16
#define unlikely(x)
Definition: likely.hh:15
MSXCPUInterface(const MSXCPUInterface &)=delete
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
byte parseSlotMap() const
Parses a slot mapping.
void unregisterGlobalRead(MSXDevice &device, word address)
unsigned getId() const
auto unique(ForwardRange &&range)
Definition: ranges.hh:137
Definition: span.hh:34
constexpr unsigned NUM
Definition: CacheLine.hh:8
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
constexpr unsigned LOW
Definition: CacheLine.hh:9
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:409
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:420
constexpr byte SECONDARY_SLOT_BIT
void distributeEvent(const EventPtr &event)
Schedule the given event for delivery.
constexpr unsigned HIGH
Definition: CacheLine.hh:10
void testUnsetExpanded(int ps, std::vector< MSXDevice *> allowed) const
virtual std::string getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:370
void setPrimarySlots(byte value)
void registerGlobalRead(MSXDevice &device, word address)
(Un)register global read.
bool replace_IO_In(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
These methods replace a previously registered device with a new one.
void unregister_IO_In(byte port, MSXDevice *device)
void removeWatchPoint(std::shared_ptr< WatchPoint > watchPoint)
size_t size(std::string_view utf8)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:177
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
CommandController & getCommandController()
Definition: Reactor.cc:316
constexpr byte MEMORY_WATCH_BIT
void add(MSXDevice &device, int base, int size)
static void removeCondition(const DebugCondition &cond)
word getAddress() const
Definition: BreakPoint.hh:21
EventDistributor & getEventDistributor()
Definition: Reactor.hh:81
auto upper_bound(ForwardRange &&range, const T &value)
Definition: ranges.hh:83
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:644
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:113
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:147
byte readSlottedMem(unsigned address, EmuTime::param time)
constexpr unsigned BITS
Definition: CacheLine.hh:6
void changeExpanded(bool newExpanded)
bool replace_IO_Out(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
Base class for CPU breakpoints.
Definition: BreakPoint.hh:13
static void setCondition(DebugCondition cond)
virtual byte peekMem(word address, EmuTime::param time) const
Read a byte from a given memory location.
Definition: MSXDevice.cc:426
Base class for CPU breakpoints.
Definition: WatchPoint.hh:13
auto equal_range(ForwardRange &&range, const T &value)
Definition: ranges.hh:95
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
const HardwareConfig * getMachineConfig() const
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void unregisterMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
General debugger condition Like breakpoints, but not tied to a specifc address.
void serialize(Archive &ar, unsigned version)
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
static void removeBreakPoint(const BreakPoint &bp)
void sort(RandomAccessRange &&range)
Definition: ranges.hh:35
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:31
GlobalCliComm & getGlobalCliComm()
Definition: Reactor.hh:82
void unregister_IO_Out(byte port, MSXDevice *device)
auto set_difference(InputRange1 &&range1, InputRange2 &&range2, OutputIter out)
Definition: ranges.hh:221
void printWarning(std::string_view message)
Definition: CliComm.cc:10
static std::unique_ptr< VDPIODelay > createVDPIODelay(const HardwareConfig &hwConf, MSXCPUInterface &cpuInterface)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
int g
void unblock()
Definition: Reactor.cc:600
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
void setInterface(MSXCPUInterface *interf)
Definition: MSXCPU.cc:74
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
void reset()
Reset (the slot state)
bool isExpanded(int ps) const
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:43
void setVariable(const TclObject &name, const TclObject &value)
Definition: Interpreter.cc:239
void registerGlobalWrite(MSXDevice &device, word address)
(Un)register global writes.
constexpr byte GLOBAL_RW_BIT
unsigned getId() const
Definition: BreakPoint.hh:22
void writeSlottedMem(unsigned address, byte value, EmuTime::param time)
unsigned getBeginAddress() const
Definition: WatchPoint.hh:34
void addDevice(MSXDevice *device)
CliComm & getCliComm()
Definition: Reactor.cc:306
ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to &#39;find_if&#39; when it&#39;s guaranteed that the predicate will be true for at least one...
Definition: stl.hh:131
std::string strCat(Ts &&...ts)
Definition: strCat.hh:573
void exitCPULoopSync()
See CPUCore::exitCPULoopsync()
Definition: MSXCPU.cc:118
byte peekSlottedMem(unsigned address, EmuTime::param time) const
auto rfind_unguarded(RANGE &range, const VAL &val)
Similar to the find(_if)_unguarded functions above, but searches from the back to front...
Definition: stl.hh:152
#define OUTER(type, member)
Definition: outer.hh:38
auto rfind_if_unguarded(RANGE &range, PRED pred)
Definition: stl.hh:160
static void insertBreakPoint(BreakPoint bp)
Interpreter & getInterpreter()
Definition: Reactor.cc:311
byte readIRQVector()
CPU uses this method to read &#39;extra&#39; data from the databus used in interrupt routines.
#define UNREACHABLE
Definition: unreachable.hh:38