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