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 "outer.hh"
25#include "ranges.hh"
26#include "stl.hh"
27#include "unreachable.hh"
28#include "xrange.hh"
29#include <cstring>
30#include <iomanip>
31#include <iostream>
32#include <iterator>
33#include <memory>
34#include <optional>
35
36namespace openmsx {
37
38static std::optional<ReadOnlySetting> breakedSetting;
39static unsigned breakedSettingCount = 0;
40
41
42// Bitfields used in the disallowReadCache and disallowWriteCache arrays
43constexpr byte SECONDARY_SLOT_BIT = 0x01;
44constexpr byte MEMORY_WATCH_BIT = 0x02;
45constexpr byte GLOBAL_RW_BIT = 0x04;
46
47std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>)
48{
49 return os << "CacheLineCounters";
50}
51std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn)
52{
53 std::string_view names[size_t(CacheLineCounters::NUM)] = {
54 "NonCachedRead",
55 "NonCachedWrite",
56 "GetReadCacheLine",
57 "GetWriteCacheLine",
58 "SlowRead",
59 "SlowWrite",
60 "DisallowCacheRead",
61 "DisallowCacheWrite",
62 "InvalidateAllSlots",
63 "InvalidateReadWrite",
64 "InvalidateRead",
65 "InvalidateWrite",
66 "FillReadWrite",
67 "FillRead",
68 "FillWrite",
69 };
70 return os << names[size_t(evn.e)];
71}
72
74 : memoryDebug (motherBoard_)
75 , slottedMemoryDebug(motherBoard_)
76 , ioDebug (motherBoard_)
77 , slotInfo(motherBoard_.getMachineInfoCommand())
78 , subSlottedInfo(motherBoard_.getMachineInfoCommand())
79 , externalSlotInfo(motherBoard_.getMachineInfoCommand())
80 , inputPortInfo (motherBoard_.getMachineInfoCommand())
81 , outputPortInfo(motherBoard_.getMachineInfoCommand())
82 , dummyDevice(DeviceFactory::createDummyDevice(
83 *motherBoard_.getMachineConfig()))
84 , msxcpu(motherBoard_.getCPU())
85 , cliComm(motherBoard_.getMSXCliComm())
86 , motherBoard(motherBoard_)
87 , fastForward(false)
88{
89 ranges::fill(primarySlotState, 0);
90 ranges::fill(secondarySlotState, 0);
91 ranges::fill(expanded, 0);
92 ranges::fill(subSlotRegister, 0);
93 ranges::fill(IO_In, dummyDevice.get());
94 ranges::fill(IO_Out, dummyDevice.get());
95 ranges::fill(visibleDevices, dummyDevice.get());
96 for (auto& sub1 : slotLayout) {
97 for (auto& sub2 : sub1) {
98 ranges::fill(sub2, dummyDevice.get());
99 }
100 }
101
102 // initially allow all regions to be cached
103 memset(disallowReadCache, 0, sizeof(disallowReadCache));
104 memset(disallowWriteCache, 0, sizeof(disallowWriteCache));
105
106 initialPrimarySlots = motherBoard.getMachineConfig()->parseSlotMap();
107 // Note: SlotState is initialised at reset
108
109 msxcpu.setInterface(this);
110
111 if (motherBoard.isTurboR()) {
112 // TODO also MSX2+ needs (slightly different) VDPIODelay
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 slotexpanders behave different.
219 } else {
220 visibleDevices[address>>14]->writeMem(address, value, time);
221 }
222 // something special in this region?
223 if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
224 // slot-select-ignore writes (Super Lode Runner)
225 for (auto& g : globalWrites) {
226 // very primitive address selection mechanism,
227 // but more than enough for now
228 if (g.addr == address) [[unlikely]] {
229 g.device->globalWrite(address, value, time);
230 }
231 }
232 // execute write watches after actual write
233 if (writeWatchSet[address >> CacheLine::BITS]
234 [address & CacheLine::LOW]) {
235 executeMemWatch(WatchPoint::WRITE_MEM, address, value);
236 }
237 }
238}
239
241{
242 if (expanded[ps] == 0) {
243 for (auto page : xrange(4)) {
244 if (slotLayout[ps][0][page] != dummyDevice.get()) {
245 throw MSXException("Can't expand slot because "
246 "it's already in use.");
247 }
248 }
249 }
250 expanded[ps]++;
251 changeExpanded(isExpanded(primarySlotState[3]));
252}
253
255 int ps,
256 std::span<const std::unique_ptr<MSXDevice>> allowed) const
257{
258 assert(isExpanded(ps));
259 if (expanded[ps] != 1) return; // ok, still expanded after this
260
261 std::vector<MSXDevice*> inUse;
262
263 auto isAllowed = [&](MSXDevice* dev) {
264 return (dev == dummyDevice.get()) ||
265 contains(allowed, dev, [](const auto& d) { return d.get(); });
266 };
267 auto check = [&](MSXDevice* dev) {
268 if (!isAllowed(dev)) {
269 if (!contains(inUse, dev)) { // filter duplicates
270 inUse.push_back(dev);
271 }
272 }
273 };
274
275 for (auto ss : xrange(4)) {
276 for (auto page : xrange(4)) {
277 MSXDevice* device = slotLayout[ps][ss][page];
278 if (auto* memDev = dynamic_cast<MSXMultiMemDevice*>(device)) {
279 for (auto* dev : memDev->getDevices()) {
280 check(dev);
281 }
282 } else {
283 check(device);
284 }
285
286 }
287 }
288 if (inUse.empty()) return; // ok, no more devices in use
289
290 auto msg = strCat("Can't remove slot expander from slot ", ps,
291 " because the following devices are still inserted:");
292 for (auto& d : inUse) {
293 strAppend(msg, ' ', d->getName());
294 }
295 strAppend(msg, '.');
296 throw MSXException(std::move(msg));
297}
298
300{
301#ifndef NDEBUG
302 try {
303 std::span<const std::unique_ptr<MSXDevice>> dummy;
304 testUnsetExpanded(ps, dummy);
305 } catch (...) {
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 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, int base, int size)
442{
443 int page = base >> 14;
444 MSXDevice*& slot = slotLayout[ps][ss][page];
445 if (size == 0x4000) {
446 // full 16kb, directly register device (no multiplexer)
447 if (slot != dummyDevice.get()) {
448 reportMemOverlap(ps, ss, *slot, device);
449 }
450 } else {
451 // partial page
452 if (slot == dummyDevice.get()) {
453 // first, ok
454 } else if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
455 // second (or more), check for overlap
456 if (!multi->canAdd(base, size)) {
457 reportMemOverlap(ps, ss, *slot, device);
458 }
459 } else {
460 // conflict with 'full ranged' device
461 reportMemOverlap(ps, ss, *slot, device);
462 }
463 }
464}
465
466void MSXCPUInterface::registerSlot(
467 MSXDevice& device, int ps, int ss, int base, int size)
468{
469 int page = base >> 14;
470 MSXDevice*& slot = slotLayout[ps][ss][page];
471 if (size == 0x4000) {
472 // full 16kb, directly register device (no multiplexer)
473 assert(slot == dummyDevice.get());
474 slot = &device;
475 } else {
476 // partial page
477 if (slot == dummyDevice.get()) {
478 // first
479 auto* multi = new MSXMultiMemDevice(device.getHardwareConfig());
480 multi->add(device, base, size);
481 slot = multi;
482 } else if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
483 // second or more
484 assert(multi->canAdd(base, size));
485 multi->add(device, base, size);
486 } else {
487 // conflict with 'full ranged' device
488 assert(false);
489 }
490 }
491 invalidateRWCache(base, size, ps, ss);
492 updateVisible(page);
493}
494
495void MSXCPUInterface::unregisterSlot(
496 MSXDevice& device, int ps, int ss, int base, int size)
497{
498 int page = base >> 14;
499 MSXDevice*& slot = slotLayout[ps][ss][page];
500 if (auto* multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
501 // partial range
502 multi->remove(device, base, size);
503 if (multi->empty()) {
504 delete multi;
505 slot = dummyDevice.get();
506 }
507 } else {
508 // full 16kb range
509 assert(slot == &device);
510 slot = dummyDevice.get();
511 }
512 invalidateRWCache(base, size, ps, ss);
513 updateVisible(page);
514}
515
517 MSXDevice& device, int ps, int ss, int base_, int size_)
518{
519 if (!isExpanded(ps) && (ss != 0)) {
520 throw MSXException(
521 "Slot ", ps, '.', ss,
522 " does not exist because slot is not expanded.");
523 }
524
525 // split range on 16kb borders
526 // first check if registration is possible
527 int base = base_;
528 int size = size_;
529 while (size > 0) {
530 int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
531 testRegisterSlot(device, ps, ss, base, partialSize);
532 base += partialSize;
533 size -= partialSize;
534 }
535 // if all checks are successful, only then actually register
536 base = base_;
537 size = size_;
538 while (size > 0) {
539 int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
540 registerSlot(device, ps, ss, base, partialSize);
541 base += partialSize;
542 size -= partialSize;
543 }
544}
545
547 MSXDevice& device, int ps, int ss, int base, int size)
548{
549 // split range on 16kb borders
550 while (size > 0) {
551 int partialSize = std::min(size, ((base + 0x4000) & ~0x3FFF) - base);
552 unregisterSlot(device, ps, ss, base, partialSize);
553 base += partialSize;
554 size -= partialSize;
555 }
556}
557
559{
560 globalWrites.push_back({&device, address});
561
562 disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
563 msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
564}
565
567{
568 GlobalRwInfo info = { &device, address };
569 move_pop_back(globalWrites, rfind_unguarded(globalWrites, info));
570
571 for (auto& g : globalWrites) {
572 if ((g.addr >> CacheLine::BITS) ==
573 (address >> CacheLine::BITS)) {
574 // there is still a global write in this region
575 return;
576 }
577 }
578 disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
579 msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
580}
581
583{
584 globalReads.push_back({&device, address});
585
586 disallowReadCache[address >> CacheLine::BITS] |= GLOBAL_RW_BIT;
587 msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
588}
589
591{
592 GlobalRwInfo info = { &device, address };
593 move_pop_back(globalReads, rfind_unguarded(globalReads, info));
594
595 for (auto& g : globalReads) {
596 if ((g.addr >> CacheLine::BITS) ==
597 (address >> CacheLine::BITS)) {
598 // there is still a global write in this region
599 return;
600 }
601 }
602 disallowReadCache[address >> CacheLine::BITS] &= ~GLOBAL_RW_BIT;
603 msxcpu.invalidateAllSlotsRWCache(address & CacheLine::HIGH, 0x100);
604}
605
606ALWAYS_INLINE void MSXCPUInterface::updateVisible(int page, int ps, int ss)
607{
608 MSXDevice* newDevice = slotLayout[ps][ss][page];
609 if (visibleDevices[page] != newDevice) {
610 visibleDevices[page] = newDevice;
611 msxcpu.updateVisiblePage(page, ps, ss);
612 }
613}
614void MSXCPUInterface::updateVisible(int 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);
628}
629void MSXCPUInterface::invalidateWCache (word start, unsigned size, int ps, int ss)
630{
632 msxcpu.invalidateWCache(start, size, ps, ss, 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);
644}
645void MSXCPUInterface::fillWCache(unsigned start, unsigned size, byte* wData, int ps, int ss)
646{
648 msxcpu.fillWCache(start, size, wData, ps, ss, disallowWriteCache);
649}
650
652{
653 for (auto i : xrange(4)) {
654 setSubSlot(i, 0);
655 }
656 setPrimarySlots(initialPrimarySlots);
657}
658
660{
661 return motherBoard.readIRQVector();
662}
663
665{
666 // Change the slot structure.
667 // Originally the code below was a loop over the 4 pages, and the check
668 // for (un)expanded-slot was done unconditionally at the end. I've
669 // completely unrolled the loop and only check for (un)expanded slot
670 // when the slot in page 3 has changed. I've also added checks for slot
671 // changes for the other 3 pages. Usually when this register is written
672 // only one of the 4 pages actually changes, so these extra checks do
673 // pay off. This does make the code a bit more complex (and the
674 // generated code slightly bigger), but it does make a measurable speed
675 // difference. Changing the slots several hundreds of times per
676 // (EmuTime) is not unusual. So this routine ended up quite high
677 // (top-10) in some profile results.
678 int ps0 = (value >> 0) & 3;
679 if (primarySlotState[0] != ps0) [[unlikely]] {
680 primarySlotState[0] = ps0;
681 int ss0 = (subSlotRegister[ps0] >> 0) & 3;
682 secondarySlotState[0] = ss0;
683 updateVisible(0, ps0, ss0);
684 }
685 int ps1 = (value >> 2) & 3;
686 if (primarySlotState[1] != ps1) [[unlikely]] {
687 primarySlotState[1] = ps1;
688 int ss1 = (subSlotRegister[ps1] >> 2) & 3;
689 secondarySlotState[1] = ss1;
690 updateVisible(1, ps1, ss1);
691 }
692 int ps2 = (value >> 4) & 3;
693 if (primarySlotState[2] != ps2) [[unlikely]] {
694 primarySlotState[2] = ps2;
695 int ss2 = (subSlotRegister[ps2] >> 4) & 3;
696 secondarySlotState[2] = ss2;
697 updateVisible(2, ps2, ss2);
698 }
699 int ps3 = (value >> 6) & 3;
700 if (primarySlotState[3] != ps3) [[unlikely]] {
701 bool oldExpanded = isExpanded(primarySlotState[3]);
702 bool newExpanded = isExpanded(ps3);
703 primarySlotState[3] = ps3;
704 int ss3 = (subSlotRegister[ps3] >> 6) & 3;
705 secondarySlotState[3] = ss3;
706 updateVisible(3, ps3, ss3);
707 if (oldExpanded != newExpanded) [[unlikely]] {
708 changeExpanded(newExpanded);
709 }
710 }
711}
712
713void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
714{
715 subSlotRegister[primSlot] = value;
716 for (int page = 0; page < 4; ++page, value >>= 2) {
717 if (primSlot == primarySlotState[page]) {
718 secondarySlotState[page] = value & 3;
719 // Change the visible devices
720 updateVisible(page);
721 }
722 }
723}
724
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, MSXDevice** devices)
836{
837 assert(dynamic_cast<WatchIO*>(&watchPoint));
838 auto& ioWatch = static_cast<WatchIO&>(watchPoint);
839 unsigned beginPort = ioWatch.getBeginAddress();
840 unsigned endPort = ioWatch.getEndAddress();
841 assert(beginPort <= endPort);
842 assert(endPort < 0x100);
843 for (unsigned port = beginPort; port <= endPort; ++port) {
844 ioWatch.getDevice(port).getDevicePtr() = devices[port];
845 devices[port] = &ioWatch.getDevice(port);
846 }
847}
848
849void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
850{
851 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "add");
852 watchPoints.push_back(watchPoint);
853 WatchPoint::Type type = watchPoint->getType();
854 switch (type) {
856 registerIOWatch(*watchPoint, IO_In);
857 break;
859 registerIOWatch(*watchPoint, IO_Out);
860 break;
863 updateMemWatch(type);
864 break;
865 default:
866 UNREACHABLE; break;
867 }
868}
869
870static void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
871{
872 assert(dynamic_cast<WatchIO*>(&watchPoint));
873 auto& ioWatch = static_cast<WatchIO&>(watchPoint);
874 unsigned beginPort = ioWatch.getBeginAddress();
875 unsigned endPort = ioWatch.getEndAddress();
876 assert(beginPort <= endPort);
877 assert(endPort < 0x100);
878
879 for (unsigned port = beginPort; port <= endPort; ++port) {
880 // find pointer to watchpoint
881 MSXDevice** prev = &devices[port];
882 while (*prev != &ioWatch.getDevice(port)) {
883 prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
884 }
885 // remove watchpoint from chain
886 *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
887 }
888}
889
890void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
891{
892 // Pass shared_ptr by value to keep the object alive for the duration
893 // of this function, otherwise it gets deleted as soon as it's removed
894 // from the watchPoints collection.
895 if (auto it = ranges::find(watchPoints, watchPoint);
896 it != end(watchPoints)) {
897 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("wp#", watchPoint->getId()), "remove");
898 // remove before calling updateMemWatch()
899 watchPoints.erase(it);
900 WatchPoint::Type type = watchPoint->getType();
901 switch (type) {
903 unregisterIOWatch(*watchPoint, IO_In);
904 break;
906 unregisterIOWatch(*watchPoint, IO_Out);
907 break;
910 updateMemWatch(type);
911 break;
912 default:
913 UNREACHABLE; break;
914 }
915 }
916}
917
919{
920 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "add");
921 conditions.push_back(std::move(cond));
922}
923
925{
926 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", cond.getId()), "remove");
927 conditions.erase(rfind_unguarded(conditions, &cond,
928 [](auto& e) { return &e; }));
929}
930
931void MSXCPUInterface::removeCondition(unsigned id)
932{
933 if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
934 // could be ==end for a condition that removes itself AND has the -once flag set
935 it != conditions.end()) {
936 cliComm.update(CliComm::DEBUG_UPDT, tmpStrCat("cond#", it->getId()), "remove");
937 conditions.erase(it);
938 }
939}
940
941void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
942{
943 std::bitset<CacheLine::SIZE>* watchSet =
944 (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
945 for (auto i : xrange(CacheLine::NUM)) {
946 watchSet[i].reset();
947 }
948 for (auto& w : watchPoints) {
949 if (w->getType() == type) {
950 unsigned beginAddr = w->getBeginAddress();
951 unsigned endAddr = w->getEndAddress();
952 assert(beginAddr <= endAddr);
953 assert(endAddr < 0x10000);
954 for (unsigned addr = beginAddr; addr <= endAddr; ++addr) {
955 watchSet[addr >> CacheLine::BITS].set(
956 addr & CacheLine::LOW);
957 }
958 }
959 }
960 for (auto i : xrange(CacheLine::NUM)) {
961 if (readWatchSet [i].any()) {
962 disallowReadCache [i] |= MEMORY_WATCH_BIT;
963 } else {
964 disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
965 }
966 if (writeWatchSet[i].any()) {
967 disallowWriteCache[i] |= MEMORY_WATCH_BIT;
968 } else {
969 disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
970 }
971 }
972 msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
973}
974
975void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
976 unsigned address, unsigned value)
977{
978 assert(!watchPoints.empty());
979 if (isFastForward()) return;
980
981 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
982 auto& interp = motherBoard.getReactor().getInterpreter();
983 interp.setVariable(TclObject("wp_last_address"),
984 TclObject(int(address)));
985 if (value != ~0u) {
986 interp.setVariable(TclObject("wp_last_value"),
987 TclObject(int(value)));
988 }
989
990 auto wpCopy = watchPoints;
991 for (auto& w : wpCopy) {
992 if ((w->getBeginAddress() <= address) &&
993 (w->getEndAddress() >= address) &&
994 (w->getType() == type)) {
995 bool remove = w->checkAndExecute(globalCliComm, interp);
996 if (remove) {
998 }
999 }
1000 }
1001
1002 interp.unsetVariable("wp_last_address");
1003 interp.unsetVariable("wp_last_value");
1004}
1005
1006
1008{
1009 assert(!isFastForward());
1010 if (breaked) return;
1011 breaked = true;
1012 msxcpu.exitCPULoopSync();
1013
1014 Reactor& reactor = motherBoard.getReactor();
1015 reactor.block();
1016 breakedSetting->setReadOnlyValue(TclObject("true"));
1017 reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1019 Event::create<BreakEvent>());
1020}
1021
1023{
1024 assert(!isFastForward());
1026 TclObject("debug break"), TclObject(), true));
1027 doContinue();
1028}
1029
1031{
1032 assert(!isFastForward());
1033 if (breaked) {
1034 breaked = false;
1035
1036 Reactor& reactor = motherBoard.getReactor();
1037 breakedSetting->setReadOnlyValue(TclObject("false"));
1038 reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1039 reactor.unblock();
1040 motherBoard.getRealTime().resync();
1041 }
1042}
1043
1045{
1046 // before the Tcl interpreter is destroyed, we must delete all
1047 // TclObjects. Breakpoints and conditions contain such objects
1048 // for the condition and action.
1049 // TODO it would be nicer if breakpoints and conditions were not
1050 // global objects.
1051 breakPoints.clear();
1052 conditions.clear();
1053}
1054
1055
1056// class MemoryDebug
1057
1058MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1059 : SimpleDebuggable(motherBoard_, "memory",
1060 "The memory currently visible for the CPU.", 0x10000)
1061{
1062}
1063
1064byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1065{
1066 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1067 return interface.peekMem(address, time);
1068}
1069
1070void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1071 EmuTime::param time)
1072{
1073 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1074 return interface.writeMem(address, value, time);
1075}
1076
1077
1078// class SlottedMemoryDebug
1079
1080MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1081 MSXMotherBoard& motherBoard_)
1082 : SimpleDebuggable(motherBoard_, "slotted memory",
1083 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1084{
1085}
1086
1087byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1088{
1089 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1090 return interface.peekSlottedMem(address, time);
1091}
1092
1093void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1094 EmuTime::param time)
1095{
1096 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1097 return interface.writeSlottedMem(address, value, time);
1098}
1099
1100
1101// class SlotInfo
1102
1103static unsigned getSlot(
1104 Interpreter& interp, const TclObject& token, const std::string& itemName)
1105{
1106 unsigned slot = token.getInt(interp);
1107 if (slot >= 4) {
1108 throw CommandException(itemName, " must be in range 0..3");
1109 }
1110 return slot;
1111}
1112
1113MSXCPUInterface::SlotInfo::SlotInfo(
1114 InfoCommand& machineInfoCommand)
1115 : InfoTopic(machineInfoCommand, "slot")
1116{
1117}
1118
1119void MSXCPUInterface::SlotInfo::execute(std::span<const TclObject> tokens,
1120 TclObject& result) const
1121{
1122 checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1123 auto& interp = getInterpreter();
1124 unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1125 unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1126 unsigned page = getSlot(interp, tokens[4], "Page");
1127 auto& interface = OUTER(MSXCPUInterface, slotInfo);
1128 if (!interface.isExpanded(ps)) {
1129 ss = 0;
1130 }
1131 interface.slotLayout[ps][ss][page]->getNameList(result);
1132}
1133
1134std::string MSXCPUInterface::SlotInfo::help(std::span<const TclObject> /*tokens*/) const
1135{
1136 return "Retrieve name of the device inserted in given "
1137 "primary slot / secondary slot / page.";
1138}
1139
1140
1141// class SubSlottedInfo
1142
1143MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1144 InfoCommand& machineInfoCommand)
1145 : InfoTopic(machineInfoCommand, "issubslotted")
1146{
1147}
1148
1149void MSXCPUInterface::SubSlottedInfo::execute(std::span<const TclObject> tokens,
1150 TclObject& result) const
1151{
1152 checkNumArgs(tokens, 3, "primary");
1153 auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1154 result = interface.isExpanded(
1155 getSlot(getInterpreter(), tokens[2], "Slot"));
1156}
1157
1158std::string MSXCPUInterface::SubSlottedInfo::help(
1159 std::span<const TclObject> /*tokens*/) const
1160{
1161 return "Indicates whether a certain primary slot is expanded.";
1162}
1163
1164
1165// class ExternalSlotInfo
1166
1167MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1168 InfoCommand& machineInfoCommand)
1169 : InfoTopic(machineInfoCommand, "isexternalslot")
1170{
1171}
1172
1173void MSXCPUInterface::ExternalSlotInfo::execute(
1174 std::span<const TclObject> tokens, TclObject& result) const
1175{
1176 checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1177 int ps = 0;
1178 int ss = 0;
1179 auto& interp = getInterpreter();
1180 switch (tokens.size()) {
1181 case 4:
1182 ss = getSlot(interp, tokens[3], "Secondary slot");
1183 // Fall-through
1184 case 3:
1185 ps = getSlot(interp, tokens[2], "Primary slot");
1186 break;
1187 }
1188 auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1189 auto& manager = interface.motherBoard.getSlotManager();
1190 result = manager.isExternalSlot(ps, ss, true);
1191}
1192
1193std::string MSXCPUInterface::ExternalSlotInfo::help(
1194 std::span<const TclObject> /*tokens*/) const
1195{
1196 return "Indicates whether a certain slot is external or internal.";
1197}
1198
1199
1200// class IODebug
1201
1202MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1203 : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1204{
1205}
1206
1207byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1208{
1209 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1210 return interface.IO_In[address & 0xFF]->peekIO(address, time);
1211}
1212
1213void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1214{
1215 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1216 interface.writeIO(word(address), value, time);
1217}
1218
1219
1220// class IOInfo
1221
1222MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1223 : InfoTopic(machineInfoCommand, name_)
1224{
1225}
1226
1227void MSXCPUInterface::IOInfo::helper(
1228 std::span<const TclObject> tokens, TclObject& result, MSXDevice** devices) const
1229{
1230 checkNumArgs(tokens, 3, "port");
1231 unsigned port = tokens[2].getInt(getInterpreter());
1232 if (port >= 256) {
1233 throw CommandException("Port must be in range 0..255");
1234 }
1235 devices[port]->getNameList(result);
1236}
1237void MSXCPUInterface::IInfo::execute(
1238 std::span<const TclObject> tokens, TclObject& result) const
1239{
1240 auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1241 helper(tokens, result, interface.IO_In);
1242}
1243void MSXCPUInterface::OInfo::execute(
1244 std::span<const TclObject> tokens, TclObject& result) const
1245{
1246 auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1247 helper(tokens, result, interface.IO_Out);
1248}
1249
1250std::string MSXCPUInterface::IOInfo::help(std::span<const TclObject> /*tokens*/) const
1251{
1252 return "Return the name of the device connected to the given IO port.";
1253}
1254
1255
1256template<typename Archive>
1257void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1258{
1259 // TODO watchPoints ???
1260
1261 // primary and 4 secondary slot select registers
1262 byte prim = 0;
1263 if constexpr (!Archive::IS_LOADER) {
1264 for (auto i : xrange(4)) {
1265 prim |= primarySlotState[i] << (2 * i);
1266 }
1267 }
1268 ar.serialize("primarySlots", prim,
1269 "subSlotRegs", subSlotRegister);
1270 if constexpr (Archive::IS_LOADER) {
1271 setPrimarySlots(prim);
1272 for (auto i : xrange(4)) {
1273 setSubSlot(i, subSlotRegister[i]);
1274 }
1275 }
1276
1277 if (delayDevice) {
1278 ar.serialize("vdpDelay", *delayDevice);
1279 }
1280}
1282
1283} // namespace openmsx
int g
Base class for CPU breakpoints.
Definition: BreakPoint.hh:14
unsigned getId() const
Definition: BreakPoint.hh:22
word getAddress() const
Definition: BreakPoint.hh:21
virtual void update(UpdateType type, std::string_view name, std::string_view value)=0
void printWarning(std::string_view message)
Definition: CliComm.cc:10
General debugger condition Like breakpoints, but not tied to a specifc address.
unsigned getId() const
static std::unique_ptr< VDPIODelay > createVDPIODelay(const HardwareConfig &hwConf, MSXCPUInterface &cpuInterface)
void distributeEvent(Event &&event)
Schedule the given event for delivery.
byte parseSlotMap() const
Parses a slot mapping.
const XMLElement & getDevicesElem() const
void setVariable(const TclObject &name, const TclObject &value)
Definition: Interpreter.cc:249
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
void invalidateRWCache(word start, unsigned size, int ps, int ss)
void insertBreakPoint(BreakPoint bp)
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss)
void unregister_IO_In(byte port, MSXDevice *device)
bool replace_IO_Out(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
void changeExpanded(bool newExpanded)
void removeCondition(const DebugCondition &cond)
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
void 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 registerMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
Devices can register themself in the MSX slotstructure.
void invalidateWCache(word start, unsigned size, int ps, int ss)
byte readIRQVector()
CPU uses this method to read 'extra' data from the databus used in interrupt routines.
bool checkBreakPoints(unsigned pc)
void serialize(Archive &ar, unsigned version)
void unregisterMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
void registerGlobalRead(MSXDevice &device, word address)
(Un)register global read.
byte readSlottedMem(unsigned address, EmuTime::param time)
void removeBreakPoint(const BreakPoint &bp)
void unregister_IO_Out(byte port, MSXDevice *device)
void registerGlobalWrite(MSXDevice &device, word address)
(Un)register global writes.
std::vector< BreakPoint > BreakPoints
bool replace_IO_In(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
These methods replace a previously registered device with a new one.
MSXCPUInterface(const MSXCPUInterface &)=delete
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss)
bool isExpanded(int ps) const
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss, const byte *disallowWrite)
Definition: MSXCPU.cc:279
void fillRWCache(unsigned start, unsigned size, const byte *rData, byte *wData, int ps, int ss, const byte *disallowRead, const byte *disallowWrite)
Fill the read and write cache lines for a specific slot with the specified value.
Definition: MSXCPU.cc:269
void invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:180
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss, const byte *disallowRead)
Definition: MSXCPU.cc:274
void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot)
Inform CPU of bank switch.
Definition: MSXCPU.cc:162
void invalidateRWCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowRead, const byte *disallowWrite)
Similar to the method above, but only invalidates one specific slot.
Definition: MSXCPU.cc:249
void exitCPULoopSync()
See CPUCore::exitCPULoopsync()
Definition: MSXCPU.cc:128
void invalidateRCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowRead)
Definition: MSXCPU.cc:256
void setInterface(MSXCPUInterface *interf)
Definition: MSXCPU.cc:75
void invalidateWCache(unsigned start, unsigned size, int ps, int ss, const byte *disallowWrite)
Definition: MSXCPU.cc:262
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:44
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:420
virtual void writeMem(word address, byte value, EmuTime::param time)
Write a given byte to a given location at a certain time to this device.
Definition: MSXDevice.cc:431
virtual byte peekMem(word address, EmuTime::param time) const
Read a byte from a given memory location.
Definition: MSXDevice.cc:437
virtual const std::string & getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:376
const HardwareConfig * getMachineConfig() const
Contains the main loop of openMSX.
Definition: Reactor.hh:68
CommandController & getCommandController()
Definition: Reactor.cc:322
void unblock()
Definition: Reactor.cc:602
CliComm & getCliComm()
Definition: Reactor.cc:312
GlobalCliComm & getGlobalCliComm()
Definition: Reactor.hh:83
Interpreter & getInterpreter()
Definition: Reactor.cc:317
EventDistributor & getEventDistributor()
Definition: Reactor.hh:82
Base class for CPU breakpoints.
Definition: WatchPoint.hh:14
unsigned getBeginAddress() const
Definition: WatchPoint.hh:34
#define ALWAYS_INLINE
Definition: inline.hh:16
constexpr double e
Definition: Math.hh:18
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:265
constexpr unsigned NUM
Definition: CacheLine.hh:8
constexpr unsigned LOW
Definition: CacheLine.hh:9
constexpr unsigned HIGH
Definition: CacheLine.hh:10
constexpr unsigned BITS
Definition: CacheLine.hh:6
This file implemented 3 utility functions:
Definition: Autofire.cc:9
@ DisallowCacheWrite
@ InvalidateReadWrite
constexpr byte GLOBAL_RW_BIT
constexpr byte SECONDARY_SLOT_BIT
std::ostream & operator<<(std::ostream &os, EnumTypeName< CacheLineCounters >)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr byte MEMORY_WATCH_BIT
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:256
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:232
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:144
auto upper_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:108
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:117
size_t size(std::string_view utf8)
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
ITER find_unguarded(ITER first, ITER last, const VAL &val, Proj proj={})
Faster alternative to 'find' when it's guaranteed that the value will be found (if not the behavior i...
Definition: stl.hh:63
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:125
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:100
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:23
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617
std::string strCat(Ts &&...ts)
Definition: strCat.hh:549
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:627
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:133
constexpr auto end(const zstring_view &x)