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