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