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 "MSXCliComm.hh"
12#include "MSXMultiIODevice.hh"
13#include "MSXMultiMemDevice.hh"
14#include "MSXWatchIODevice.hh"
15#include "MSXException.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 "narrow.hh"
25#include "outer.hh"
26#include "ranges.hh"
27#include "stl.hh"
28#include "unreachable.hh"
29#include "xrange.hh"
30#include <array>
31#include <iomanip>
32#include <iostream>
33#include <iterator>
34#include <memory>
35#include <optional>
36
37namespace openmsx {
38
39static std::optional<ReadOnlySetting> breakedSetting;
40static unsigned breakedSettingCount = 0;
41
42
43// Bitfields used in the disallowReadCache and disallowWriteCache arrays
44static constexpr byte SECONDARY_SLOT_BIT = 0x01;
45static constexpr byte MEMORY_WATCH_BIT = 0x02;
46static constexpr byte GLOBAL_RW_BIT = 0x04;
47
48std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>)
49{
50 return os << "CacheLineCounters";
51}
52std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn)
53{
54 std::array<std::string_view, size_t(CacheLineCounters::NUM)> names = {
55 "NonCachedRead",
56 "NonCachedWrite",
57 "GetReadCacheLine",
58 "GetWriteCacheLine",
59 "SlowRead",
60 "SlowWrite",
61 "DisallowCacheRead",
62 "DisallowCacheWrite",
63 "InvalidateAllSlots",
64 "InvalidateReadWrite",
65 "InvalidateRead",
66 "InvalidateWrite",
67 "FillReadWrite",
68 "FillRead",
69 "FillWrite",
70 };
71 return os << names[size_t(evn.e)];
72}
73
75 : memoryDebug (motherBoard_)
76 , slottedMemoryDebug(motherBoard_)
77 , ioDebug (motherBoard_)
78 , slotInfo(motherBoard_.getMachineInfoCommand())
79 , subSlottedInfo(motherBoard_.getMachineInfoCommand())
80 , externalSlotInfo(motherBoard_.getMachineInfoCommand())
81 , inputPortInfo (motherBoard_.getMachineInfoCommand())
82 , outputPortInfo(motherBoard_.getMachineInfoCommand())
83 , dummyDevice(DeviceFactory::createDummyDevice(
84 *motherBoard_.getMachineConfig()))
85 , msxcpu(motherBoard_.getCPU())
86 , cliComm(motherBoard_.getMSXCliComm())
87 , motherBoard(motherBoard_)
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 ranges::fill(disallowReadCache, 0);
104 ranges::fill(disallowWriteCache, 0);
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
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';
159 }
160 if (IO_Out[port] != dummyDevice.get()) {
161 std::cout << "Out-port " << port << " still registered "
162 << IO_Out[port]->getName() << '\n';
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
177void MSXCPUInterface::removeAllWatchPoints()
178{
179 while (!watchPoints.empty()) {
180 removeWatchPoint(watchPoints.back());
181 }
182
183}
184
185byte 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
211void 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 slot-expanders 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 (...) {
307 }
308#endif
309 expanded[ps]--;
310 changeExpanded(isExpanded(primarySlotState[3]));
311}
312
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
325MSXDevice*& 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
362void 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 const 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
391void 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
433static 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
440void MSXCPUInterface::testRegisterSlot(
441 MSXDevice& device, int ps, int ss, unsigned base, unsigned size)
442{
443 auto 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
466void MSXCPUInterface::registerSlot(
467 MSXDevice& device, int ps, int ss, unsigned base, unsigned size)
468{
469 auto page = narrow<byte>(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(narrow<word>(base), size, ps, ss);
492 updateVisible(page);
493}
494
495void MSXCPUInterface::unregisterSlot(
496 MSXDevice& device, int ps, int ss, unsigned base, unsigned size)
497{
498 auto page = narrow<byte>(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(narrow<word>(base), size, ps, ss);
513 updateVisible(page);
514}
515
517 MSXDevice& device, int ps, int ss, unsigned base_, unsigned 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 auto base = base_;
528 auto size = size_;
529 while (size != 0) {
530 auto 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 auto 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, unsigned base, unsigned size)
548{
549 // split range on 16kb borders
550 while (size != 0) {
551 auto 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
606ALWAYS_INLINE void MSXCPUInterface::updateVisible(byte page, byte ps, byte 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}
614void MSXCPUInterface::updateVisible(byte page)
615{
616 updateVisible(page, primarySlotState[page], secondarySlotState[page]);
617}
618
619void MSXCPUInterface::invalidateRWCache(word start, unsigned size, int ps, int ss)
620{
622 msxcpu.invalidateRWCache(start, size, ps, ss, disallowReadCache, disallowWriteCache);
623}
624void MSXCPUInterface::invalidateRCache (word start, unsigned size, int ps, int ss)
625{
627 msxcpu.invalidateRCache(start, size, ps, ss, disallowReadCache, disallowWriteCache);
628}
629void MSXCPUInterface::invalidateWCache (word start, unsigned size, int ps, int ss)
630{
632 msxcpu.invalidateWCache(start, size, ps, ss, disallowReadCache, disallowWriteCache);
633}
634
635void 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}
640void MSXCPUInterface::fillRCache(unsigned start, unsigned size, const byte* rData, int ps, int ss)
641{
643 msxcpu.fillRCache(start, size, rData, ps, ss, disallowReadCache, disallowWriteCache);
644}
645void MSXCPUInterface::fillWCache(unsigned start, unsigned size, byte* wData, int ps, int ss)
646{
648 msxcpu.fillWCache(start, size, wData, ps, ss, disallowReadCache, disallowWriteCache);
649}
650
652{
653 for (auto i : xrange(byte(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 byte ps0 = (value >> 0) & 3;
679 if (primarySlotState[0] != ps0) [[unlikely]] {
680 primarySlotState[0] = ps0;
681 byte ss0 = (subSlotRegister[ps0] >> 0) & 3;
682 secondarySlotState[0] = ss0;
683 updateVisible(0, ps0, ss0);
684 }
685 byte ps1 = (value >> 2) & 3;
686 if (primarySlotState[1] != ps1) [[unlikely]] {
687 primarySlotState[1] = ps1;
688 byte ss1 = (subSlotRegister[ps1] >> 2) & 3;
689 secondarySlotState[1] = ss1;
690 updateVisible(1, ps1, ss1);
691 }
692 byte ps2 = (value >> 4) & 3;
693 if (primarySlotState[2] != ps2) [[unlikely]] {
694 primarySlotState[2] = ps2;
695 byte ss2 = (subSlotRegister[ps2] >> 4) & 3;
696 secondarySlotState[2] = ss2;
697 updateVisible(2, ps2, ss2);
698 }
699 byte 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 byte 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
713void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
714{
715 subSlotRegister[primSlot] = value;
716 for (byte 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
725byte 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
734byte 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
751byte 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
768void 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}
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
835static void registerIOWatch(WatchPoint& watchPoint, std::span<MSXDevice*, 256> devices)
836{
837 auto& ioWatch = checked_cast<WatchIO&>(watchPoint);
838 unsigned beginPort = ioWatch.getBeginAddress();
839 unsigned endPort = ioWatch.getEndAddress();
840 assert(beginPort <= endPort);
841 assert(endPort < 0x100);
842 for (unsigned port = beginPort; port <= endPort; ++port) {
843 ioWatch.getDevice(narrow_cast<byte>(port)).getDevicePtr() = devices[port];
844 devices[port] = &ioWatch.getDevice(narrow_cast<byte>(port));
845 }
846}
847
848void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
849{
850 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "add");
851 watchPoints.push_back(watchPoint);
852 WatchPoint::Type type = watchPoint->getType();
853 switch (type) {
855 registerIOWatch(*watchPoint, IO_In);
856 break;
858 registerIOWatch(*watchPoint, IO_Out);
859 break;
862 updateMemWatch(type);
863 break;
864 default:
865 UNREACHABLE; break;
866 }
867}
868
869static void unregisterIOWatch(WatchPoint& watchPoint, std::span<MSXDevice*, 256> devices)
870{
871 auto& ioWatch = checked_cast<WatchIO&>(watchPoint);
872 unsigned beginPort = ioWatch.getBeginAddress();
873 unsigned endPort = ioWatch.getEndAddress();
874 assert(beginPort <= endPort);
875 assert(endPort < 0x100);
876
877 for (unsigned port = beginPort; port <= endPort; ++port) {
878 // find pointer to watchpoint
879 MSXDevice** prev = &devices[port];
880 while (*prev != &ioWatch.getDevice(narrow_cast<byte>(port))) {
881 prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
882 }
883 // remove watchpoint from chain
884 *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
885 }
886}
887
888void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
889{
890 // Pass shared_ptr by value to keep the object alive for the duration
891 // of this function, otherwise it gets deleted as soon as it's removed
892 // from the watchPoints collection.
893 if (auto it = ranges::find(watchPoints, watchPoint);
894 it != end(watchPoints)) {
895 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "remove");
896 // remove before calling updateMemWatch()
897 watchPoints.erase(it);
898 WatchPoint::Type type = watchPoint->getType();
899 switch (type) {
901 unregisterIOWatch(*watchPoint, IO_In);
902 break;
904 unregisterIOWatch(*watchPoint, IO_Out);
905 break;
908 updateMemWatch(type);
909 break;
910 default:
911 UNREACHABLE; break;
912 }
913 }
914}
915
917{
918 if (auto it = ranges::find(watchPoints, id, &WatchPoint::getId);
919 it != watchPoints.end()) {
920 removeWatchPoint(*it); // not efficient, does a 2nd search, but good enough
921 }
922}
923
925{
926 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "add");
927 conditions.push_back(std::move(cond));
928}
929
931{
932 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "remove");
933 conditions.erase(rfind_unguarded(conditions, &cond,
934 [](auto& e) { return &e; }));
935}
936
938{
939 if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
940 // could be ==end for a condition that removes itself AND has the -once flag set
941 it != conditions.end()) {
942 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", it->getId()), "remove");
943 conditions.erase(it);
944 }
945}
946
947void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
948{
949 std::span<std::bitset<CacheLine::SIZE>, CacheLine::NUM> watchSet =
950 (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
951 for (auto i : xrange(CacheLine::NUM)) {
952 watchSet[i].reset();
953 }
954 for (auto& w : watchPoints) {
955 if (w->getType() == type) {
956 unsigned beginAddr = w->getBeginAddress();
957 unsigned endAddr = w->getEndAddress();
958 assert(beginAddr <= endAddr);
959 assert(endAddr < 0x10000);
960 for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
961 watchSet[addr >> CacheLine::BITS].set(
962 addr & CacheLine::LOW);
963 }
964 }
965 }
966 for (auto i : xrange(CacheLine::NUM)) {
967 if (readWatchSet [i].any()) {
968 disallowReadCache [i] |= MEMORY_WATCH_BIT;
969 } else {
970 disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
971 }
972 if (writeWatchSet[i].any()) {
973 disallowWriteCache[i] |= MEMORY_WATCH_BIT;
974 } else {
975 disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
976 }
977 }
978 msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
979}
980
981void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
982 unsigned address, unsigned value)
983{
984 assert(!watchPoints.empty());
985 if (isFastForward()) return;
986
987 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
988 auto& interp = motherBoard.getReactor().getInterpreter();
989 interp.setVariable(TclObject("wp_last_address"),
990 TclObject(int(address)));
991 if (value != ~0u) {
992 interp.setVariable(TclObject("wp_last_value"),
993 TclObject(int(value)));
994 }
995
996 auto wpCopy = watchPoints;
997 for (auto& w : wpCopy) {
998 if ((w->getBeginAddress() <= address) &&
999 (w->getEndAddress() >= address) &&
1000 (w->getType() == type)) {
1001 bool remove = w->checkAndExecute(globalCliComm, interp);
1002 if (remove) {
1004 }
1005 }
1006 }
1007
1008 interp.unsetVariable("wp_last_address");
1009 interp.unsetVariable("wp_last_value");
1010}
1011
1012
1014{
1015 assert(!isFastForward());
1016 if (breaked) return;
1017 breaked = true;
1018 msxcpu.exitCPULoopSync();
1019
1020 Reactor& reactor = motherBoard.getReactor();
1021 reactor.block();
1022 breakedSetting->setReadOnlyValue(TclObject("true"));
1023 reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1025}
1026
1028{
1029 assert(!isFastForward());
1031 TclObject("debug break"), TclObject(), true));
1032 doContinue();
1033}
1034
1036{
1037 assert(!isFastForward());
1038 if (breaked) {
1039 breaked = false;
1040
1041 Reactor& reactor = motherBoard.getReactor();
1042 breakedSetting->setReadOnlyValue(TclObject("false"));
1043 reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1044 reactor.unblock();
1045 motherBoard.getRealTime().resync();
1046 }
1047}
1048
1050{
1051 // before the Tcl interpreter is destroyed, we must delete all
1052 // TclObjects. Breakpoints and conditions contain such objects
1053 // for the condition and action.
1054 // TODO it would be nicer if breakpoints and conditions were not
1055 // global objects.
1056 breakPoints.clear();
1057 conditions.clear();
1058}
1059
1061{
1062 assert(0 <= ps && ps < 4);
1063 assert(0 <= ss && ss < 4);
1064 assert(0 <= page && page < 4);
1065 return slotLayout[ps][ss][page];
1066}
1067
1068// class MemoryDebug
1069
1070MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1071 : SimpleDebuggable(motherBoard_, "memory",
1072 "The memory currently visible for the CPU.", 0x10000)
1073{
1074}
1075
1076byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1077{
1078 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1079 return interface.peekMem(narrow<word>(address), time);
1080}
1081
1082void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1083 EmuTime::param time)
1084{
1085 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1086 return interface.writeMem(narrow<word>(address), value, time);
1087}
1088
1089
1090// class SlottedMemoryDebug
1091
1092MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1093 MSXMotherBoard& motherBoard_)
1094 : SimpleDebuggable(motherBoard_, "slotted memory",
1095 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1096{
1097}
1098
1099byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1100{
1101 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1102 return interface.peekSlottedMem(address, time);
1103}
1104
1105void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1106 EmuTime::param time)
1107{
1108 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1109 return interface.writeSlottedMem(address, value, time);
1110}
1111
1112
1113// class SlotInfo
1114
1115static unsigned getSlot(
1116 Interpreter& interp, const TclObject& token, const std::string& itemName)
1117{
1118 unsigned slot = token.getInt(interp);
1119 if (slot >= 4) {
1120 throw CommandException(itemName, " must be in range 0..3");
1121 }
1122 return slot;
1123}
1124
1125MSXCPUInterface::SlotInfo::SlotInfo(
1126 InfoCommand& machineInfoCommand)
1127 : InfoTopic(machineInfoCommand, "slot")
1128{
1129}
1130
1131void MSXCPUInterface::SlotInfo::execute(std::span<const TclObject> tokens,
1132 TclObject& result) const
1133{
1134 checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1135 auto& interp = getInterpreter();
1136 unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1137 unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1138 unsigned page = getSlot(interp, tokens[4], "Page");
1139 auto& interface = OUTER(MSXCPUInterface, slotInfo);
1140 if (!interface.isExpanded(narrow<int>(ps))) {
1141 ss = 0;
1142 }
1143 interface.slotLayout[ps][ss][page]->getNameList(result);
1144}
1145
1146std::string MSXCPUInterface::SlotInfo::help(std::span<const TclObject> /*tokens*/) const
1147{
1148 return "Retrieve name of the device inserted in given "
1149 "primary slot / secondary slot / page.";
1150}
1151
1152
1153// class SubSlottedInfo
1154
1155MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1156 InfoCommand& machineInfoCommand)
1157 : InfoTopic(machineInfoCommand, "issubslotted")
1158{
1159}
1160
1161void MSXCPUInterface::SubSlottedInfo::execute(std::span<const TclObject> tokens,
1162 TclObject& result) const
1163{
1164 checkNumArgs(tokens, 3, "primary");
1165 auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1166 result = interface.isExpanded(narrow<int>(
1167 getSlot(getInterpreter(), tokens[2], "Slot")));
1168}
1169
1170std::string MSXCPUInterface::SubSlottedInfo::help(
1171 std::span<const TclObject> /*tokens*/) const
1172{
1173 return "Indicates whether a certain primary slot is expanded.";
1174}
1175
1176
1177// class ExternalSlotInfo
1178
1179MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1180 InfoCommand& machineInfoCommand)
1181 : InfoTopic(machineInfoCommand, "isexternalslot")
1182{
1183}
1184
1185void MSXCPUInterface::ExternalSlotInfo::execute(
1186 std::span<const TclObject> tokens, TclObject& result) const
1187{
1188 checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1189 int ps = 0;
1190 int ss = 0;
1191 auto& interp = getInterpreter();
1192 switch (tokens.size()) {
1193 case 4:
1194 ss = narrow<int>(getSlot(interp, tokens[3], "Secondary slot"));
1195 // Fall-through
1196 case 3:
1197 ps = narrow<int>(getSlot(interp, tokens[2], "Primary slot"));
1198 break;
1199 }
1200 auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1201 auto& manager = interface.motherBoard.getSlotManager();
1202 result = manager.isExternalSlot(ps, ss, true);
1203}
1204
1205std::string MSXCPUInterface::ExternalSlotInfo::help(
1206 std::span<const TclObject> /*tokens*/) const
1207{
1208 return "Indicates whether a certain slot is external or internal.";
1209}
1210
1211
1212// class IODebug
1213
1214MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1215 : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1216{
1217}
1218
1219byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1220{
1221 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1222 return interface.IO_In[address & 0xFF]->peekIO(narrow<word>(address), time);
1223}
1224
1225void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1226{
1227 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1228 interface.writeIO(word(address), value, time);
1229}
1230
1231
1232// class IOInfo
1233
1234MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1235 : InfoTopic(machineInfoCommand, name_)
1236{
1237}
1238
1239void MSXCPUInterface::IOInfo::helper(
1240 std::span<const TclObject> tokens, TclObject& result, std::span<MSXDevice*, 256> devices) const
1241{
1242 checkNumArgs(tokens, 3, "port");
1243 unsigned port = tokens[2].getInt(getInterpreter());
1244 if (port >= 256) {
1245 throw CommandException("Port must be in range 0..255");
1246 }
1247 devices[port]->getNameList(result);
1248}
1249void MSXCPUInterface::IInfo::execute(
1250 std::span<const TclObject> tokens, TclObject& result) const
1251{
1252 auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1253 helper(tokens, result, interface.IO_In);
1254}
1255void MSXCPUInterface::OInfo::execute(
1256 std::span<const TclObject> tokens, TclObject& result) const
1257{
1258 auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1259 helper(tokens, result, interface.IO_Out);
1260}
1261
1262std::string MSXCPUInterface::IOInfo::help(std::span<const TclObject> /*tokens*/) const
1263{
1264 return "Return the name of the device connected to the given IO port.";
1265}
1266
1267
1268template<typename Archive>
1269void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1270{
1271 // TODO watchPoints ???
1272
1273 // primary and 4 secondary slot select registers
1274 byte prim = 0;
1275 if constexpr (!Archive::IS_LOADER) {
1276 for (auto i : xrange(4)) {
1277 prim |= byte(primarySlotState[i] << (2 * i));
1278 }
1279 }
1280 ar.serialize("primarySlots", prim,
1281 "subSlotRegs", subSlotRegister);
1282 if constexpr (Archive::IS_LOADER) {
1283 setPrimarySlots(prim);
1284 for (auto i : xrange(byte(4))) {
1285 setSubSlot(i, subSlotRegister[i]);
1286 }
1287 }
1288
1289 if (delayDevice) {
1290 ar.serialize("vdpDelay", *delayDevice);
1291 }
1292}
1294
1295} // 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 specific 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:250
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.
MSXDevice * getMSXDevice(int ps, int ss, int page)
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)
void unregisterMemDevice(MSXDevice &device, int ps, int ss, unsigned base, unsigned size)
void removeCondition(const DebugCondition &cond)
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
void testUnsetExpanded(int ps, std::span< const std::unique_ptr< MSXDevice > > allowed) const
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 invalidateWCache(word start, unsigned size, int ps, int ss)
byte readIRQVector()
CPU uses this method to read 'extra' data from the data bus used in interrupt routines.
bool checkBreakPoints(unsigned pc)
void serialize(Archive &ar, unsigned version)
void registerGlobalRead(MSXDevice &device, word address)
(Un)register global read.
byte readSlottedMem(unsigned address, EmuTime::param time)
void registerMemDevice(MSXDevice &device, int ps, int ss, unsigned base, unsigned size)
Devices can register themself in the MSX slot structure.
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 invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:181
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Definition: MSXCPU.cc:282
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
Definition: MSXCPU.cc:160
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Definition: MSXCPU.cc:276
void fillRWCache(unsigned start, unsigned size, const byte *rData, byte *wData, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Fill the read and write cache lines for a specific slot with the specified value.
Definition: MSXCPU.cc:270
void invalidateRCache(unsigned start, unsigned size, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Definition: MSXCPU.cc:255
void exitCPULoopSync()
See CPUCore::exitCPULoopSync()
Definition: MSXCPU.cc:126
void invalidateRWCache(unsigned start, unsigned size, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Similar to the method above, but only invalidates one specific slot.
Definition: MSXCPU.cc:247
void setInterface(MSXCPUInterface *interface)
Definition: MSXCPU.cc:73
void invalidateWCache(unsigned start, unsigned size, int ps, int ss, std::span< const byte, 256 > disallowRead, std::span< const byte, 256 > disallowWrite)
Definition: MSXCPU.cc:262
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:36
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:47
virtual const std::string & getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:375
const HardwareConfig * getMachineConfig() const
Contains the main loop of openMSX.
Definition: Reactor.hh:72
CommandController & getCommandController()
Definition: Reactor.cc:329
void unblock()
Definition: Reactor.cc:636
CliComm & getCliComm()
Definition: Reactor.cc:319
GlobalCliComm & getGlobalCliComm()
Definition: Reactor.hh:87
Interpreter & getInterpreter()
Definition: Reactor.cc:324
EventDistributor & getEventDistributor()
Definition: Reactor.hh:86
Base class for CPU breakpoints.
Definition: WatchPoint.hh:14
unsigned getId() const
Definition: WatchPoint.hh:32
#define ALWAYS_INLINE
Definition: inline.hh:16
constexpr double e
Definition: Math.hh:21
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:267
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
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
@ DisallowCacheWrite
@ InvalidateReadWrite
std::ostream & operator<<(std::ostream &os, EnumTypeName< CacheLineCounters >)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:305
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:281
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:160
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:124
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:133
size_t size(std::string_view utf8)
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
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
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:693
std::string strCat(Ts &&...ts)
Definition: strCat.hh:625
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:703
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:132
constexpr auto end(const zstring_view &x)