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