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  cliComm.printWarning(
381  "Conflicting input port 0x",
382  hex_string<2>(port),
383  " for devices ", devicePtr->getName());
384  }
385  }
386 }
387 
388 void MSXCPUInterface::unregister_IO(MSXDevice*& devicePtr, MSXDevice* device)
389 {
390  if (auto* multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
391  // remove from MultiIO device
392  multi->removeDevice(device);
393  auto& devices = multi->getDevices();
394  if (devices.size() == 1) {
395  // only one remaining, remove MultiIO device
396  devicePtr = devices.front();
397  devices.pop_back();
398  delete multi;
399  }
400  } else {
401  // remove last, put back DummyDevice
402  assert(devicePtr == device);
403  devicePtr = dummyDevice.get();
404  }
405 }
406 
408  byte port, MSXDevice* oldDevice, MSXDevice* newDevice)
409 {
410  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
411  if (devicePtr != oldDevice) {
412  // error, this was not the expected device
413  return false;
414  }
415  devicePtr = newDevice;
416  return true;
417 }
419  byte port, MSXDevice* oldDevice, MSXDevice* newDevice)
420 {
421  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
422  if (devicePtr != oldDevice) {
423  // error, this was not the expected device
424  return false;
425  }
426  devicePtr = newDevice;
427  return true;
428 }
429 
430 static void reportMemOverlap(int ps, int ss, MSXDevice& dev1, MSXDevice& dev2)
431 {
432  throw MSXException(
433  "Overlapping memory devices in slot ", ps, '.', ss,
434  ": ", dev1.getName(), " and ", dev2.getName(), '.');
435 }
436 
437 void MSXCPUInterface::testRegisterSlot(
438  MSXDevice& device, int ps, int ss, int base, int size)
439 {
440  int page = base >> 14;
441  MSXDevice*& slot = slotLayout[ps][ss][page];
442  if (size == 0x4000) {
443  // full 16kb, directly register device (no multiplexer)
444  if (slot != dummyDevice.get()) {
445  reportMemOverlap(ps, ss, *slot, device);
446  }
447  } else {
448  // partial page
449  if (slot == dummyDevice.get()) {
450  // first, ok
451  } else if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
452  // second (or more), check for overlap
453  if (!multi->canAdd(base, size)) {
454  reportMemOverlap(ps, ss, *slot, device);
455  }
456  } else {
457  // conflict with 'full ranged' device
458  reportMemOverlap(ps, ss, *slot, device);
459  }
460  }
461 }
462 
463 void MSXCPUInterface::registerSlot(
464  MSXDevice& device, int ps, int ss, int base, int size)
465 {
466  int page = base >> 14;
467  MSXDevice*& slot = slotLayout[ps][ss][page];
468  if (size == 0x4000) {
469  // full 16kb, directly register device (no multiplexer)
470  assert(slot == dummyDevice.get());
471  slot = &device;
472  } else {
473  // partial page
474  if (slot == dummyDevice.get()) {
475  // first
476  auto* multi = new MSXMultiMemDevice(device.getHardwareConfig());
477  multi->add(device, base, size);
478  slot = multi;
479  } else if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
480  // second or more
481  assert(multi->canAdd(base, size));
482  multi->add(device, base, size);
483  } else {
484  // conflict with 'full ranged' device
485  assert(false);
486  }
487  }
488  invalidateRWCache(base, size, ps, ss);
489  updateVisible(page);
490 }
491 
492 void MSXCPUInterface::unregisterSlot(
493  MSXDevice& device, int ps, int ss, int base, int size)
494 {
495  int page = base >> 14;
496  MSXDevice*& slot = slotLayout[ps][ss][page];
497  if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
498  // partial range
499  multi->remove(device, base, size);
500  if (multi->empty()) {
501  delete multi;
502  slot = dummyDevice.get();
503  }
504  } else {
505  // full 16kb range
506  assert(slot == &device);
507  slot = dummyDevice.get();
508  }
509  invalidateRWCache(base, size, ps, ss);
510  updateVisible(page);
511 }
512 
514  MSXDevice& device, int ps, int ss, int base_, int size_)
515 {
516  if (!isExpanded(ps) && (ss != 0)) {
517  throw MSXException(
518  "Slot ", ps, '.', ss,
519  " does not exist because slot is not expanded.");
520  }
521 
522  // split range on 16kb borders
523  // first check if registration is possible
524  int base = base_;
525  int size = size_;
526  while (size > 0) {
527  int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
528  testRegisterSlot(device, ps, ss, base, partialSize);
529  base += partialSize;
530  size -= partialSize;
531  }
532  // if all checks are successful, only then actually register
533  base = base_;
534  size = size_;
535  while (size > 0) {
536  int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
537  registerSlot(device, ps, ss, base, partialSize);
538  base += partialSize;
539  size -= partialSize;
540  }
541 }
542 
544  MSXDevice& device, int ps, int ss, int base, int size)
545 {
546  // split range on 16kb borders
547  while (size > 0) {
548  int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
549  unregisterSlot(device, ps, ss, base, partialSize);
550  base += partialSize;
551  size -= partialSize;
552  }
553 }
554 
556 {
557  globalWrites.push_back({&device, address});
558 
559  disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
560  msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
561 }
562 
564 {
565  GlobalRwInfo info = { &device, address };
566  move_pop_back(globalWrites, rfind_unguarded(globalWrites, info));
567 
568  for (auto& g : globalWrites) {
569  if ((g.addr >> CacheLine::BITS) ==
570  (address >> CacheLine::BITS)) {
571  // there is still a global write in this region
572  return;
573  }
574  }
575  disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
576  msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
577 }
578 
580 {
581  globalReads.push_back({&device, address});
582 
583  disallowReadCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
584  msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
585 }
586 
588 {
589  GlobalRwInfo info = { &device, address };
590  move_pop_back(globalReads, rfind_unguarded(globalReads, info));
591 
592  for (auto& g : globalReads) {
593  if ((g.addr >> CacheLine::BITS) ==
594  (address >> CacheLine::BITS)) {
595  // there is still a global write in this region
596  return;
597  }
598  }
599  disallowReadCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
600  msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
601 }
602 
603 ALWAYS_INLINE void MSXCPUInterface::updateVisible(int page, int ps, int ss)
604 {
605  MSXDevice* newDevice = slotLayout[ps][ss][page];
606  if (visibleDevices[page] != newDevice) {
607  visibleDevices[page] = newDevice;
608  msxcpu.updateVisiblePage(page, ps, ss);
609  }
610 }
611 void MSXCPUInterface::updateVisible(int page)
612 {
613  updateVisible(page, primarySlotState[page], secondarySlotState[page]);
614 }
615 
616 void MSXCPUInterface::invalidateRWCache(word start, unsigned size, int ps, int ss)
617 {
619  msxcpu.invalidateRWCache(start, size, ps, ss, disallowReadCache, disallowWriteCache);
620 }
621 void MSXCPUInterface::invalidateRCache (word start, unsigned size, int ps, int ss)
622 {
624  msxcpu.invalidateRCache(start, size, ps, ss, disallowReadCache);
625 }
626 void MSXCPUInterface::invalidateWCache (word start, unsigned size, int ps, int ss)
627 {
629  msxcpu.invalidateWCache(start, size, ps, ss, disallowWriteCache);
630 }
631 
632 void MSXCPUInterface::fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss)
633 {
635  msxcpu.fillRWCache(start, size, rData, wData, ps, ss, disallowReadCache, disallowWriteCache);
636 }
637 void MSXCPUInterface::fillRCache(unsigned start, unsigned size, const byte* rData, int ps, int ss)
638 {
640  msxcpu.fillRCache(start, size, rData, ps, ss, disallowReadCache);
641 }
642 void MSXCPUInterface::fillWCache(unsigned start, unsigned size, byte* wData, int ps, int ss)
643 {
645  msxcpu.fillWCache(start, size, wData, ps, ss, disallowWriteCache);
646 }
647 
649 {
650  for (auto i : xrange(4)) {
651  setSubSlot(i, 0);
652  }
653  setPrimarySlots(initialPrimarySlots);
654 }
655 
657 {
658  return motherBoard.readIRQVector();
659 }
660 
662 {
663  // Change the slot structure.
664  // Originally the code below was a loop over the 4 pages, and the check
665  // for (un)expanded-slot was done unconditionally at the end. I've
666  // completely unrolled the loop and only check for (un)expanded slot
667  // when the slot in page 3 has changed. I've also added checks for slot
668  // changes for the other 3 pages. Usually when this register is written
669  // only one of the 4 pages actually changes, so these extra checks do
670  // pay off. This does make the code a bit more complex (and the
671  // generated code slightly bigger), but it does make a measurable speed
672  // difference. Changing the slots several hundreds of times per
673  // (EmuTime) is not unusual. So this routine ended up quite high
674  // (top-10) in some profile results.
675  int ps0 = (value >> 0) & 3;
676  if (unlikely(primarySlotState[0] != ps0)) {
677  primarySlotState[0] = ps0;
678  int ss0 = (subSlotRegister[ps0] >> 0) & 3;
679  secondarySlotState[0] = ss0;
680  updateVisible(0, ps0, ss0);
681  }
682  int ps1 = (value >> 2) & 3;
683  if (unlikely(primarySlotState[1] != ps1)) {
684  primarySlotState[1] = ps1;
685  int ss1 = (subSlotRegister[ps1] >> 2) & 3;
686  secondarySlotState[1] = ss1;
687  updateVisible(1, ps1, ss1);
688  }
689  int ps2 = (value >> 4) & 3;
690  if (unlikely(primarySlotState[2] != ps2)) {
691  primarySlotState[2] = ps2;
692  int ss2 = (subSlotRegister[ps2] >> 4) & 3;
693  secondarySlotState[2] = ss2;
694  updateVisible(2, ps2, ss2);
695  }
696  int ps3 = (value >> 6) & 3;
697  if (unlikely(primarySlotState[3] != ps3)) {
698  bool oldExpanded = isExpanded(primarySlotState[3]);
699  bool newExpanded = isExpanded(ps3);
700  primarySlotState[3] = ps3;
701  int ss3 = (subSlotRegister[ps3] >> 6) & 3;
702  secondarySlotState[3] = ss3;
703  updateVisible(3, ps3, ss3);
704  if (unlikely(oldExpanded != newExpanded)) {
705  changeExpanded(newExpanded);
706  }
707  }
708 }
709 
710 void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
711 {
712  subSlotRegister[primSlot] = value;
713  for (int page = 0; page < 4; ++page, value >>= 2) {
714  if (primSlot == primarySlotState[page]) {
715  secondarySlotState[page] = value & 3;
716  // Change the visible devices
717  updateVisible(page);
718  }
719  }
720 }
721 
722 byte MSXCPUInterface::peekMem(word address, EmuTime::param time) const
723 {
724  if ((address == 0xFFFF) && isExpanded(primarySlotState[3])) {
725  return 0xFF ^ subSlotRegister[primarySlotState[3]];
726  } else {
727  return visibleDevices[address >> 14]->peekMem(address, time);
728  }
729 }
730 
731 byte MSXCPUInterface::peekSlottedMem(unsigned address, EmuTime::param time) const
732 {
733  byte primSlot = (address & 0xC0000) >> 18;
734  byte subSlot = (address & 0x30000) >> 16;
735  byte page = (address & 0x0C000) >> 14;
736  word offset = (address & 0xFFFF); // includes page
737  if (!isExpanded(primSlot)) {
738  subSlot = 0;
739  }
740 
741  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
742  return 0xFF ^ subSlotRegister[primSlot];
743  } else {
744  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
745  }
746 }
747 
748 byte MSXCPUInterface::readSlottedMem(unsigned address, EmuTime::param time)
749 {
750  byte primSlot = (address & 0xC0000) >> 18;
751  byte subSlot = (address & 0x30000) >> 16;
752  byte page = (address & 0x0C000) >> 14;
753  word offset = (address & 0xFFFF); // includes page
754  if (!isExpanded(primSlot)) {
755  subSlot = 0;
756  }
757 
758  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
759  return 0xFF ^ subSlotRegister[primSlot];
760  } else {
761  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
762  }
763 }
764 
765 void MSXCPUInterface::writeSlottedMem(unsigned address, byte value,
766  EmuTime::param time)
767 {
768  byte primSlot = (address & 0xC0000) >> 18;
769  byte subSlot = (address & 0x30000) >> 16;
770  byte page = (address & 0x0C000) >> 14;
771  word offset = (address & 0xFFFF); // includes page
772  if (!isExpanded(primSlot)) {
773  subSlot = 0;
774  }
775 
776  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
777  setSubSlot(primSlot, value);
778  } else {
779  slotLayout[primSlot][subSlot][page]->writeMem(offset, value, time);
780  }
781 }
782 
784 {
785  auto it = ranges::upper_bound(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
786  breakPoints.insert(it, std::move(bp));
787 }
788 
790 {
791  auto [first, last] = ranges::equal_range(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
792  breakPoints.erase(find_unguarded(first, last, &bp,
793  [](const BreakPoint& i) { return &i; }));
794 }
795 void MSXCPUInterface::removeBreakPoint(unsigned id)
796 {
797  if (auto it = ranges::find(breakPoints, id, &BreakPoint::getId);
798  // could be ==end for a breakpoint that removes itself AND has the -once flag set
799  it != breakPoints.end()) {
800  breakPoints.erase(it);
801  }
802 }
803 
805  std::pair<BreakPoints::const_iterator,
806  BreakPoints::const_iterator> range,
807  MSXMotherBoard& motherBoard)
808 {
809  // create copy for the case that breakpoint/condition removes itself
810  // - keeps object alive by holding a shared_ptr to it
811  // - avoids iterating over a changing collection
812  BreakPoints bpCopy(range.first, range.second);
813  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
814  auto& interp = motherBoard.getReactor().getInterpreter();
815  for (auto& p : bpCopy) {
816  p.checkAndExecute(globalCliComm, interp);
817  if (p.onlyOnce()) {
818  removeBreakPoint(p.getId());
819  }
820  }
821  auto condCopy = conditions;
822  for (auto& c : condCopy) {
823  c.checkAndExecute(globalCliComm, interp);
824  if (c.onlyOnce()) {
825  removeCondition(c.getId());
826  }
827  }
828 }
829 
830 static void registerIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
831 {
832  assert(dynamic_cast<WatchIO*>(&watchPoint));
833  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
834  unsigned beginPort = ioWatch.getBeginAddress();
835  unsigned endPort = ioWatch.getEndAddress();
836  assert(beginPort <= endPort);
837  assert(endPort < 0x100);
838  for (unsigned port = beginPort; port <= endPort; ++port) {
839  ioWatch.getDevice(port).getDevicePtr() = devices[port];
840  devices[port] = &ioWatch.getDevice(port);
841  }
842 }
843 
844 void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
845 {
846  watchPoints.push_back(watchPoint);
847  WatchPoint::Type type = watchPoint->getType();
848  switch (type) {
849  case WatchPoint::READ_IO:
850  registerIOWatch(*watchPoint, IO_In);
851  break;
853  registerIOWatch(*watchPoint, IO_Out);
854  break;
857  updateMemWatch(type);
858  break;
859  default:
860  UNREACHABLE; break;
861  }
862 }
863 
864 static void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
865 {
866  assert(dynamic_cast<WatchIO*>(&watchPoint));
867  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
868  unsigned beginPort = ioWatch.getBeginAddress();
869  unsigned endPort = ioWatch.getEndAddress();
870  assert(beginPort <= endPort);
871  assert(endPort < 0x100);
872 
873  for (unsigned port = beginPort; port <= endPort; ++port) {
874  // find pointer to watchpoint
875  MSXDevice** prev = &devices[port];
876  while (*prev != &ioWatch.getDevice(port)) {
877  prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
878  }
879  // remove watchpoint from chain
880  *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
881  }
882 }
883 
884 void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
885 {
886  // Pass shared_ptr by value to keep the object alive for the duration
887  // of this function, otherwise it gets deleted as soon as it's removed
888  // from the watchPoints collection.
889  if (auto it = ranges::find(watchPoints, watchPoint);
890  it != end(watchPoints)) {
891  // remove before calling updateMemWatch()
892  watchPoints.erase(it);
893  WatchPoint::Type type = watchPoint->getType();
894  switch (type) {
895  case WatchPoint::READ_IO:
896  unregisterIOWatch(*watchPoint, IO_In);
897  break;
899  unregisterIOWatch(*watchPoint, IO_Out);
900  break;
903  updateMemWatch(type);
904  break;
905  default:
906  UNREACHABLE; break;
907  }
908  }
909 }
910 
912 {
913  conditions.push_back(std::move(cond));
914 }
915 
917 {
918  conditions.erase(rfind_unguarded(conditions, &cond,
919  [](auto& e) { return &e; }));
920 }
921 
922 void MSXCPUInterface::removeCondition(unsigned id)
923 {
924  if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
925  // could be ==end for a condition that removes itself AND has the -once flag set
926  it != conditions.end()) {
927  conditions.erase(it);
928  }
929 }
930 
931 void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
932 {
933  std::bitset<CacheLine::SIZE>* watchSet =
934  (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
935  for (auto i : xrange(CacheLine::NUM)) {
936  watchSet[i].reset();
937  }
938  for (auto& w : watchPoints) {
939  if (w->getType() == type) {
940  unsigned beginAddr = w->getBeginAddress();
941  unsigned endAddr = w->getEndAddress();
942  assert(beginAddr <= endAddr);
943  assert(endAddr < 0x10000);
944  for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
945  watchSet[addr >> CacheLine::BITS].set(
946  addr & CacheLine::LOW);
947  }
948  }
949  }
950  for (auto i : xrange(CacheLine::NUM)) {
951  if (readWatchSet [i].any()) {
952  disallowReadCache [i] |= MEMORY_WATCH_BIT;
953  } else {
954  disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
955  }
956  if (writeWatchSet[i].any()) {
957  disallowWriteCache[i] |= MEMORY_WATCH_BIT;
958  } else {
959  disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
960  }
961  }
962  msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
963 }
964 
965 void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
966  unsigned address, unsigned value)
967 {
968  assert(!watchPoints.empty());
969  if (isFastForward()) return;
970 
971  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
972  auto& interp = motherBoard.getReactor().getInterpreter();
973  interp.setVariable(TclObject("wp_last_address"),
974  TclObject(int(address)));
975  if (value != ~0u) {
976  interp.setVariable(TclObject("wp_last_value"),
977  TclObject(int(value)));
978  }
979 
980  auto wpCopy = watchPoints;
981  for (auto& w : wpCopy) {
982  if ((w->getBeginAddress() <= address) &&
983  (w->getEndAddress() >= address) &&
984  (w->getType() == type)) {
985  w->checkAndExecute(globalCliComm, interp);
986  if (w->onlyOnce()) {
987  removeWatchPoint(w);
988  }
989  }
990  }
991 
992  interp.unsetVariable("wp_last_address");
993  interp.unsetVariable("wp_last_value");
994 }
995 
996 
998 {
999  assert(!isFastForward());
1000  if (breaked) return;
1001  breaked = true;
1002  msxcpu.exitCPULoopSync();
1003 
1004  Reactor& reactor = motherBoard.getReactor();
1005  reactor.block();
1006  breakedSetting->setReadOnlyValue(TclObject("true"));
1007  reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1009  Event::create<BreakEvent>());
1010 }
1011 
1013 {
1014  assert(!isFastForward());
1016  TclObject("debug break"), TclObject(), true));
1017  doContinue();
1018 }
1019 
1021 {
1022  assert(!isFastForward());
1023  if (breaked) {
1024  breaked = false;
1025 
1026  Reactor& reactor = motherBoard.getReactor();
1027  breakedSetting->setReadOnlyValue(TclObject("false"));
1028  reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1029  reactor.unblock();
1030  motherBoard.getRealTime().resync();
1031  }
1032 }
1033 
1035 {
1036  // before the Tcl interpreter is destroyed, we must delete all
1037  // TclObjects. Breakpoints and conditions contain such objects
1038  // for the condition and action.
1039  // TODO it would be nicer if breakpoints and conditions were not
1040  // global objects.
1041  breakPoints.clear();
1042  conditions.clear();
1043 }
1044 
1045 
1046 // class MemoryDebug
1047 
1048 MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1049  : SimpleDebuggable(motherBoard_, "memory",
1050  "The memory currently visible for the CPU.", 0x10000)
1051 {
1052 }
1053 
1054 byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1055 {
1056  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1057  return interface.peekMem(address, time);
1058 }
1059 
1060 void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1061  EmuTime::param time)
1062 {
1063  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1064  return interface.writeMem(address, value, time);
1065 }
1066 
1067 
1068 // class SlottedMemoryDebug
1069 
1070 MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1071  MSXMotherBoard& motherBoard_)
1072  : SimpleDebuggable(motherBoard_, "slotted memory",
1073  "The memory in slots and subslots.", 0x10000 * 4 * 4)
1074 {
1075 }
1076 
1077 byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1078 {
1079  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1080  return interface.peekSlottedMem(address, time);
1081 }
1082 
1083 void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1084  EmuTime::param time)
1085 {
1086  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1087  return interface.writeSlottedMem(address, value, time);
1088 }
1089 
1090 
1091 // class SlotInfo
1092 
1093 static unsigned getSlot(
1094  Interpreter& interp, const TclObject& token, const std::string& itemName)
1095 {
1096  unsigned slot = token.getInt(interp);
1097  if (slot >= 4) {
1098  throw CommandException(itemName, " must be in range 0..3");
1099  }
1100  return slot;
1101 }
1102 
1103 MSXCPUInterface::SlotInfo::SlotInfo(
1104  InfoCommand& machineInfoCommand)
1105  : InfoTopic(machineInfoCommand, "slot")
1106 {
1107 }
1108 
1109 void MSXCPUInterface::SlotInfo::execute(span<const TclObject> tokens,
1110  TclObject& result) const
1111 {
1112  checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1113  auto& interp = getInterpreter();
1114  unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1115  unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1116  unsigned page = getSlot(interp, tokens[4], "Page");
1117  auto& interface = OUTER(MSXCPUInterface, slotInfo);
1118  if (!interface.isExpanded(ps)) {
1119  ss = 0;
1120  }
1121  interface.slotLayout[ps][ss][page]->getNameList(result);
1122 }
1123 
1124 std::string MSXCPUInterface::SlotInfo::help(span<const TclObject> /*tokens*/) const
1125 {
1126  return "Retrieve name of the device inserted in given "
1127  "primary slot / secondary slot / page.";
1128 }
1129 
1130 
1131 // class SubSlottedInfo
1132 
1133 MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1134  InfoCommand& machineInfoCommand)
1135  : InfoTopic(machineInfoCommand, "issubslotted")
1136 {
1137 }
1138 
1139 void MSXCPUInterface::SubSlottedInfo::execute(span<const TclObject> tokens,
1140  TclObject& result) const
1141 {
1142  checkNumArgs(tokens, 3, "primary");
1143  auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1144  result = interface.isExpanded(
1145  getSlot(getInterpreter(), tokens[2], "Slot"));
1146 }
1147 
1148 std::string MSXCPUInterface::SubSlottedInfo::help(
1149  span<const TclObject> /*tokens*/) const
1150 {
1151  return "Indicates whether a certain primary slot is expanded.";
1152 }
1153 
1154 
1155 // class ExternalSlotInfo
1156 
1157 MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1158  InfoCommand& machineInfoCommand)
1159  : InfoTopic(machineInfoCommand, "isexternalslot")
1160 {
1161 }
1162 
1163 void MSXCPUInterface::ExternalSlotInfo::execute(
1164  span<const TclObject> tokens, TclObject& result) const
1165 {
1166  checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1167  int ps = 0;
1168  int ss = 0;
1169  auto& interp = getInterpreter();
1170  switch (tokens.size()) {
1171  case 4:
1172  ss = getSlot(interp, tokens[3], "Secondary slot");
1173  // Fall-through
1174  case 3:
1175  ps = getSlot(interp, tokens[2], "Primary slot");
1176  break;
1177  }
1178  auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1179  auto& manager = interface.motherBoard.getSlotManager();
1180  result = manager.isExternalSlot(ps, ss, true);
1181 }
1182 
1183 std::string MSXCPUInterface::ExternalSlotInfo::help(
1184  span<const TclObject> /*tokens*/) const
1185 {
1186  return "Indicates whether a certain slot is external or internal.";
1187 }
1188 
1189 
1190 // class IODebug
1191 
1192 MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1193  : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1194 {
1195 }
1196 
1197 byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1198 {
1199  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1200  return interface.IO_In[address & 0xFF]->peekIO(address, time);
1201 }
1202 
1203 void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1204 {
1205  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1206  interface.writeIO(word(address), value, time);
1207 }
1208 
1209 
1210 // class IOInfo
1211 
1212 MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1213  : InfoTopic(machineInfoCommand, name_)
1214 {
1215 }
1216 
1217 void MSXCPUInterface::IOInfo::helper(
1218  span<const TclObject> tokens, TclObject& result, MSXDevice** devices) const
1219 {
1220  checkNumArgs(tokens, 3, "port");
1221  unsigned port = tokens[2].getInt(getInterpreter());
1222  if (port >= 256) {
1223  throw CommandException("Port must be in range 0..255");
1224  }
1225  devices[port]->getNameList(result);
1226 }
1227 void MSXCPUInterface::IInfo::execute(
1228  span<const TclObject> tokens, TclObject& result) const
1229 {
1230  auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1231  helper(tokens, result, interface.IO_In);
1232 }
1233 void MSXCPUInterface::OInfo::execute(
1234  span<const TclObject> tokens, TclObject& result) const
1235 {
1236  auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1237  helper(tokens, result, interface.IO_Out);
1238 }
1239 
1240 std::string MSXCPUInterface::IOInfo::help(span<const TclObject> /*tokens*/) const
1241 {
1242  return "Return the name of the device connected to the given IO port.";
1243 }
1244 
1245 
1246 template<typename Archive>
1247 void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1248 {
1249  // TODO watchPoints ???
1250 
1251  // primary and 4 secondary slot select registers
1252  byte prim = 0;
1253  if constexpr (!Archive::IS_LOADER) {
1254  for (auto i : xrange(4)) {
1255  prim |= primarySlotState[i] << (2 * i);
1256  }
1257  }
1258  ar.serialize("primarySlots", prim,
1259  "subSlotRegs", subSlotRegister);
1260  if constexpr (Archive::IS_LOADER) {
1261  setPrimarySlots(prim);
1262  for (auto i : xrange(4)) {
1263  setSubSlot(i, subSlotRegister[i]);
1264  }
1265  }
1266 
1267  if (delayDevice) {
1268  ar.serialize("vdpDelay", *delayDevice);
1269  }
1270 }
1272 
1273 } // 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.
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:998
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