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 "Reactor.hh"
20#include "ReadOnlySetting.hh"
21#include "RealTime.hh"
22#include "Scheduler.hh"
24#include "TclObject.hh"
25#include "VDPIODelay.hh"
26#include "WatchPoint.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(narrow<byte>(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(narrow<byte>(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(narrow<byte>(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(narrow<byte>(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(narrow<byte>(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(narrow<byte>(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, bp.getIdStr(), "add");
832 breakPoints.push_back(std::move(bp));
833}
834
836{
837 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, bp.getIdStr(), "remove");
838 breakPoints.erase(find_unguarded(breakPoints, &bp, [](const BreakPoint& i) { return &i; }));
839}
841{
842 if (auto it = ranges::find(breakPoints, id, &BreakPoint::getId);
843 // could be ==end for a breakpoint that removes itself AND has the -once flag set
844 it != breakPoints.end()) {
845 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, it->getIdStr(), "remove");
846 breakPoints.erase(it);
847 }
848}
849
851{
852 // create copy for the case that breakpoint/condition removes itself
853 // - avoids iterating over a changing collection
854 std::vector<BreakPoint> bpCopy;
855 for (const auto& bp : breakPoints) {
856 if (bp.isEnabled() && bp.getAddress() == pc) bpCopy.push_back(bp);
857 }
858 std::vector<DebugCondition> condCopy;
859 for (const auto& cond : conditions) {
860 if (cond.isEnabled()) condCopy.push_back(cond);
861 }
862 if (bpCopy.empty() && condCopy.empty()) return false;
863
864 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
865 auto& interp = motherBoard.getReactor().getInterpreter();
866 auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
867 for (auto& p : bpCopy) {
868 bool remove = p.checkAndExecute(globalCliComm, interp);
869 if (remove) {
870 removeBreakPoint(p.getId());
871 }
872 }
873 for (auto& c : condCopy) {
874 bool remove = c.checkAndExecute(globalCliComm, interp);
875 if (remove) {
876 removeCondition(c.getId());
877 }
878 }
879 return isBreaked();
880}
881
882void MSXCPUInterface::registerWatchPoint(WatchPoint& wp)
883{
884 auto type = wp.getType();
885 switch (type) {
886 using enum WatchPoint::Type;
887 case READ_IO:
888 wp.registerIOWatch(motherBoard, IO_In);
889 break;
890 case WRITE_IO:
891 wp.registerIOWatch(motherBoard, IO_Out);
892 break;
893 case READ_MEM:
894 case WRITE_MEM:
895 updateMemWatch(type);
896 break;
897 default:
899 }
900}
901
902void MSXCPUInterface::unregisterWatchPoint(WatchPoint& wp)
903{
904 auto type = wp.getType();
905 switch (type) {
906 using enum WatchPoint::Type;
907 case READ_IO:
908 wp.unregisterIOWatch(IO_In);
909 break;
910 case WRITE_IO:
911 wp.unregisterIOWatch(IO_Out);
912 break;
913 case READ_MEM:
914 case WRITE_MEM:
915 updateMemWatch(type);
916 break;
917 default:
919 }
920}
921
922void MSXCPUInterface::setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint)
923{
924 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, watchPoint->getIdStr(), "add");
925 watchPoints.push_back(watchPoint);
926 registerWatchPoint(*watchPoint);
927}
928
929void MSXCPUInterface::removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint)
930{
931 // Pass shared_ptr by value to keep the object alive for the duration
932 // of this function, otherwise it gets deleted as soon as it's removed
933 // from the watchPoints collection.
934 if (auto it = ranges::find(watchPoints, watchPoint);
935 it != end(watchPoints)) {
936 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, watchPoint->getIdStr(), "remove");
937 // remove before calling updateMemWatch()
938 watchPoints.erase(it);
939 unregisterWatchPoint(*watchPoint);
940 }
941}
942
944{
945 if (auto it = ranges::find(watchPoints, id, &WatchPoint::getId);
946 it != watchPoints.end()) {
947 removeWatchPoint(*it); // not efficient, does a 2nd search, but good enough
948 }
949}
950
952{
953 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, cond.getIdStr(), "add");
954 conditions.push_back(std::move(cond));
955}
956
958{
959 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, cond.getIdStr(), "remove");
960 conditions.erase(rfind_unguarded(conditions, &cond,
961 [](auto& e) { return &e; }));
962}
963
965{
966 if (auto it = ranges::find(conditions, id, &DebugCondition::getId);
967 // could be ==end for a condition that removes itself AND has the -once flag set
968 it != conditions.end()) {
969 cliComm.update(CliComm::UpdateType::DEBUG_UPDT, it->getIdStr(), "remove");
970 conditions.erase(it);
971 }
972}
973
974void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
975{
976 std::span<std::bitset<CacheLine::SIZE>, CacheLine::NUM> watchSet =
977 (type == WatchPoint::Type::READ_MEM) ? readWatchSet : writeWatchSet;
978 for (auto i : xrange(CacheLine::NUM)) {
979 watchSet[i].reset();
980 }
981 for (const auto& w : watchPoints) {
982 if (w->getType() == type) {
983 auto begin = w->getBeginAddress();
984 auto end = w->getEndAddress();
985 if (!begin || !end) continue;
986 assert(begin <= end);
987 for (unsigned addr = *begin; addr <= *end; ++addr) {
988 watchSet[addr >> CacheLine::BITS].set(
989 addr & CacheLine::LOW);
990 }
991 }
992 }
993 for (auto i : xrange(CacheLine::NUM)) {
994 if (readWatchSet [i].any()) {
995 disallowReadCache [i] |= MEMORY_WATCH_BIT;
996 } else {
997 disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
998 }
999 if (writeWatchSet[i].any()) {
1000 disallowWriteCache[i] |= MEMORY_WATCH_BIT;
1001 } else {
1002 disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
1003 }
1004 }
1005 msxcpu.invalidateAllSlotsRWCache(0x0000, 0x10000);
1006}
1007
1008void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
1009 unsigned address, unsigned value)
1010{
1011 assert(!watchPoints.empty());
1012 if (isFastForward()) return;
1013
1014 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
1015 auto& interp = motherBoard.getReactor().getInterpreter();
1016 interp.setVariable(TclObject("wp_last_address"),
1017 TclObject(int(address)));
1018 if (value != ~0u) {
1019 interp.setVariable(TclObject("wp_last_value"),
1020 TclObject(int(value)));
1021 }
1022
1023 auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
1024 for( auto wpCopy = watchPoints; auto& w : wpCopy) {
1025 if ((w->getBeginAddress() <= address) &&
1026 (w->getEndAddress() >= address) &&
1027 (w->getType() == type)) {
1028 bool remove = w->checkAndExecute(globalCliComm, interp);
1029 if (remove) {
1031 }
1032 }
1033 }
1034
1035 interp.unsetVariable("wp_last_address");
1036 interp.unsetVariable("wp_last_value");
1037}
1038
1039
1041{
1042 assert(!isFastForward());
1043 if (breaked) return;
1044 breaked = true;
1045 msxcpu.exitCPULoopSync();
1046
1047 Reactor& reactor = motherBoard.getReactor();
1048 reactor.block();
1049 breakedSetting->setReadOnlyValue(TclObject("true"));
1050 reactor.getCliComm().update(CliComm::UpdateType::STATUS, "cpu", "suspended");
1052}
1053
1055{
1056 assert(!isFastForward());
1057 DebugCondition dc; // cmd = debug break
1058 dc.setOnce(true);
1059 setCondition(std::move(dc));
1060 doContinue();
1061}
1062
1064{
1065 assert(!isFastForward());
1066 pauseSetting.setBoolean(false); // unpause
1067 if (breaked) {
1068 breaked = false;
1069
1070 Reactor& reactor = motherBoard.getReactor();
1071 breakedSetting->setReadOnlyValue(TclObject("false"));
1072 reactor.getCliComm().update(CliComm::UpdateType::STATUS, "cpu", "running");
1073 reactor.unblock();
1074 motherBoard.getRealTime().resync();
1075 }
1076}
1077
1079{
1080 // before the Tcl interpreter is destroyed, we must delete all
1081 // TclObjects. Breakpoints and conditions contain such objects
1082 // for the condition and action.
1083 // TODO it would be nicer if breakpoints and conditions were not
1084 // global objects.
1085 breakPoints.clear();
1086 conditions.clear();
1087}
1088
1090{
1091 assert(0 <= ps && ps < 4);
1092 assert(0 <= ss && ss < 4);
1093 assert(0 <= page && page < 4);
1094 return slotLayout[ps][ss][page];
1095}
1096
1097// class MemoryDebug
1098
1099MSXCPUInterface::MemoryDebug::MemoryDebug(MSXMotherBoard& motherBoard_)
1100 : SimpleDebuggable(motherBoard_, "memory",
1101 "The memory currently visible for the CPU.", 0x10000)
1102{
1103}
1104
1105byte MSXCPUInterface::MemoryDebug::read(unsigned address, EmuTime::param time)
1106{
1107 const auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1108 return interface.peekMem(narrow<word>(address), time);
1109}
1110
1111void MSXCPUInterface::MemoryDebug::write(unsigned address, byte value,
1112 EmuTime::param time)
1113{
1114 auto& interface = OUTER(MSXCPUInterface, memoryDebug);
1115 return interface.writeMem(narrow<word>(address), value, time);
1116}
1117
1118
1119// class SlottedMemoryDebug
1120
1121MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1122 MSXMotherBoard& motherBoard_)
1123 : SimpleDebuggable(motherBoard_, "slotted memory",
1124 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1125{
1126}
1127
1128byte MSXCPUInterface::SlottedMemoryDebug::read(unsigned address, EmuTime::param time)
1129{
1130 const auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1131 return interface.peekSlottedMem(address, time);
1132}
1133
1134void MSXCPUInterface::SlottedMemoryDebug::write(unsigned address, byte value,
1135 EmuTime::param time)
1136{
1137 auto& interface = OUTER(MSXCPUInterface, slottedMemoryDebug);
1138 return interface.writeSlottedMem(address, value, time);
1139}
1140
1141
1142// class SlotInfo
1143
1144static unsigned getSlot(
1145 Interpreter& interp, const TclObject& token, const std::string& itemName)
1146{
1147 unsigned slot = token.getInt(interp);
1148 if (slot >= 4) {
1149 throw CommandException(itemName, " must be in range 0..3");
1150 }
1151 return slot;
1152}
1153
1154MSXCPUInterface::SlotInfo::SlotInfo(
1155 InfoCommand& machineInfoCommand)
1156 : InfoTopic(machineInfoCommand, "slot")
1157{
1158}
1159
1160void MSXCPUInterface::SlotInfo::execute(std::span<const TclObject> tokens,
1161 TclObject& result) const
1162{
1163 checkNumArgs(tokens, 5, Prefix{2}, "primary secondary page");
1164 auto& interp = getInterpreter();
1165 unsigned ps = getSlot(interp, tokens[2], "Primary slot");
1166 unsigned ss = getSlot(interp, tokens[3], "Secondary slot");
1167 unsigned page = getSlot(interp, tokens[4], "Page");
1168 auto& interface = OUTER(MSXCPUInterface, slotInfo);
1169 if (!interface.isExpanded(narrow<int>(ps))) {
1170 ss = 0;
1171 }
1172 interface.slotLayout[ps][ss][page]->getNameList(result);
1173}
1174
1175std::string MSXCPUInterface::SlotInfo::help(std::span<const TclObject> /*tokens*/) const
1176{
1177 return "Retrieve name of the device inserted in given "
1178 "primary slot / secondary slot / page.";
1179}
1180
1181
1182// class SubSlottedInfo
1183
1184MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1185 InfoCommand& machineInfoCommand)
1186 : InfoTopic(machineInfoCommand, "issubslotted")
1187{
1188}
1189
1190void MSXCPUInterface::SubSlottedInfo::execute(std::span<const TclObject> tokens,
1191 TclObject& result) const
1192{
1193 checkNumArgs(tokens, 3, "primary");
1194 const auto& interface = OUTER(MSXCPUInterface, subSlottedInfo);
1195 result = interface.isExpanded(narrow<int>(
1196 getSlot(getInterpreter(), tokens[2], "Slot")));
1197}
1198
1199std::string MSXCPUInterface::SubSlottedInfo::help(
1200 std::span<const TclObject> /*tokens*/) const
1201{
1202 return "Indicates whether a certain primary slot is expanded.";
1203}
1204
1205
1206// class ExternalSlotInfo
1207
1208MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1209 InfoCommand& machineInfoCommand)
1210 : InfoTopic(machineInfoCommand, "isexternalslot")
1211{
1212}
1213
1214void MSXCPUInterface::ExternalSlotInfo::execute(
1215 std::span<const TclObject> tokens, TclObject& result) const
1216{
1217 checkNumArgs(tokens, Between{3, 4}, "primary ?secondary?");
1218 int ps = 0;
1219 int ss = 0;
1220 auto& interp = getInterpreter();
1221 switch (tokens.size()) {
1222 case 4:
1223 ss = narrow<int>(getSlot(interp, tokens[3], "Secondary slot"));
1224 [[fallthrough]];
1225 case 3:
1226 ps = narrow<int>(getSlot(interp, tokens[2], "Primary slot"));
1227 break;
1228 }
1229 const auto& interface = OUTER(MSXCPUInterface, externalSlotInfo);
1230 const auto& manager = interface.motherBoard.getSlotManager();
1231 result = manager.isExternalSlot(ps, ss, true);
1232}
1233
1234std::string MSXCPUInterface::ExternalSlotInfo::help(
1235 std::span<const TclObject> /*tokens*/) const
1236{
1237 return "Indicates whether a certain slot is external or internal.";
1238}
1239
1240
1241// class IODebug
1242
1243MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1244 : SimpleDebuggable(motherBoard_, "ioports", "IO ports.", 0x100)
1245{
1246}
1247
1248byte MSXCPUInterface::IODebug::read(unsigned address, EmuTime::param time)
1249{
1250 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1251 return interface.IO_In[address & 0xFF]->peekIO(narrow<word>(address), time);
1252}
1253
1254void MSXCPUInterface::IODebug::write(unsigned address, byte value, EmuTime::param time)
1255{
1256 auto& interface = OUTER(MSXCPUInterface, ioDebug);
1257 interface.writeIO(word(address), value, time);
1258}
1259
1260
1261// class IOInfo
1262
1263MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand, const char* name_)
1264 : InfoTopic(machineInfoCommand, name_)
1265{
1266}
1267
1268void MSXCPUInterface::IOInfo::helper(
1269 std::span<const TclObject> tokens, TclObject& result, std::span<MSXDevice*, 256> devices) const
1270{
1271 checkNumArgs(tokens, 3, "port");
1272 unsigned port = tokens[2].getInt(getInterpreter());
1273 if (port >= 256) {
1274 throw CommandException("Port must be in range 0..255");
1275 }
1276 devices[port]->getNameList(result);
1277}
1278void MSXCPUInterface::IInfo::execute(
1279 std::span<const TclObject> tokens, TclObject& result) const
1280{
1281 auto& interface = OUTER(MSXCPUInterface, inputPortInfo);
1282 helper(tokens, result, interface.IO_In);
1283}
1284void MSXCPUInterface::OInfo::execute(
1285 std::span<const TclObject> tokens, TclObject& result) const
1286{
1287 auto& interface = OUTER(MSXCPUInterface, outputPortInfo);
1288 helper(tokens, result, interface.IO_Out);
1289}
1290
1291std::string MSXCPUInterface::IOInfo::help(std::span<const TclObject> /*tokens*/) const
1292{
1293 return "Return the name of the device connected to the given IO port.";
1294}
1295
1296
1297template<typename Archive>
1298void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1299{
1300 // TODO watchPoints ???
1301
1302 // primary and 4 secondary slot select registers
1303 byte prim = 0;
1304 if constexpr (!Archive::IS_LOADER) {
1305 for (auto i : xrange(4)) {
1306 prim |= byte(primarySlotState[i] << (2 * i));
1307 }
1308 }
1309 ar.serialize("primarySlots", prim,
1310 "subSlotRegs", subSlotRegister);
1311 if constexpr (Archive::IS_LOADER) {
1312 setPrimarySlots(prim);
1313 for (auto i : xrange(byte(4))) {
1314 setSubSlot(i, subSlotRegister[i]);
1315 }
1316 }
1317
1318 if (delayDevice) {
1319 ar.serialize("vdpDelay", *delayDevice);
1320 }
1321}
1323
1324} // namespace openmsx
int g
void setOnce(Interpreter &interp, const TclObject &o)
std::string getIdStr() const
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.
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
void registerIOWatch(MSXMotherBoard &motherBoard, std::span< MSXDevice *, 256 > devices)
Definition WatchPoint.cc:20
Type getType() const
#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
#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
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)