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