38 using std::shared_ptr;
42 static std::unique_ptr<ReadOnlySetting> breakedSetting;
43 static unsigned breakedSettingCount = 0;
53 return os <<
"CacheLineCounters";
67 "InvalidateReadWrite",
74 return os << names[size_t(evn.
e)];
78 : memoryDebug (motherBoard_)
79 , slottedMemoryDebug(motherBoard_)
80 , ioDebug (motherBoard_)
81 , slotInfo(motherBoard_.getMachineInfoCommand())
82 , subSlottedInfo(motherBoard_.getMachineInfoCommand())
83 , externalSlotInfo(motherBoard_.getMachineInfoCommand())
84 , inputPortInfo (motherBoard_.getMachineInfoCommand())
85 , outputPortInfo(motherBoard_.getMachineInfoCommand())
87 *motherBoard_.getMachineConfig()))
88 , msxcpu(motherBoard_.getCPU())
89 , cliComm(motherBoard_.getMSXCliComm())
90 , motherBoard(motherBoard_)
100 for (
auto& sub1 : slotLayout) {
101 for (
auto& sub2 : sub1) {
107 memset(disallowReadCache, 0,
sizeof(disallowReadCache));
108 memset(disallowWriteCache, 0,
sizeof(disallowWriteCache));
119 for (
auto port :
xrange(0x98, 0x9c)) {
120 assert(IO_In [port] == dummyDevice.get());
121 assert(IO_Out[port] == dummyDevice.get());
122 IO_In [port] = delayDevice.get();
123 IO_Out[port] = delayDevice.get();
127 if (breakedSettingCount++ == 0) {
128 assert(!breakedSetting);
129 breakedSetting = std::make_unique<ReadOnlySetting>(
131 "breaked",
"Similar to 'debug breaked'",
139 if (--breakedSettingCount == 0) {
140 assert(breakedSetting);
141 breakedSetting =
nullptr;
144 removeAllWatchPoints();
147 for (
auto port :
xrange(0x98, 0x9c)) {
148 assert(IO_In [port] == delayDevice.get());
149 assert(IO_Out[port] == delayDevice.get());
150 IO_In [port] = dummyDevice.get();
151 IO_Out[port] = dummyDevice.get();
158 for (
auto port :
xrange(256)) {
159 if (IO_In[port] != dummyDevice.get()) {
160 std::cout <<
"In-port " << port <<
" still registered "
161 << IO_In[port]->
getName() <<
'\n';
164 if (IO_Out[port] != dummyDevice.get()) {
165 std::cout <<
"Out-port " << port <<
" still registered "
166 << IO_Out[port]->
getName() <<
'\n';
170 for (
auto primSlot :
xrange(4)) {
172 for (
auto secSlot :
xrange(4)) {
173 for (
auto page :
xrange(4)) {
174 assert(slotLayout[primSlot][secSlot][page] == dummyDevice.get());
181 void MSXCPUInterface::removeAllWatchPoints()
183 while (!watchPoints.empty()) {
189 byte MSXCPUInterface::readMemSlow(
word address, EmuTime::param time)
195 for (
auto&
g : globalReads) {
199 g.device->globalRead(address, time);
209 return 0xFF ^ subSlotRegister[primarySlotState[3]];
211 return visibleDevices[address >> 14]->
readMem(address, time);
215 void MSXCPUInterface::writeMemSlow(
word address,
byte value, EmuTime::param time)
219 setSubSlot(primarySlotState[3], value);
224 visibleDevices[address>>14]->
writeMem(address, value, time);
229 for (
auto&
g : globalWrites) {
233 g.device->globalWrite(address, value, time);
246 if (expanded[ps] == 0) {
247 for (
auto page :
xrange(4)) {
248 if (slotLayout[ps][0][page] != dummyDevice.get()) {
250 "it's already in use.");
259 int ps, vector<MSXDevice*> allowed)
const
262 allowed.push_back(dummyDevice.get());
265 if (expanded[ps] != 1)
return;
267 std::vector<MSXDevice*> inUse;
268 for (
auto ss :
xrange(4)) {
269 for (
auto page :
xrange(4)) {
270 MSXDevice* device = slotLayout[ps][ss][page];
271 std::vector<MSXDevice*> devices;
272 std::vector<MSXDevice*>::iterator end_devices;
274 devices = memDev->getDevices();
278 devices.push_back(device);
279 end_devices =
end(devices);
283 std::inserter(inUse,
end(inUse)));
287 if (inUse.empty())
return;
289 string msg =
strCat(
"Can't remove slot expander from slot ", ps,
290 " because the following devices are still inserted:");
291 for (
auto& d : inUse) {
302 vector<MSXDevice*> dummy;
324 MSXDevice*& MSXCPUInterface::getDevicePtr(
byte port,
bool isIn)
326 MSXDevice** devicePtr = isIn ? &IO_In[port] : &IO_Out[port];
328 devicePtr = &watch->getDevicePtr();
330 if (*devicePtr == delayDevice.get()) {
331 devicePtr = isIn ? &delayDevice->getInDevicePtr (port)
332 : &delayDevice->getOutDevicePtr(port);
339 MSXDevice*& devicePtr = getDevicePtr(port,
true);
340 register_IO(port,
true, devicePtr, device);
345 MSXDevice*& devicePtr = getDevicePtr(port,
true);
346 unregister_IO(devicePtr, device);
351 MSXDevice*& devicePtr = getDevicePtr(port,
false);
352 register_IO(port,
false, devicePtr, device);
357 MSXDevice*& devicePtr = getDevicePtr(port,
false);
358 unregister_IO(devicePtr, device);
361 void MSXCPUInterface::register_IO(
int port,
bool isIn,
364 if (devicePtr == dummyDevice.get()) {
368 if (
auto* multi =
dynamic_cast<MSXMultiIODevice*
>(devicePtr)) {
370 multi->addDevice(device);
374 multi->addDevice(devicePtr);
375 multi->addDevice(device);
380 "Conflicting input port 0x",
382 " for devices ", devicePtr->
getName());
389 if (
auto* multi =
dynamic_cast<MSXMultiIODevice*
>(devicePtr)) {
391 multi->removeDevice(device);
392 auto& devices = multi->getDevices();
393 if (devices.size() == 1) {
395 devicePtr = devices.front();
401 assert(devicePtr == device);
402 devicePtr = dummyDevice.get();
409 MSXDevice*& devicePtr = getDevicePtr(port,
true);
410 if (devicePtr != oldDevice) {
414 devicePtr = newDevice;
420 MSXDevice*& devicePtr = getDevicePtr(port,
false);
421 if (devicePtr != oldDevice) {
425 devicePtr = newDevice;
432 "Overlapping memory devices in slot ", ps,
'.', ss,
436 void MSXCPUInterface::testRegisterSlot(
439 int page = base >> 14;
440 MSXDevice*& slot = slotLayout[ps][ss][page];
441 if (
size == 0x4000) {
443 if (slot != dummyDevice.get()) {
444 reportMemOverlap(ps, ss, *slot, device);
448 if (slot == dummyDevice.get()) {
450 }
else if (
auto* multi =
dynamic_cast<MSXMultiMemDevice*
>(slot)) {
452 if (!multi->canAdd(base,
size)) {
453 reportMemOverlap(ps, ss, *slot, device);
457 reportMemOverlap(ps, ss, *slot, device);
462 void MSXCPUInterface::registerSlot(
465 int page = base >> 14;
466 MSXDevice*& slot = slotLayout[ps][ss][page];
467 if (
size == 0x4000) {
469 assert(slot == dummyDevice.get());
473 if (slot == dummyDevice.get()) {
475 auto* multi =
new MSXMultiMemDevice(device.getHardwareConfig());
476 multi->add(device, base,
size);
478 }
else if (
auto* multi =
dynamic_cast<MSXMultiMemDevice*
>(slot)) {
480 assert(multi->canAdd(base,
size));
481 multi->add(device, base,
size);
491 void MSXCPUInterface::unregisterSlot(
494 int page = base >> 14;
495 MSXDevice*& slot = slotLayout[ps][ss][page];
496 if (
auto* multi =
dynamic_cast<MSXMultiMemDevice*
>(slot)) {
498 multi->remove(device, base,
size);
499 if (multi->empty()) {
501 slot = dummyDevice.get();
505 assert(slot == &device);
506 slot = dummyDevice.get();
513 MSXDevice& device,
int ps,
int ss,
int base_,
int size_)
517 "Slot ", ps,
'.', ss,
518 " does not exist because slot is not expanded.");
526 int partialSize =
min(
size, ((base + 0x4000) & ~0x3FFF) - base);
527 testRegisterSlot(device, ps, ss, base, partialSize);
535 int partialSize =
min(
size, ((base + 0x4000) & ~0x3FFF) - base);
536 registerSlot(device, ps, ss, base, partialSize);
547 int partialSize =
min(
size, ((base + 0x4000) & ~0x3FFF) - base);
548 unregisterSlot(device, ps, ss, base, partialSize);
556 globalWrites.push_back({&device, address});
564 GlobalRwInfo info = { &device, address };
567 for (
auto&
g : globalWrites) {
580 globalReads.push_back({&device, address});
588 GlobalRwInfo info = { &device, address };
591 for (
auto&
g : globalReads) {
602 ALWAYS_INLINE void MSXCPUInterface::updateVisible(
int page,
int ps,
int ss)
604 MSXDevice* newDevice = slotLayout[ps][ss][page];
605 if (visibleDevices[page] != newDevice) {
606 visibleDevices[page] = newDevice;
610 void MSXCPUInterface::updateVisible(
int page)
612 updateVisible(page, primarySlotState[page], secondarySlotState[page]);
634 msxcpu.
fillRWCache(start,
size, rData, wData, ps, ss, disallowReadCache, disallowWriteCache);
644 msxcpu.
fillWCache(start,
size, wData, ps, ss, disallowWriteCache);
649 for (
auto i :
xrange(4)) {
674 int ps0 = (value >> 0) & 3;
675 if (
unlikely(primarySlotState[0] != ps0)) {
676 primarySlotState[0] = ps0;
677 int ss0 = (subSlotRegister[ps0] >> 0) & 3;
678 secondarySlotState[0] = ss0;
679 updateVisible(0, ps0, ss0);
681 int ps1 = (value >> 2) & 3;
682 if (
unlikely(primarySlotState[1] != ps1)) {
683 primarySlotState[1] = ps1;
684 int ss1 = (subSlotRegister[ps1] >> 2) & 3;
685 secondarySlotState[1] = ss1;
686 updateVisible(1, ps1, ss1);
688 int ps2 = (value >> 4) & 3;
689 if (
unlikely(primarySlotState[2] != ps2)) {
690 primarySlotState[2] = ps2;
691 int ss2 = (subSlotRegister[ps2] >> 4) & 3;
692 secondarySlotState[2] = ss2;
693 updateVisible(2, ps2, ss2);
695 int ps3 = (value >> 6) & 3;
696 if (
unlikely(primarySlotState[3] != ps3)) {
697 bool oldExpanded =
isExpanded(primarySlotState[3]);
699 primarySlotState[3] = ps3;
700 int ss3 = (subSlotRegister[ps3] >> 6) & 3;
701 secondarySlotState[3] = ss3;
702 updateVisible(3, ps3, ss3);
703 if (
unlikely(oldExpanded != newExpanded)) {
709 void MSXCPUInterface::setSubSlot(
byte primSlot,
byte value)
711 subSlotRegister[primSlot] = value;
712 for (
int page = 0; page < 4; ++page, value >>= 2) {
713 if (primSlot == primarySlotState[page]) {
714 secondarySlotState[page] = value & 3;
723 if ((address == 0xFFFF) &&
isExpanded(primarySlotState[3])) {
724 return 0xFF ^ subSlotRegister[primarySlotState[3]];
726 return visibleDevices[address >> 14]->
peekMem(address, time);
732 byte primSlot = (address & 0xC0000) >> 18;
733 byte subSlot = (address & 0x30000) >> 16;
734 byte page = (address & 0x0C000) >> 14;
735 word offset = (address & 0xFFFF);
740 if ((offset == 0xFFFF) &&
isExpanded(primSlot)) {
741 return 0xFF ^ subSlotRegister[primSlot];
743 return slotLayout[primSlot][subSlot][page]->
peekMem(offset, time);
749 byte primSlot = (address & 0xC0000) >> 18;
750 byte subSlot = (address & 0x30000) >> 16;
751 byte page = (address & 0x0C000) >> 14;
752 word offset = (address & 0xFFFF);
757 if ((offset == 0xFFFF) &&
isExpanded(primSlot)) {
758 return 0xFF ^ subSlotRegister[primSlot];
760 return slotLayout[primSlot][subSlot][page]->
peekMem(offset, time);
767 byte primSlot = (address & 0xC0000) >> 18;
768 byte subSlot = (address & 0x30000) >> 16;
769 byte page = (address & 0x0C000) >> 14;
770 word offset = (address & 0xFFFF);
775 if ((offset == 0xFFFF) &&
isExpanded(primSlot)) {
776 setSubSlot(primSlot, value);
778 slotLayout[primSlot][subSlot][page]->
writeMem(offset, value, time);
785 breakPoints.insert(it, std::move(bp));
792 [&](
const BreakPoint& i) {
return &i == &bp; }));
799 it != breakPoints.end()) {
800 breakPoints.erase(it);
805 std::pair<BreakPoints::const_iterator,
806 BreakPoints::const_iterator> range,
807 MSXMotherBoard& motherBoard)
813 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
814 auto& interp = motherBoard.getReactor().getInterpreter();
815 for (
auto& p : bpCopy) {
816 p.checkAndExecute(globalCliComm, interp);
821 auto condCopy = conditions;
822 for (
auto& c : condCopy) {
823 c.checkAndExecute(globalCliComm, interp);
830 static void registerIOWatch(WatchPoint& watchPoint,
MSXDevice** devices)
832 assert(
dynamic_cast<WatchIO*
>(&watchPoint));
833 auto& ioWatch =
static_cast<WatchIO&
>(watchPoint);
834 unsigned beginPort = ioWatch.getBeginAddress();
835 unsigned endPort = ioWatch.getEndAddress();
836 assert(beginPort <= endPort);
837 assert(endPort < 0x100);
838 for (
unsigned port = beginPort; port <= endPort; ++port) {
839 ioWatch.getDevice(port).getDevicePtr() = devices[port];
840 devices[port] = &ioWatch.getDevice(port);
846 watchPoints.push_back(watchPoint);
850 registerIOWatch(*watchPoint, IO_In);
853 registerIOWatch(*watchPoint, IO_Out);
857 updateMemWatch(type);
866 assert(
dynamic_cast<WatchIO*
>(&watchPoint));
867 auto& ioWatch =
static_cast<WatchIO&
>(watchPoint);
869 unsigned endPort = ioWatch.getEndAddress();
870 assert(beginPort <= endPort);
871 assert(endPort < 0x100);
873 for (
unsigned port = beginPort; port <= endPort; ++port) {
876 while (*prev != &ioWatch.getDevice(port)) {
877 prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
880 *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
890 it !=
end(watchPoints)) {
892 watchPoints.erase(it);
896 unregisterIOWatch(*watchPoint, IO_In);
899 unregisterIOWatch(*watchPoint, IO_Out);
903 updateMemWatch(type);
913 conditions.push_back(std::move(cond));
927 it != conditions.end()) {
928 conditions.erase(it);
934 std::bitset<CacheLine::SIZE>* watchSet =
939 for (
auto& w : watchPoints) {
940 if (w->getType() == type) {
941 unsigned beginAddr = w->getBeginAddress();
942 unsigned endAddr = w->getEndAddress();
943 assert(beginAddr <= endAddr);
944 assert(endAddr < 0x10000);
945 for (
unsigned addr = beginAddr; addr <= endAddr; ++addr) {
952 if (readWatchSet [i].any()) {
957 if (writeWatchSet[i].any()) {
967 unsigned address,
unsigned value)
969 assert(!watchPoints.empty());
972 auto& globalCliComm = motherBoard.getReactor().getGlobalCliComm();
973 auto& interp = motherBoard.getReactor().getInterpreter();
974 interp.setVariable(TclObject(
"wp_last_address"),
975 TclObject(
int(address)));
977 interp.setVariable(TclObject(
"wp_last_value"),
978 TclObject(
int(value)));
981 auto wpCopy = watchPoints;
982 for (
auto& w : wpCopy) {
983 if ((w->getBeginAddress() <= address) &&
984 (w->getEndAddress() >= address) &&
985 (w->getType() == type)) {
986 w->checkAndExecute(globalCliComm, interp);
993 interp.unsetVariable(
"wp_last_address");
994 interp.unsetVariable(
"wp_last_value");
1001 if (breaked)
return;
1005 Reactor& reactor = motherBoard.getReactor();
1007 breakedSetting->setReadOnlyValue(
TclObject(
"true"));
1027 Reactor& reactor = motherBoard.getReactor();
1028 breakedSetting->setReadOnlyValue(
TclObject(
"false"));
1031 motherBoard.getRealTime().resync();
1042 breakPoints.clear();
1049 MSXCPUInterface::MemoryDebug::MemoryDebug(
MSXMotherBoard& motherBoard_)
1051 "The memory currently visible for the CPU.", 0x10000)
1055 byte MSXCPUInterface::MemoryDebug::read(
unsigned address, EmuTime::param time)
1058 return interface.peekMem(address, time);
1061 void MSXCPUInterface::MemoryDebug::write(
unsigned address,
byte value,
1062 EmuTime::param time)
1065 return interface.writeMem(address, value, time);
1071 MSXCPUInterface::SlottedMemoryDebug::SlottedMemoryDebug(
1072 MSXMotherBoard& motherBoard_)
1073 : SimpleDebuggable(motherBoard_,
"slotted memory",
1074 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1078 byte MSXCPUInterface::SlottedMemoryDebug::read(
unsigned address, EmuTime::param time)
1081 return interface.peekSlottedMem(address, time);
1084 void MSXCPUInterface::SlottedMemoryDebug::write(
unsigned address,
byte value,
1085 EmuTime::param time)
1088 return interface.writeSlottedMem(address, value, time);
1094 static unsigned getSlot(
1095 Interpreter& interp,
const TclObject& token,
const string& itemName)
1097 unsigned slot = token.getInt(interp);
1099 throw CommandException(itemName,
" must be in range 0..3");
1104 MSXCPUInterface::SlotInfo::SlotInfo(
1105 InfoCommand& machineInfoCommand)
1106 : InfoTopic(machineInfoCommand,
"slot")
1111 TclObject& result)
const
1113 checkNumArgs(tokens, 5, Prefix{2},
"primary secondary page");
1114 auto& interp = getInterpreter();
1115 unsigned ps = getSlot(interp, tokens[2],
"Primary slot");
1116 unsigned ss = getSlot(interp, tokens[3],
"Secondary slot");
1117 unsigned page = getSlot(interp, tokens[4],
"Page");
1119 if (!interface.isExpanded(ps)) {
1122 interface.slotLayout[ps][ss][page]->getNameList(result);
1125 string MSXCPUInterface::SlotInfo::help(
const vector<string>& )
const
1127 return "Retrieve name of the device inserted in given "
1128 "primary slot / secondary slot / page.";
1134 MSXCPUInterface::SubSlottedInfo::SubSlottedInfo(
1135 InfoCommand& machineInfoCommand)
1136 : InfoTopic(machineInfoCommand,
"issubslotted")
1141 TclObject& result)
const
1143 checkNumArgs(tokens, 3,
"primary");
1145 result = interface.isExpanded(
1146 getSlot(getInterpreter(), tokens[2],
"Slot"));
1149 string MSXCPUInterface::SubSlottedInfo::help(
1150 const vector<string>& )
const
1152 return "Indicates whether a certain primary slot is expanded.";
1158 MSXCPUInterface::ExternalSlotInfo::ExternalSlotInfo(
1159 InfoCommand& machineInfoCommand)
1160 : InfoTopic(machineInfoCommand,
"isexternalslot")
1164 void MSXCPUInterface::ExternalSlotInfo::execute(
1167 checkNumArgs(tokens, Between{3, 4},
"primary ?secondary?");
1170 auto& interp = getInterpreter();
1171 switch (tokens.
size()) {
1173 ss = getSlot(interp, tokens[3],
"Secondary slot");
1176 ps = getSlot(interp, tokens[2],
"Primary slot");
1180 auto& manager = interface.motherBoard.getSlotManager();
1181 result = manager.isExternalSlot(ps, ss,
true);
1184 string MSXCPUInterface::ExternalSlotInfo::help(
1185 const vector<string>& )
const
1187 return "Indicates whether a certain slot is external or internal.";
1193 MSXCPUInterface::IODebug::IODebug(MSXMotherBoard& motherBoard_)
1194 : SimpleDebuggable(motherBoard_,
"ioports",
"IO ports.", 0x100)
1198 byte MSXCPUInterface::IODebug::read(
unsigned address, EmuTime::param time)
1201 return interface.IO_In[address & 0xFF]->peekIO(address, time);
1204 void MSXCPUInterface::IODebug::write(
unsigned address,
byte value, EmuTime::param time)
1207 interface.writeIO(
word(address), value, time);
1213 MSXCPUInterface::IOInfo::IOInfo(InfoCommand& machineInfoCommand,
const char* name_)
1214 : InfoTopic(machineInfoCommand, name_)
1218 void MSXCPUInterface::IOInfo::helper(
1221 checkNumArgs(tokens, 3,
"port");
1222 unsigned port = tokens[2].getInt(getInterpreter());
1224 throw CommandException(
"Port must be in range 0..255");
1226 devices[port]->getNameList(result);
1228 void MSXCPUInterface::IInfo::execute(
1232 helper(tokens, result, interface.IO_In);
1234 void MSXCPUInterface::OInfo::execute(
1238 helper(tokens, result, interface.IO_Out);
1241 string MSXCPUInterface::IOInfo::help(
const vector<string>& )
const
1243 return "Return the name of the device connected to the given IO port.";
1247 template<
typename Archive>
1254 if (!ar.isLoader()) {
1255 for (
auto i :
xrange(4)) {
1256 prim |= primarySlotState[i] << (2 * i);
1259 ar.serialize(
"primarySlots", prim,
1260 "subSlotRegs", subSlotRegister);
1261 if (ar.isLoader()) {
1263 for (
auto i :
xrange(4)) {
1264 setSubSlot(i, subSlotRegister[i]);
1269 ar.serialize(
"vdpDelay", *delayDevice);