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::Type::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::Type::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, const MSXDevice& dev1, const 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 const 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 if (byte ps0 = (value >> 0) & 3; primarySlotState[0] != ps0) [[unlikely]] {
691 primarySlotState[0] = ps0;
692 byte ss0 = (subSlotRegister[ps0] >> 0) & 3;
693 secondarySlotState[0] = ss0;
694 updateVisible(0, ps0, ss0);
695 }
696 if (byte ps1 = (value >> 2) & 3; primarySlotState[1] != ps1) [[unlikely]] {
697 primarySlotState[1] = ps1;
698 byte ss1 = (subSlotRegister[ps1] >> 2) & 3;
699 secondarySlotState[1] = ss1;
700 updateVisible(1, ps1, ss1);
701 }
702 if (byte ps2 = (value >> 4) & 3; primarySlotState[2] != ps2) [[unlikely]] {
703 primarySlotState[2] = ps2;
704 byte ss2 = (subSlotRegister[ps2] >> 4) & 3;
705 secondarySlotState[2] = ss2;
706 updateVisible(2, ps2, ss2);
707 }
708 if (byte ps3 = (value >> 6) & 3; primarySlotState[3] != ps3) [[unlikely]] {
709 bool oldExpanded = isExpanded(primarySlotState[3]);
710 bool newExpanded = isExpanded(ps3);
711 primarySlotState[3] = ps3;
712 byte ss3 = (subSlotRegister[ps3] >> 6) & 3;
713 secondarySlotState[3] = ss3;
714 updateVisible(3, ps3, ss3);
715 if (oldExpanded != newExpanded) [[unlikely]] {
716 changeExpanded(newExpanded);
717 }
718 }
719}
720
721void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
722{
723 subSlotRegister[primSlot] = value;
724 for (byte page = 0; page < 4; ++page, value >>= 2) {
725 if (primSlot == primarySlotState[page]) {
726 secondarySlotState[page] = value & 3;
727 // Change the visible devices
728 updateVisible(page);
729 }
730 }
731}
732
733byte MSXCPUInterface::peekMem(word address, EmuTime::param time) const
734{
735 if ((address == 0xFFFF) && isExpanded(primarySlotState[3])) {
736 return 0xFF ^ subSlotRegister[primarySlotState[3]];
737 } else {
738 return visibleDevices[address >> 14]->peekMem(address, time);
739 }
740}
741
742byte MSXCPUInterface::peekSlottedMem(unsigned address, EmuTime::param time) const
743{
744 byte primSlot = (address & 0xC0000) >> 18;
745 byte subSlot = (address & 0x30000) >> 16;
746 byte page = (address & 0x0C000) >> 14;
747 word offset = (address & 0xFFFF); // includes page
748 if (!isExpanded(primSlot)) {
749 subSlot = 0;
750 }
751
752 if ((offset == 0xFFFF) && isExpanded(primSlot)) {
753 return 0xFF ^ subSlotRegister[primSlot];
754 } else {
755 return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
756 }
757}
758
759byte MSXCPUInterface::readSlottedMem(unsigned address, EmuTime::param time)
760{
761 byte primSlot = (address & 0xC0000) >> 18;
762 byte subSlot = (address & 0x30000) >> 16;
763 byte page = (address & 0x0C000) >> 14;
764 word offset = (address & 0xFFFF); // includes page
765 if (!isExpanded(primSlot)) {
766 subSlot = 0;
767 }
768
769 if ((offset == 0xFFFF) && isExpanded(primSlot)) {
770 return 0xFF ^ subSlotRegister[primSlot];
771 } else {
772 return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
773 }
774}
775
776void MSXCPUInterface::writeSlottedMem(unsigned address, byte value,
777 EmuTime::param time)
778{
779 byte primSlot = (address & 0xC0000) >> 18;
780 byte subSlot = (address & 0x30000) >> 16;
781 byte page = (address & 0x0C000) >> 14;
782 word offset = (address & 0xFFFF); // includes page
783 if (!isExpanded(primSlot)) {
784 subSlot = 0;
785 }
786
787 if ((offset == 0xFFFF) && isExpanded(primSlot)) {
788 setSubSlot(primSlot, value);
789 } else {
790 slotLayout[primSlot][subSlot][page]->writeMem(offset, value, time);
791 }
792}
793
795{
796 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", bp.getId()), "add");
797 auto it = ranges::upper_bound(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
798 breakPoints.insert(it, std::move(bp));
799}
800
802{
803 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", bp.getId()), "remove");
804 auto [first, last] = ranges::equal_range(breakPoints, bp.getAddress(), {}, &BreakPoint::getAddress);
805 breakPoints.erase(find_unguarded(first, last, &bp,
806 [](const BreakPoint& i) { return &i; }));
807}
809{
810 if (auto it = ranges::find(breakPoints, id, &BreakPoint::getId);
811 // could be ==end for a breakpoint that removes itself AND has the -once flag set
812 it != breakPoints.end()) {
813 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("bp#", it->getId()), "remove");
814 breakPoints.erase(it);
815 }
816}
817
819 std::pair<BreakPoints::const_iterator,
820 BreakPoints::const_iterator> range)
821{
822 // create copy for the case that breakpoint/condition removes itself
823 // - keeps object alive by holding a shared_ptr to it
824 // - avoids iterating over a changing collection
825 BreakPoints bpCopy(range.first, range.second);
826 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
827 auto& interp = motherBoard.getReactor().getInterpreter();
828 auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
829 for (auto& p : bpCopy) {
830 bool remove = p.checkAndExecute(globalCliComm, interp);
831 if (remove) {
832 removeBreakPoint(p.getId());
833 }
834 }
835 auto condCopy = conditions;
836 for (auto& c : condCopy) {
837 bool remove = c.checkAndExecute(globalCliComm, interp);
838 if (remove) {
839 removeCondition(c.getId());
840 }
841 }
842}
843
844static void registerIOWatch(WatchPoint& watchPoint, std::span<MSXDevice*, 256> devices)
845{
846 auto& ioWatch = checked_cast<WatchIO&>(watchPoint);
847 unsigned beginPort = ioWatch.getBeginAddress();
848 unsigned endPort = ioWatch.getEndAddress();
849 assert(beginPort <= endPort);
850 assert(endPort < 0x100);
851 for (unsigned port = beginPort; port <= endPort; ++port) {
852 ioWatch.getDevice(narrow_cast<byte>(port)).getDevicePtr() = devices[port];
853 devices[port] = &ioWatch.getDevice(narrow_cast<byte>(port));
854 }
855}
856
857void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
858{
859 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "add");
860 watchPoints.push_back(watchPoint);
861 WatchPoint::Type type = watchPoint->getType();
862 switch (type) {
863 using enum WatchPoint::Type;
864 case READ_IO:
865 registerIOWatch(*watchPoint, IO_In);
866 break;
867 case WRITE_IO:
868 registerIOWatch(*watchPoint, IO_Out);
869 break;
870 case READ_MEM:
871 case WRITE_MEM:
872 updateMemWatch(type);
873 break;
874 default:
876 }
877}
878
879static void unregisterIOWatch(WatchPoint& watchPoint, std::span<MSXDevice*, 256> devices)
880{
881 auto& ioWatch = checked_cast<WatchIO&>(watchPoint);
882 unsigned beginPort = ioWatch.getBeginAddress();
883 unsigned endPort = ioWatch.getEndAddress();
884 assert(beginPort <= endPort);
885 assert(endPort < 0x100);
886
887 for (unsigned port = beginPort; port <= endPort; ++port) {
888 // find pointer to watchpoint
889 MSXDevice** prev = &devices[port];
890 while (*prev != &ioWatch.getDevice(narrow_cast<byte>(port))) {
891 prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
892 }
893 // remove watchpoint from chain
894 *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
895 }
896}
897
898void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
899{
900 // Pass shared_ptr by value to keep the object alive for the duration
901 // of this function, otherwise it gets deleted as soon as it's removed
902 // from the watchPoints collection.
903 if (auto it = ranges::find(watchPoints, watchPoint);
904 it != end(watchPoints)) {
905 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "remove");
906 // remove before calling updateMemWatch()
907 watchPoints.erase(it);
908 WatchPoint::Type type = watchPoint->getType();
909 switch (type) {
910 using enum WatchPoint::Type;
911 case READ_IO:
912 unregisterIOWatch(*watchPoint, IO_In);
913 break;
914 case WRITE_IO:
915 unregisterIOWatch(*watchPoint, IO_Out);
916 break;
917 case READ_MEM:
918 case WRITE_MEM:
919 updateMemWatch(type);
920 break;
921 default:
923 }
924 }
925}
926
928{
929 if (auto it = ranges::find(watchPoints, id, &WatchPoint::getId);
930 it != watchPoints.end()) {
931 removeWatchPoint(*it); // not efficient, does a 2nd search, but good enough
932 }
933}
934
936{
937 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "add");
938 conditions.push_back(std::move(cond));
939}
940
942{
943 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "remove");
944 conditions.erase(rfind_unguarded(conditions, &cond,
945 [](auto& e) { return &e; }));
946}
947
949{
950 if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
951 // could be ==end for a condition that removes itself AND has the -once flag set
952 it != conditions.end()) {
953 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", it->getId()), "remove");
954 conditions.erase(it);
955 }
956}
957
958void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
959{
960 std::span<std::bitset<CacheLine::SIZE>, CacheLine::NUM> watchSet =
961 (type == WatchPoint::Type::READ_MEM) ? readWatchSet : writeWatchSet;
962 for (auto i : xrange(CacheLine::NUM)) {
963 watchSet[i].reset();
964 }
965 for (auto& w : watchPoints) {
966 if (w->getType() == type) {
967 unsigned beginAddr = w->getBeginAddress();
968 unsigned endAddr = w->getEndAddress();
969 assert(beginAddr <= endAddr);
970 assert(endAddr < 0x10000);
971 for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
972 watchSet[addr >> CacheLine::BITS].set(
973 addr & CacheLine::LOW);
974 }
975 }
976 }
977 for (auto i : xrange(CacheLine::NUM)) {
978 if (readWatchSet [i].any()) {
979 disallowReadCache [i] |= MEMORY_WATCH_BIT;
980 } else {
981 disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
982 }
983 if (writeWatchSet[i].any()) {
984 disallowWriteCache[i] |= MEMORY_WATCH_BIT;
985 } else {
986 disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
987 }
988 }
989 msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
990}
991
992void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
993 unsigned address, unsigned value)
994{
995 assert(!watchPoints.empty());
996 if (isFastForward()) return;
997
998 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
999 auto& interp = motherBoard.getReactor().getInterpreter();
1000 interp.setVariable(TclObject("wp_last_address"),
1001 TclObject(int(address)));
1002 if (value != ~0u) {
1003 interp.setVariable(TclObject("wp_last_value"),
1004 TclObject(int(value)));
1005 }
1006
1007 auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
1008 auto wpCopy = watchPoints;
1009 for (auto& w : wpCopy) {
1010 if ((w->getBeginAddress() <= address) &&
1011 (w->getEndAddress() >= address) &&
1012 (w->getType() == type)) {
1013 bool remove = w->checkAndExecute(globalCliComm, interp);
1014 if (remove) {
1016 }
1017 }
1018 }
1019
1020 interp.unsetVariable("wp_last_address");
1021 interp.unsetVariable("wp_last_value");
1022}
1023
1024
1026{
1027 assert(!isFastForward());
1028 if (breaked) return;
1029 breaked = true;
1030 msxcpu.exitCPULoopSync();
1031
1032 Reactor& reactor = motherBoard.getReactor();
1033 reactor.block();
1034 breakedSetting->setReadOnlyValue(TclObject("true"));
1035 reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1037}
1038
1040{
1041 assert(!isFastForward());
1043 TclObject("debug break"), TclObject(), true));
1044 doContinue();
1045}
1046
1048{
1049 assert(!isFastForward());
1050 pauseSetting.setBoolean(false); // unpause
1051 if (breaked) {
1052 breaked = false;
1053
1054 Reactor& reactor = motherBoard.getReactor();
1055 breakedSetting->setReadOnlyValue(TclObject("false"));
1056 reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1057 reactor.unblock();
1058 motherBoard.getRealTime().resync();
1059 }
1060}
1061
1063{
1064 // before the Tcl interpreter is destroyed, we must delete all
1065 // TclObjects. Breakpoints and conditions contain such objects
1066 // for the condition and action.
1067 // TODO it would be nicer if breakpoints and conditions were not
1068 // global objects.
1069 breakPoints.clear();
1070 conditions.clear();
1071}
1072
1074{
1075 assert(0 <= ps && ps < 4);
1076 assert(0 <= ss && ss < 4);
1077 assert(0 <= page && page < 4);
1078 return slotLayout[ps][ss][page];
1079}
1080
1081// class MemoryDebug
1082
1083MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1084 : SimpleDebuggable(motherBoard_, "memory",
1085 "The memory currently visible for the CPU.", 0x10000)
1086{
1087}
1088
1089byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1090{
1091 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1092 return interface.peekMem(narrow<word>(address), time);
1093}
1094
1095void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1096 EmuTime::param time)
1097{
1098 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1099 return interface.writeMem(narrow<word>(address), value, time);
1100}
1101
1102
1103// class SlottedMemoryDebug
1104
1105MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1106 MSXMotherBoard& motherBoard_)
1107 : SimpleDebuggable(motherBoard_, "slotted memory",
1108 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1109{
1110}
1111
1112byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1113{
1114 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1115 return interface.peekSlottedMem(address, time);
1116}
1117
1118void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1119 EmuTime::param time)
1120{
1121 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1122 return interface.writeSlottedMem(address, value, time);
1123}
1124
1125
1126// class SlotInfo
1127
1128static unsigned getSlot(
1129 Interpreter& interp, const TclObject& token, const std::string& itemName)
1130{
1131 unsigned slot = token.getInt(interp);
1132 if (slot >= 4) {
1133 throw CommandException(itemName, " must be in range 0..3");
1134 }
1135 return slot;
1136}
1137
1138MSXCPUInterface::SlotInfo::SlotInfo(
1139 InfoCommand& machineInfoCommand)
1140 : InfoTopic(machineInfoCommand, "slot")
1141{
1142}
1143
1144void MSXCPUInterface::SlotInfo::execute(std::span<const TclObject> tokens,
1145 TclObject& result) const
1146{
1147 checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1148 auto& interp = getInterpreter();
1149 unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1150 unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1151 unsigned page = getSlot(interp, tokens[4], "Page");
1152 auto& interface = OUTER(MSXCPUInterface, slotInfo);
1153 if (!interface.isExpanded(narrow<int>(ps))) {
1154 ss = 0;
1155 }
1156 interface.slotLayout[ps][ss][page]->getNameList(result);
1157}
1158
1159std::string MSXCPUInterface::SlotInfo::help(std::span<const TclObject> /*tokens*/) const
1160{
1161 return "Retrieve name of the device inserted in given "
1162 "primary slot / secondary slot / page.";
1163}
1164
1165
1166// class SubSlottedInfo
1167
1168MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1169 InfoCommand& machineInfoCommand)
1170 : InfoTopic(machineInfoCommand, "issubslotted")
1171{
1172}
1173
1174void MSXCPUInterface::SubSlottedInfo::execute(std::span<const TclObject> tokens,
1175 TclObject& result) const
1176{
1177 checkNumArgs(tokens, 3, "primary");
1178 auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1179 result = interface.isExpanded(narrow<int>(
1180 getSlot(getInterpreter(), tokens[2], "Slot")));
1181}
1182
1183std::string MSXCPUInterface::SubSlottedInfo::help(
1184 std::span<const TclObject> /*tokens*/) const
1185{
1186 return "Indicates whether a certain primary slot is expanded.";
1187}
1188
1189
1190// class ExternalSlotInfo
1191
1192MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1193 InfoCommand& machineInfoCommand)
1194 : InfoTopic(machineInfoCommand, "isexternalslot")
1195{
1196}
1197
1198void MSXCPUInterface::ExternalSlotInfo::execute(
1199 std::span<const TclObject> tokens, TclObject& result) const
1200{
1201 checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1202 int ps = 0;
1203 int ss = 0;
1204 auto& interp = getInterpreter();
1205 switch (tokens.size()) {
1206 case 4:
1207 ss = narrow<int>(getSlot(interp, tokens[3], "Secondary slot"));
1208 // Fall-through
1209 case 3:
1210 ps = narrow<int>(getSlot(interp, tokens[2], "Primary slot"));
1211 break;
1212 }
1213 auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1214 auto& manager = interface.motherBoard.getSlotManager();
1215 result = manager.isExternalSlot(ps, ss, true);
1216}
1217
1218std::string MSXCPUInterface::ExternalSlotInfo::help(
1219 std::span<const TclObject> /*tokens*/) const
1220{
1221 return "Indicates whether a certain slot is external or internal.";
1222}
1223
1224
1225// class IODebug
1226
1227MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1228 : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1229{
1230}
1231
1232byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1233{
1234 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1235 return interface.IO_In[address & 0xFF]->peekIO(narrow<word>(address), time);
1236}
1237
1238void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1239{
1240 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1241 interface.writeIO(word(address), value, time);
1242}
1243
1244
1245// class IOInfo
1246
1247MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1248 : InfoTopic(machineInfoCommand, name_)
1249{
1250}
1251
1252void MSXCPUInterface::IOInfo::helper(
1253 std::span<const TclObject> tokens, TclObject& result, std::span<MSXDevice*, 256> devices) const
1254{
1255 checkNumArgs(tokens, 3, "port");
1256 unsigned port = tokens[2].getInt(getInterpreter());
1257 if (port >= 256) {
1258 throw CommandException("Port must be in range 0..255");
1259 }
1260 devices[port]->getNameList(result);
1261}
1262void MSXCPUInterface::IInfo::execute(
1263 std::span<const TclObject> tokens, TclObject& result) const
1264{
1265 auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1266 helper(tokens, result, interface.IO_In);
1267}
1268void MSXCPUInterface::OInfo::execute(
1269 std::span<const TclObject> tokens, TclObject& result) const
1270{
1271 auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1272 helper(tokens, result, interface.IO_Out);
1273}
1274
1275std::string MSXCPUInterface::IOInfo::help(std::span<const TclObject> /*tokens*/) const
1276{
1277 return "Return the name of the device connected to the given IO port.";
1278}
1279
1280
1281template<typename Archive>
1282void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1283{
1284 // TODO watchPoints ???
1285
1286 // primary and 4 secondary slot select registers
1287 byte prim = 0;
1288 if constexpr (!Archive::IS_LOADER) {
1289 for (auto i : xrange(4)) {
1290 prim |= byte(primarySlotState[i] << (2 * i));
1291 }
1292 }
1293 ar.serialize("primarySlots", prim,
1294 "subSlotRegs", subSlotRegister);
1295 if constexpr (Archive::IS_LOADER) {
1296 setPrimarySlots(prim);
1297 for (auto i : xrange(byte(4))) {
1298 setSubSlot(i, subSlotRegister[i]);
1299 }
1300 }
1301
1302 if (delayDevice) {
1303 ar.serialize("vdpDelay", *delayDevice);
1304 }
1305}
1307
1308} // 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)
MSXCPUInterface(MSXMotherBoard &motherBoard)
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.
byte readIRQVector() const
CPU uses this method to read 'extra' data from the data bus used in interrupt routines.
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)
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.
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:49
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:74
CommandController & getCommandController()
Definition Reactor.cc:333
CliComm & getCliComm()
Definition Reactor.cc:323
GlobalCliComm & getGlobalCliComm()
Definition Reactor.hh:89
Interpreter & getInterpreter()
Definition Reactor.cc:328
EventDistributor & getEventDistributor()
Definition Reactor.hh:88
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)