38 using std::ostringstream;
45 using std::shared_ptr;
87 virtual void execute(
const vector<TclObject>& tokens,
89 virtual string help(
const vector<string>& tokens)
const;
99 virtual void execute(
const vector<TclObject>& tokens,
101 virtual string help(
const vector<string>& tokens)
const;
111 virtual void execute(
const vector<TclObject>& tokens,
113 virtual string help(
const vector<string>& tokens)
const;
123 virtual void execute(
const vector<TclObject>& tokens,
125 virtual string help(
const vector<string>& tokens)
const;
133 bool MSXCPUInterface::breaked =
false;
134 bool MSXCPUInterface::continued =
false;
135 bool MSXCPUInterface::step =
false;
140 static std::unique_ptr<ReadOnlySetting<BooleanSetting>> breakedSetting;
141 static unsigned breakedSettingCount = 0;
145 static const byte SECUNDARY_SLOT_BIT = 0x01;
146 static const byte MEMORY_WATCH_BIT = 0x02;
147 static const byte GLOBAL_WRITE_BIT = 0x04;
152 *this, motherBoard_))
154 *this, motherBoard_))
156 *this, motherBoard_))
158 motherBoard_.getMachineInfoCommand(), *this))
160 motherBoard_.getMachineInfoCommand(), *this))
162 motherBoard_.getMachineInfoCommand(),
163 motherBoard_.getSlotManager()))
165 motherBoard_.getMachineInfoCommand(), *this, true))
167 motherBoard_.getMachineInfoCommand(), *this, false))
169 *motherBoard_.getMachineConfig()))
170 , msxcpu(motherBoard_.getCPU())
171 , cliComm(motherBoard_.getMSXCliComm())
172 , motherBoard(motherBoard_)
175 for (
int port = 0; port < 256; ++port) {
176 IO_In [port] = dummyDevice.get();
177 IO_Out[port] = dummyDevice.get();
179 for (
int primSlot = 0; primSlot < 4; ++primSlot) {
180 primarySlotState[primSlot] = 0;
181 secondarySlotState[primSlot] = 0;
182 expanded[primSlot] = 0;
183 subSlotRegister[primSlot] = 0;
184 for (
int secSlot = 0; secSlot < 4; ++secSlot) {
185 for (
int page = 0; page < 4; ++page) {
186 slotLayout[primSlot][secSlot][page] = dummyDevice.get();
190 for (
int page = 0; page < 4; ++page) {
191 visibleDevices[page] = dummyDevice.get();
195 memset(disallowReadCache, 0,
sizeof(disallowReadCache));
196 memset(disallowWriteCache, 0,
sizeof(disallowWriteCache));
206 for (
int port = 0x98; port <= 0x9B; ++port) {
207 assert(IO_In [port] == dummyDevice.get());
208 assert(IO_Out[port] == dummyDevice.get());
209 IO_In [port] = delayDevice.get();
210 IO_Out[port] = delayDevice.get();
214 if (breakedSettingCount++ == 0) {
215 assert(!breakedSetting.get());
216 breakedSetting = make_unique<ReadOnlySetting<BooleanSetting>>(
218 "breaked",
"Similar to 'debug breaked'",
false);
224 if (--breakedSettingCount == 0) {
225 assert(breakedSetting.get());
226 breakedSetting =
nullptr;
229 removeAllWatchPoints();
231 if (delayDevice.get()) {
232 for (
int port = 0x98; port <= 0x9B; ++port) {
233 assert(IO_In [port] == delayDevice.get());
234 assert(IO_Out[port] == delayDevice.get());
235 IO_In [port] = dummyDevice.get();
236 IO_Out[port] = dummyDevice.get();
243 for (
int port = 0; port < 256; ++port) {
244 if (IO_In[port] != dummyDevice.get()) {
245 std::cout <<
"In-port " << port <<
" still registered "
246 << IO_In[port]->
getName() << std::endl;
249 if (IO_Out[port] != dummyDevice.get()) {
250 std::cout <<
"Out-port " << port <<
" still registered "
251 << IO_Out[port]->
getName() << std::endl;
255 for (
int primSlot = 0; primSlot < 4; ++primSlot) {
257 for (
int secSlot = 0; secSlot < 4; ++secSlot) {
258 for (
int page = 0; page < 4; ++page) {
259 assert(slotLayout[primSlot][secSlot][page] == dummyDevice.get());
266 void MSXCPUInterface::removeAllWatchPoints()
268 while (!watchPoints.empty()) {
277 if (
unlikely(disallowReadCache[address >> CacheLine::BITS])) {
279 if (readWatchSet[address >> CacheLine::BITS]
280 [address & CacheLine::LOW]) {
285 return 0xFF ^ subSlotRegister[primarySlotState[3]];
287 return visibleDevices[address >> 14]->
readMem(address, time);
294 setSubSlot(primarySlotState[3], value);
296 visibleDevices[address>>14]->
writeMem(address, value, time);
299 if (
unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
301 for (
auto& g : globalWrites) {
305 g.device->globalWrite(address, value, time);
309 if (writeWatchSet[address >> CacheLine::BITS]
310 [address & CacheLine::LOW]) {
318 if (expanded[ps] == 0) {
319 for (
int page = 0; page < 4; ++page) {
320 if (slotLayout[ps][0][page] != dummyDevice.get()) {
322 "it's already in use.");
331 int ps, vector<MSXDevice*> allowed)
const
334 allowed.push_back(dummyDevice.get());
335 sort(allowed.begin(), allowed.end());
337 if (expanded[ps] != 1)
return;
339 std::vector<MSXDevice*> inUse;
340 for (
int ss = 0; ss < 4; ++ss) {
341 for (
int page = 0; page < 4; ++page) {
342 MSXDevice* device = slotLayout[ps][ss][page];
343 std::vector<MSXDevice*> devices;
344 if (
auto memDev = dynamic_cast<MSXMultiMemDevice*>(device)) {
345 devices = memDev->getDevices();
346 sort(devices.begin(), devices.end());
348 devices.push_back(device);
350 std::set_difference(devices.begin(), devices.end(),
351 allowed.begin(), allowed.end(),
352 std::inserter(inUse, inUse.end()));
356 if (inUse.empty())
return;
359 msg <<
"Can't remove slot expander from slot " << ps
360 <<
" because the following devices are still inserted:";
361 for (
auto& d : inUse) {
362 msg <<
" " << d->getName();
372 vector<MSXDevice*> dummy;
385 disallowReadCache [0xFF] |= SECUNDARY_SLOT_BIT;
386 disallowWriteCache[0xFF] |= SECUNDARY_SLOT_BIT;
388 disallowReadCache [0xFF] &= ~SECUNDARY_SLOT_BIT;
389 disallowWriteCache[0xFF] &= ~SECUNDARY_SLOT_BIT;
394 MSXDevice*& MSXCPUInterface::getDevicePtr(
byte port,
bool isIn)
396 MSXDevice** devicePtr = isIn ? &IO_In[port] : &IO_Out[port];
397 while (
auto watch = dynamic_cast<MSXWatchIODevice*>(*devicePtr)) {
398 devicePtr = &watch->getDevicePtr();
400 if (*devicePtr == delayDevice.get()) {
401 devicePtr = isIn ? &delayDevice->getInDevicePtr (port)
402 : &delayDevice->getOutDevicePtr(port);
409 MSXDevice*& devicePtr = getDevicePtr(port,
true);
410 register_IO(port,
true, devicePtr, device);
415 MSXDevice*& devicePtr = getDevicePtr(port,
true);
416 unregister_IO(devicePtr, device);
421 MSXDevice*& devicePtr = getDevicePtr(port,
false);
422 register_IO(port,
false, devicePtr, device);
427 MSXDevice*& devicePtr = getDevicePtr(port,
false);
428 unregister_IO(devicePtr, device);
431 void MSXCPUInterface::register_IO(
int port,
bool isIn,
435 "-port " << std::hex << port << std::dec);
437 if (devicePtr == dummyDevice.get()) {
441 if (
auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
443 multi->addDevice(device);
447 multi->addDevice(devicePtr);
448 multi->addDevice(device);
453 "Conflicting input port 0x" +
455 " for devices " + devicePtr->
getName());
462 if (
auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
464 multi->removeDevice(device);
465 auto& devices = multi->getDevices();
466 if (devices.size() == 1) {
468 devicePtr = devices.front();
474 assert(devicePtr == device);
475 devicePtr = dummyDevice.get();
483 "Overlapping memory devices in slot " << ps <<
'.' << ss <<
484 ": " << dev1.getName() <<
" and " << dev2.getName() <<
'.');
487 void MSXCPUInterface::testRegisterSlot(
490 int page = base >> 14;
491 MSXDevice*& slot = slotLayout[ps][ss][page];
492 if (size == 0x4000) {
494 if (slot != dummyDevice.get()) {
495 reportMemOverlap(ps, ss, *slot, device);
499 if (slot == dummyDevice.get()) {
501 }
else if (
auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
503 if (!multi->canAdd(base, size)) {
504 reportMemOverlap(ps, ss, *slot, device);
508 reportMemOverlap(ps, ss, *slot, device);
513 void MSXCPUInterface::registerSlot(
514 MSXDevice& device,
int ps,
int ss,
int base,
int size)
516 PRT_DEBUG(device.getName() <<
" registers at " <<
517 std::dec << ps <<
' ' << ss <<
" 0x" <<
518 std::hex << base <<
"-0x" << (base + size - 1));
519 int page = base >> 14;
520 MSXDevice*& slot = slotLayout[ps][ss][page];
521 if (size == 0x4000) {
523 assert(slot == dummyDevice.get());
527 if (slot == dummyDevice.get()) {
529 MSXMultiMemDevice* multi =
530 new MSXMultiMemDevice(device.getHardwareConfig());
531 multi->add(device, base, size);
533 }
else if (
auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
535 assert(multi->canAdd(base, size));
536 multi->add(device, base, size);
545 void MSXCPUInterface::unregisterSlot(
546 MSXDevice& device,
int ps,
int ss,
int base,
int size)
548 int page = base >> 14;
549 MSXDevice*& slot = slotLayout[ps][ss][page];
550 if (
auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
552 multi->remove(device, base, size);
553 if (multi->empty()) {
555 slot = dummyDevice.get();
559 assert(slot == &device);
560 slot = dummyDevice.get();
566 MSXDevice& device,
int ps,
int ss,
int base_,
int size_)
570 "Slot " << ps <<
'.' << ss <<
571 " does not exist because slot is not expanded.");
579 int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
580 testRegisterSlot(device, ps, ss, base, partialSize);
588 int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
589 registerSlot(device, ps, ss, base, partialSize);
596 MSXDevice& device,
int ps,
int ss,
int base,
int size)
600 int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
601 unregisterSlot(device, ps, ss, base, partialSize);
609 GlobalWriteInfo info = { &device, address };
610 globalWrites.push_back(info);
612 disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_WRITE_BIT;
618 GlobalWriteInfo info = { &device, address };
619 auto it = find(globalWrites.begin(), globalWrites.end(), info);
620 assert(it != globalWrites.end());
621 globalWrites.erase(it);
623 for (
auto& g : globalWrites) {
624 if ((g.addr >> CacheLine::BITS) ==
625 (address >> CacheLine::BITS)) {
630 disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_WRITE_BIT;
634 ALWAYS_INLINE void MSXCPUInterface::updateVisible(
int page,
int ps,
int ss)
636 MSXDevice* newDevice = slotLayout[ps][ss][page];
637 if (visibleDevices[page] != newDevice) {
638 visibleDevices[page] = newDevice;
642 void MSXCPUInterface::updateVisible(
int page)
644 updateVisible(page, primarySlotState[page], secondarySlotState[page]);
649 for (
int i = 0; i < 4; ++i) {
650 subSlotRegister[i] = 0;
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;
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;
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;
775 if ((offset == 0xFFFF) &&
isExpanded(primSlot)) {
776 setSubSlot(primSlot, value);
778 slotLayout[primSlot][subSlot][page]->
writeMem(offset, value, time);
790 breakPoints.insert(std::make_pair(bp->getAddress(), bp));
795 auto range = breakPoints.equal_range(bp.
getAddress());
796 for (
auto it = range.first; it != range.second; ++it) {
797 if (it->second.get() == &bp) {
798 breakPoints.erase(it);
810 std::pair<BreakPoints::const_iterator,
811 BreakPoints::const_iterator> range)
817 for (
auto& p : bpCopy) {
818 p.second->checkAndExecute();
820 auto condCopy = conditions;
821 for (
auto& c : condCopy) {
822 c->checkAndExecute();
829 watchPoints.push_back(watchPoint);
833 registerIOWatch(*watchPoint, IO_In);
836 registerIOWatch(*watchPoint, IO_Out);
840 updateMemWatch(type);
852 for (
auto it = watchPoints.begin(); it != watchPoints.end(); ++it) {
853 if (*it == watchPoint) {
855 watchPoints.erase(it);
859 unregisterIOWatch(*watchPoint, IO_In);
862 unregisterIOWatch(*watchPoint, IO_Out);
866 updateMemWatch(type);
884 conditions.push_back(cond);
889 for (
auto it = conditions.begin(); it != conditions.end(); ++it) {
890 if (it->get() == &cond) {
891 conditions.erase(it);
905 assert(dynamic_cast<WatchIO*>(&watchPoint));
906 auto& ioWatch =
static_cast<WatchIO&
>(watchPoint);
908 unsigned endPort = ioWatch.getEndAddress();
909 assert(beginPort <= endPort);
910 assert(endPort < 0x100);
911 for (
unsigned port = beginPort; port <= endPort; ++port) {
912 ioWatch.getDevice(port).getDevicePtr() = devices[port];
913 devices[port] = &ioWatch.getDevice(port);
917 void MSXCPUInterface::unregisterIOWatch(WatchPoint& watchPoint,
MSXDevice** devices)
919 assert(dynamic_cast<WatchIO*>(&watchPoint));
920 auto& ioWatch =
static_cast<WatchIO&
>(watchPoint);
921 unsigned beginPort = ioWatch.getBeginAddress();
922 unsigned endPort = ioWatch.getEndAddress();
923 assert(beginPort <= endPort);
924 assert(endPort < 0x100);
926 for (
unsigned port = beginPort; port <= endPort; ++port) {
929 while (*prev != &ioWatch.getDevice(port)) {
930 prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
933 *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
939 std::bitset<CacheLine::SIZE>* watchSet =
941 for (
unsigned i = 0; i < CacheLine::NUM; ++i) {
944 for (
auto& w : watchPoints) {
945 if (w->getType() == type) {
946 unsigned beginAddr = w->getBeginAddress();
947 unsigned endAddr = w->getEndAddress();
948 assert(beginAddr <= endAddr);
949 assert(endAddr < 0x10000);
950 for (
unsigned addr = beginAddr; addr <= endAddr; ++addr) {
951 watchSet[addr >> CacheLine::BITS].set(
952 addr & CacheLine::LOW);
956 for (
unsigned i = 0; i < CacheLine::NUM; ++i) {
957 if (readWatchSet [i].any()) {
958 disallowReadCache [i] |= MEMORY_WATCH_BIT;
960 disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
962 if (writeWatchSet[i].any()) {
963 disallowWriteCache[i] |= MEMORY_WATCH_BIT;
965 disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
972 unsigned address,
unsigned value)
974 assert(!watchPoints.empty());
977 Tcl_Interp* interp = watchPoints.front()->getInterpreter();
985 auto wpCopy = watchPoints;
986 for (
auto& w : wpCopy) {
987 if ((w->getBeginAddress() <= address) &&
988 (w->getEndAddress() >= address) &&
989 (w->getType() == type)) {
990 w->checkAndExecute();
994 Tcl_UnsetVar(interp,
"wp_last_address", TCL_GLOBAL_ONLY);
995 Tcl_UnsetVar(interp,
"wp_last_value", TCL_GLOBAL_ONLY);
1002 if (breaked)
return;
1008 breakedSetting->setReadOnlyValue(
true);
1032 void MSXCPUInterface::doContinue2()
1036 breakedSetting->setReadOnlyValue(
false);
1048 breakPoints.clear();
1058 "The memory currently visible for the CPU.", 0x10000)
1059 , interface(interface_)
1065 return interface.
peekMem(address, time);
1071 return interface.
writeMem(address, value, time);
1080 "The memory in slots and subslots.", 0x10000 * 4 * 4)
1081 , interface(interface_)
1099 static unsigned getSlot(
const TclObject& token,
const string& itemName)
1101 unsigned slot = token.
getInt();
1111 , interface(interface_)
1118 if (tokens.size() != 5) {
1121 unsigned ps = getSlot(tokens[2],
"Primary slot");
1122 unsigned ss = getSlot(tokens[3],
"Secondary slot");
1123 unsigned page = getSlot(tokens[4],
"Page");
1127 interface.slotLayout[ps][ss][page]->
getNameList(result);
1132 return "Retrieve name of the device inserted in given "
1133 "primary slot / secondary slot / page.";
1141 :
InfoTopic(machineInfoCommand,
"issubslotted")
1142 , interface(interface_)
1149 if (tokens.size() != 3) {
1156 const vector<string>& )
const
1158 return "Indicates whether a certain primary slot is expanded.";
1166 :
InfoTopic(machineInfoCommand,
"isexternalslot")
1176 switch (tokens.size()) {
1178 ss = getSlot(tokens[3],
"Secondary slot");
1181 ps = getSlot(tokens[2],
"Primary slot");
1190 const vector<string>& )
const
1192 return "Indicates whether a certain slot is external or internal.";
1201 , interface(interface_)
1207 return interface.IO_In[address & 0xFF]->
peekIO(address, time);
1220 :
InfoTopic(machineInfoCommand, input_ ?
"input_port" :
"output_port")
1221 , interface(interface_), input(input_)
1228 if (tokens.size() != 3) {
1231 unsigned port = tokens[2].getInt();
1235 MSXDevice** devices = input ? interface.IO_In : interface.IO_Out;
1241 return "Return the name of the device connected to the given IO port.";
1244 template<
typename Archive>
1251 if (!ar.isLoader()) {
1252 for (
int i = 0; i < 4; ++i) {
1253 prim |= primarySlotState[i] << (2 * i);
1256 ar.serialize(
"primarySlots", prim);
1257 ar.serialize(
"subSlotRegs", subSlotRegister);
1258 if (ar.isLoader()) {
1260 for (
int i = 0; i < 4; ++i) {
1261 setSubSlot(i, subSlotRegister[i]);
1265 if (delayDevice.get()) {
1266 ar.serialize(
"vdpDelay", *delayDevice);