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 (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
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 (g.addr == address) [[unlikely]] {
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 ((address == 0xFFFF) && isExpanded(primarySlotState[3])) [[unlikely]] {
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 ((address == 0xFFFF) && isExpanded(primarySlotState[3])) [[unlikely]] {
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 (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
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 (g.addr == address) [[unlikely]] {
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  std::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 {
303  std::span<const std::unique_ptr<MSXDevice>> dummy;
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 (primarySlotState[0] != ps0) [[unlikely]] {
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 (primarySlotState[1] != ps1) [[unlikely]] {
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 (primarySlotState[2] != ps2) [[unlikely]] {
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 (primarySlotState[3] != ps3) [[unlikely]] {
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 (oldExpanded != newExpanded) [[unlikely]] {
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  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", bp.getId()), "add");
789  auto it = ranges::upper_bound(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
790  breakPoints.insert(it, std::move(bp));
791 }
792 
794 {
795  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", bp.getId()), "remove");
796  auto [first, last] = ranges::equal_range(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
797  breakPoints.erase(find_unguarded(first, last, &bp,
798  [](const BreakPoint& i) { return &i; }));
799 }
800 void MSXCPUInterface::removeBreakPoint(unsigned id)
801 {
802  if (auto it = ranges::find(breakPoints, id, &BreakPoint::getId);
803  // could be ==end for a breakpoint that removes itself AND has the -once flag set
804  it != breakPoints.end()) {
805  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", it->getId()), "remove");
806  breakPoints.erase(it);
807  }
808 }
809 
811  std::pair<BreakPoints::const_iterator,
812  BreakPoints::const_iterator> range)
813 {
814  // create copy for the case that breakpoint/condition removes itself
815  // - keeps object alive by holding a shared_ptr to it
816  // - avoids iterating over a changing collection
817  BreakPoints bpCopy(range.first, range.second);
818  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
819  auto& interp = motherBoard.getReactor().getInterpreter();
820  for (auto& p : bpCopy) {
821  bool remove = p.checkAndExecute(globalCliComm, interp);
822  if (remove) {
823  removeBreakPoint(p.getId());
824  }
825  }
826  auto condCopy = conditions;
827  for (auto& c : condCopy) {
828  bool remove = c.checkAndExecute(globalCliComm, interp);
829  if (remove) {
830  removeCondition(c.getId());
831  }
832  }
833 }
834 
835 static void registerIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
836 {
837  assert(dynamic_cast<WatchIO*>(&watchPoint));
838  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
839  unsigned beginPort = ioWatch.getBeginAddress();
840  unsigned endPort = ioWatch.getEndAddress();
841  assert(beginPort <= endPort);
842  assert(endPort < 0x100);
843  for (unsigned port = beginPort; port <= endPort; ++port) {
844  ioWatch.getDevice(port).getDevicePtr() = devices[port];
845  devices[port] = &ioWatch.getDevice(port);
846  }
847 }
848 
849 void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
850 {
851  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "add");
852  watchPoints.push_back(watchPoint);
853  WatchPoint::Type type = watchPoint->getType();
854  switch (type) {
855  case WatchPoint::READ_IO:
856  registerIOWatch(*watchPoint, IO_In);
857  break;
859  registerIOWatch(*watchPoint, IO_Out);
860  break;
863  updateMemWatch(type);
864  break;
865  default:
866  UNREACHABLE; break;
867  }
868 }
869 
870 static void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
871 {
872  assert(dynamic_cast<WatchIO*>(&watchPoint));
873  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
874  unsigned beginPort = ioWatch.getBeginAddress();
875  unsigned endPort = ioWatch.getEndAddress();
876  assert(beginPort <= endPort);
877  assert(endPort < 0x100);
878 
879  for (unsigned port = beginPort; port <= endPort; ++port) {
880  // find pointer to watchpoint
881  MSXDevice** prev = &devices[port];
882  while (*prev != &ioWatch.getDevice(port)) {
883  prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
884  }
885  // remove watchpoint from chain
886  *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
887  }
888 }
889 
890 void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
891 {
892  // Pass shared_ptr by value to keep the object alive for the duration
893  // of this function, otherwise it gets deleted as soon as it's removed
894  // from the watchPoints collection.
895  if (auto it = ranges::find(watchPoints, watchPoint);
896  it != end(watchPoints)) {
897  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "remove");
898  // remove before calling updateMemWatch()
899  watchPoints.erase(it);
900  WatchPoint::Type type = watchPoint->getType();
901  switch (type) {
902  case WatchPoint::READ_IO:
903  unregisterIOWatch(*watchPoint, IO_In);
904  break;
906  unregisterIOWatch(*watchPoint, IO_Out);
907  break;
910  updateMemWatch(type);
911  break;
912  default:
913  UNREACHABLE; break;
914  }
915  }
916 }
917 
919 {
920  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "add");
921  conditions.push_back(std::move(cond));
922 }
923 
925 {
926  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "remove");
927  conditions.erase(rfind_unguarded(conditions, &cond,
928  [](auto& e) { return &e; }));
929 }
930 
931 void MSXCPUInterface::removeCondition(unsigned id)
932 {
933  if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
934  // could be ==end for a condition that removes itself AND has the -once flag set
935  it != conditions.end()) {
936  cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", it->getId()), "remove");
937  conditions.erase(it);
938  }
939 }
940 
941 void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
942 {
943  std::bitset<CacheLine::SIZE>* watchSet =
944  (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
945  for (auto i : xrange(CacheLine::NUM)) {
946  watchSet[i].reset();
947  }
948  for (auto& w : watchPoints) {
949  if (w->getType() == type) {
950  unsigned beginAddr = w->getBeginAddress();
951  unsigned endAddr = w->getEndAddress();
952  assert(beginAddr <= endAddr);
953  assert(endAddr < 0x10000);
954  for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
955  watchSet[addr >> CacheLine::BITS].set(
956  addr & CacheLine::LOW);
957  }
958  }
959  }
960  for (auto i : xrange(CacheLine::NUM)) {
961  if (readWatchSet [i].any()) {
962  disallowReadCache [i] |= MEMORY_WATCH_BIT;
963  } else {
964  disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
965  }
966  if (writeWatchSet[i].any()) {
967  disallowWriteCache[i] |= MEMORY_WATCH_BIT;
968  } else {
969  disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
970  }
971  }
972  msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
973 }
974 
975 void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
976  unsigned address, unsigned value)
977 {
978  assert(!watchPoints.empty());
979  if (isFastForward()) return;
980 
981  auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
982  auto& interp = motherBoard.getReactor().getInterpreter();
983  interp.setVariable(TclObject("wp_last_address"),
984  TclObject(int(address)));
985  if (value != ~0u) {
986  interp.setVariable(TclObject("wp_last_value"),
987  TclObject(int(value)));
988  }
989 
990  auto wpCopy = watchPoints;
991  for (auto& w : wpCopy) {
992  if ((w->getBeginAddress() <= address) &&
993  (w->getEndAddress() >= address) &&
994  (w->getType() == type)) {
995  bool remove = w->checkAndExecute(globalCliComm, interp);
996  if (remove) {
997  removeWatchPoint(w);
998  }
999  }
1000  }
1001 
1002  interp.unsetVariable("wp_last_address");
1003  interp.unsetVariable("wp_last_value");
1004 }
1005 
1006 
1008 {
1009  assert(!isFastForward());
1010  if (breaked) return;
1011  breaked = true;
1012  msxcpu.exitCPULoopSync();
1013 
1014  Reactor& reactor = motherBoard.getReactor();
1015  reactor.block();
1016  breakedSetting->setReadOnlyValue(TclObject("true"));
1017  reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1019  Event::create<BreakEvent>());
1020 }
1021 
1023 {
1024  assert(!isFastForward());
1026  TclObject("debug break"), TclObject(), true));
1027  doContinue();
1028 }
1029 
1031 {
1032  assert(!isFastForward());
1033  if (breaked) {
1034  breaked = false;
1035 
1036  Reactor& reactor = motherBoard.getReactor();
1037  breakedSetting->setReadOnlyValue(TclObject("false"));
1038  reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1039  reactor.unblock();
1040  motherBoard.getRealTime().resync();
1041  }
1042 }
1043 
1045 {
1046  // before the Tcl interpreter is destroyed, we must delete all
1047  // TclObjects. Breakpoints and conditions contain such objects
1048  // for the condition and action.
1049  // TODO it would be nicer if breakpoints and conditions were not
1050  // global objects.
1051  breakPoints.clear();
1052  conditions.clear();
1053 }
1054 
1055 
1056 // class MemoryDebug
1057 
1058 MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1059  : SimpleDebuggable(motherBoard_, "memory",
1060  "The memory currently visible for the CPU.", 0x10000)
1061 {
1062 }
1063 
1064 byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1065 {
1066  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1067  return interface.peekMem(address, time);
1068 }
1069 
1070 void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1071  EmuTime::param time)
1072 {
1073  auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1074  return interface.writeMem(address, value, time);
1075 }
1076 
1077 
1078 // class SlottedMemoryDebug
1079 
1080 MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1081  MSXMotherBoard& motherBoard_)
1082  : SimpleDebuggable(motherBoard_, "slotted memory",
1083  "The memory in slots and subslots.", 0x10000 * 4 * 4)
1084 {
1085 }
1086 
1087 byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1088 {
1089  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1090  return interface.peekSlottedMem(address, time);
1091 }
1092 
1093 void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1094  EmuTime::param time)
1095 {
1096  auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1097  return interface.writeSlottedMem(address, value, time);
1098 }
1099 
1100 
1101 // class SlotInfo
1102 
1103 static unsigned getSlot(
1104  Interpreter& interp, const TclObject& token, const std::string& itemName)
1105 {
1106  unsigned slot = token.getInt(interp);
1107  if (slot >= 4) {
1108  throw CommandException(itemName, " must be in range 0..3");
1109  }
1110  return slot;
1111 }
1112 
1113 MSXCPUInterface::SlotInfo::SlotInfo(
1114  InfoCommand& machineInfoCommand)
1115  : InfoTopic(machineInfoCommand, "slot")
1116 {
1117 }
1118 
1119 void MSXCPUInterface::SlotInfo::execute(std::span<const TclObject> tokens,
1120  TclObject& result) const
1121 {
1122  checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1123  auto& interp = getInterpreter();
1124  unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1125  unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1126  unsigned page = getSlot(interp, tokens[4], "Page");
1127  auto& interface = OUTER(MSXCPUInterface, slotInfo);
1128  if (!interface.isExpanded(ps)) {
1129  ss = 0;
1130  }
1131  interface.slotLayout[ps][ss][page]->getNameList(result);
1132 }
1133 
1134 std::string MSXCPUInterface::SlotInfo::help(std::span<const TclObject> /*tokens*/) const
1135 {
1136  return "Retrieve name of the device inserted in given "
1137  "primary slot / secondary slot / page.";
1138 }
1139 
1140 
1141 // class SubSlottedInfo
1142 
1143 MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1144  InfoCommand& machineInfoCommand)
1145  : InfoTopic(machineInfoCommand, "issubslotted")
1146 {
1147 }
1148 
1149 void MSXCPUInterface::SubSlottedInfo::execute(std::span<const TclObject> tokens,
1150  TclObject& result) const
1151 {
1152  checkNumArgs(tokens, 3, "primary");
1153  auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1154  result = interface.isExpanded(
1155  getSlot(getInterpreter(), tokens[2], "Slot"));
1156 }
1157 
1158 std::string MSXCPUInterface::SubSlottedInfo::help(
1159  std::span<const TclObject> /*tokens*/) const
1160 {
1161  return "Indicates whether a certain primary slot is expanded.";
1162 }
1163 
1164 
1165 // class ExternalSlotInfo
1166 
1167 MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1168  InfoCommand& machineInfoCommand)
1169  : InfoTopic(machineInfoCommand, "isexternalslot")
1170 {
1171 }
1172 
1173 void MSXCPUInterface::ExternalSlotInfo::execute(
1174  std::span<const TclObject> tokens, TclObject& result) const
1175 {
1176  checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1177  int ps = 0;
1178  int ss = 0;
1179  auto& interp = getInterpreter();
1180  switch (tokens.size()) {
1181  case 4:
1182  ss = getSlot(interp, tokens[3], "Secondary slot");
1183  // Fall-through
1184  case 3:
1185  ps = getSlot(interp, tokens[2], "Primary slot");
1186  break;
1187  }
1188  auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1189  auto& manager = interface.motherBoard.getSlotManager();
1190  result = manager.isExternalSlot(ps, ss, true);
1191 }
1192 
1193 std::string MSXCPUInterface::ExternalSlotInfo::help(
1194  std::span<const TclObject> /*tokens*/) const
1195 {
1196  return "Indicates whether a certain slot is external or internal.";
1197 }
1198 
1199 
1200 // class IODebug
1201 
1202 MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1203  : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1204 {
1205 }
1206 
1207 byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1208 {
1209  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1210  return interface.IO_In[address & 0xFF]->peekIO(address, time);
1211 }
1212 
1213 void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1214 {
1215  auto& interface = OUTER(MSXCPUInterface, ioDebug);
1216  interface.writeIO(word(address), value, time);
1217 }
1218 
1219 
1220 // class IOInfo
1221 
1222 MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1223  : InfoTopic(machineInfoCommand, name_)
1224 {
1225 }
1226 
1227 void MSXCPUInterface::IOInfo::helper(
1228  std::span<const TclObject> tokens, TclObject& result, MSXDevice** devices) const
1229 {
1230  checkNumArgs(tokens, 3, "port");
1231  unsigned port = tokens[2].getInt(getInterpreter());
1232  if (port >= 256) {
1233  throw CommandException("Port must be in range 0..255");
1234  }
1235  devices[port]->getNameList(result);
1236 }
1237 void MSXCPUInterface::IInfo::execute(
1238  std::span<const TclObject> tokens, TclObject& result) const
1239 {
1240  auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1241  helper(tokens, result, interface.IO_In);
1242 }
1243 void MSXCPUInterface::OInfo::execute(
1244  std::span<const TclObject> tokens, TclObject& result) const
1245 {
1246  auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1247  helper(tokens, result, interface.IO_Out);
1248 }
1249 
1250 std::string MSXCPUInterface::IOInfo::help(std::span<const TclObject> /*tokens*/) const
1251 {
1252  return "Return the name of the device connected to the given IO port.";
1253 }
1254 
1255 
1256 template<typename Archive>
1257 void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1258 {
1259  // TODO watchPoints ???
1260 
1261  // primary and 4 secondary slot select registers
1262  byte prim = 0;
1263  if constexpr (!Archive::IS_LOADER) {
1264  for (auto i : xrange(4)) {
1265  prim |= primarySlotState[i] << (2 * i);
1266  }
1267  }
1268  ar.serialize("primarySlots", prim,
1269  "subSlotRegs", subSlotRegister);
1270  if constexpr (Archive::IS_LOADER) {
1271  setPrimarySlots(prim);
1272  for (auto i : xrange(4)) {
1273  setSubSlot(i, subSlotRegister[i]);
1274  }
1275  }
1276 
1277  if (delayDevice) {
1278  ar.serialize("vdpDelay", *delayDevice);
1279  }
1280 }
1282 
1283 } // 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 setVariable(const TclObject &name, const TclObject &value)
Definition: Interpreter.cc:249
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)
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 testUnsetExpanded(int ps, std::span< const std::unique_ptr< MSXDevice >> allowed) const
void unregister_IO_In(byte port, MSXDevice *device)
bool replace_IO_Out(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
void changeExpanded(bool newExpanded)
void removeCondition(const DebugCondition &cond)
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
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)
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.
bool checkBreakPoints(unsigned pc)
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)
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
GlobalCliComm & getGlobalCliComm()
Definition: Reactor.hh:83
void unblock()
Definition: Reactor.cc:602
CliComm & getCliComm()
Definition: Reactor.cc:312
Interpreter & getInterpreter()
Definition: Reactor.cc:317
Base class for CPU breakpoints.
Definition: WatchPoint.hh:14
unsigned getBeginAddress() const
Definition: WatchPoint.hh:34
#define ALWAYS_INLINE
Definition: inline.hh:16
constexpr double e
Definition: Math.hh:18
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:258
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
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:256
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:232
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:144
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:108
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:117
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:63
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:125
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:100
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:23
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617
std::string strCat(Ts &&...ts)
Definition: strCat.hh:549
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:627
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:133
constexpr auto end(const zstring_view &x)