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