openMSX
MSXCPUInterface.cc
Go to the documentation of this file.
1 #include "MSXCPUInterface.hh"
2 #include "BreakPoint.hh"
3 #include "DebugCondition.hh"
4 #include "DummyDevice.hh"
5 #include "SimpleDebuggable.hh"
6 #include "InfoTopic.hh"
7 #include "CommandException.hh"
8 #include "TclObject.hh"
9 #include "Reactor.hh"
10 #include "MSXMotherBoard.hh"
11 #include "MSXCPU.hh"
12 #include "VDPIODelay.hh"
13 #include "CliComm.hh"
14 #include "MSXMultiIODevice.hh"
15 #include "MSXMultiMemDevice.hh"
16 #include "MSXWatchIODevice.hh"
17 #include "MSXException.hh"
18 #include "CartridgeSlotManager.hh"
19 #include "EventDistributor.hh"
20 #include "Event.hh"
21 #include "DeviceFactory.hh"
22 #include "ReadOnlySetting.hh"
23 #include "BooleanSetting.hh"
24 #include "serialize.hh"
25 #include "StringOp.hh"
26 #include "checked_cast.hh"
27 #include "unreachable.hh"
28 #include "memory.hh"
29 #include <tcl.h>
30 #include <cstdio>
31 #include <sstream>
32 #include <iomanip>
33 #include <algorithm>
34 #include <iostream>
35 #include <cstring>
36 #include <iterator>
37 
38 using std::ostringstream;
39 using std::setfill;
40 using std::setw;
41 using std::uppercase;
42 using std::string;
43 using std::vector;
44 using std::min;
45 using std::shared_ptr;
46 
47 namespace openmsx {
48 
50 {
51 public:
52  MemoryDebug(MSXCPUInterface& interface,
53  MSXMotherBoard& motherBoard);
54  virtual byte read(unsigned address, EmuTime::param time);
55  virtual void write(unsigned address, byte value, EmuTime::param time);
56 private:
57  MSXCPUInterface& interface;
58 };
59 
61 {
62 public:
64  MSXMotherBoard& motherBoard);
65  virtual byte read(unsigned address, EmuTime::param time);
66  virtual void write(unsigned address, byte value, EmuTime::param time);
67 private:
68  MSXCPUInterface& interface;
69 };
70 
71 class IODebug : public SimpleDebuggable
72 {
73 public:
74  IODebug(MSXCPUInterface& interface,
75  MSXMotherBoard& motherBoard);
76  virtual byte read(unsigned address, EmuTime::param time);
77  virtual void write(unsigned address, byte value, EmuTime::param time);
78 private:
79  MSXCPUInterface& interface;
80 };
81 
82 class SlotInfo : public InfoTopic
83 {
84 public:
85  SlotInfo(InfoCommand& machineInfoCommand,
86  MSXCPUInterface& interface);
87  virtual void execute(const vector<TclObject>& tokens,
88  TclObject& result) const;
89  virtual string help(const vector<string>& tokens) const;
90 private:
91  MSXCPUInterface& interface;
92 };
93 
94 class SubSlottedInfo : public InfoTopic
95 {
96 public:
97  SubSlottedInfo(InfoCommand& machineInfoCommand,
98  MSXCPUInterface& interface);
99  virtual void execute(const vector<TclObject>& tokens,
100  TclObject& result) const;
101  virtual string help(const vector<string>& tokens) const;
102 private:
103  MSXCPUInterface& interface;
104 };
105 
107 {
108 public:
109  ExternalSlotInfo(InfoCommand& machineInfoCommand,
110  CartridgeSlotManager& manager);
111  virtual void execute(const vector<TclObject>& tokens,
112  TclObject& result) const;
113  virtual string help(const vector<string>& tokens) const;
114 private:
115  CartridgeSlotManager& manager;
116 };
117 
118 class IOInfo : public InfoTopic
119 {
120 public:
121  IOInfo(InfoCommand& machineInfoCommand,
122  MSXCPUInterface& interface, bool input);
123  virtual void execute(const vector<TclObject>& tokens,
124  TclObject& result) const;
125  virtual string help(const vector<string>& tokens) const;
126 private:
127  MSXCPUInterface& interface;
128  bool input;
129 };
130 
131 
132 // Global variables
133 bool MSXCPUInterface::breaked = false;
134 bool MSXCPUInterface::continued = false;
135 bool MSXCPUInterface::step = false;
136 MSXCPUInterface::BreakPoints MSXCPUInterface::breakPoints;
137 //TODO watchpoints
138 MSXCPUInterface::Conditions MSXCPUInterface::conditions;
139 
140 static std::unique_ptr<ReadOnlySetting<BooleanSetting>> breakedSetting;
141 static unsigned breakedSettingCount = 0;
142 
143 
144 // Bitfields used in the disallowReadCache and disallowWriteCache arrays
145 static const byte SECUNDARY_SLOT_BIT = 0x01;
146 static const byte MEMORY_WATCH_BIT = 0x02;
147 static const byte GLOBAL_WRITE_BIT = 0x04;
148 
149 
151  : memoryDebug(make_unique<MemoryDebug>(
152  *this, motherBoard_))
153  , slottedMemoryDebug(make_unique<SlottedMemoryDebug>(
154  *this, motherBoard_))
155  , ioDebug(make_unique<IODebug>(
156  *this, motherBoard_))
157  , slotInfo(make_unique<SlotInfo>(
158  motherBoard_.getMachineInfoCommand(), *this))
159  , subSlottedInfo(make_unique<SubSlottedInfo>(
160  motherBoard_.getMachineInfoCommand(), *this))
161  , externalSlotInfo(make_unique<ExternalSlotInfo>(
162  motherBoard_.getMachineInfoCommand(),
163  motherBoard_.getSlotManager()))
164  , inputPortInfo(make_unique<IOInfo>(
165  motherBoard_.getMachineInfoCommand(), *this, true))
166  , outputPortInfo(make_unique<IOInfo>(
167  motherBoard_.getMachineInfoCommand(), *this, false))
168  , dummyDevice(DeviceFactory::createDummyDevice(
169  *motherBoard_.getMachineConfig()))
170  , msxcpu(motherBoard_.getCPU())
171  , cliComm(motherBoard_.getMSXCliComm())
172  , motherBoard(motherBoard_)
173  , fastForward(false)
174 {
175  for (int port = 0; port < 256; ++port) {
176  IO_In [port] = dummyDevice.get();
177  IO_Out[port] = dummyDevice.get();
178  }
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();
187  }
188  }
189  }
190  for (int page = 0; page < 4; ++page) {
191  visibleDevices[page] = dummyDevice.get();
192  }
193 
194  // initially allow all regions to be cached
195  memset(disallowReadCache, 0, sizeof(disallowReadCache));
196  memset(disallowWriteCache, 0, sizeof(disallowWriteCache));
197 
198  // Note: SlotState is initialised at reset
199 
200  msxcpu.setInterface(this);
201 
202  if (motherBoard.isTurboR()) {
203  // TODO also MSX2+ needs (slightly different) VDPIODelay
204  delayDevice = DeviceFactory::createVDPIODelay(
205  *motherBoard.getMachineConfig(), *this);
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();
211  }
212  }
213 
214  if (breakedSettingCount++ == 0) {
215  assert(!breakedSetting.get());
216  breakedSetting = make_unique<ReadOnlySetting<BooleanSetting>>(
217  motherBoard.getReactor().getCommandController(),
218  "breaked", "Similar to 'debug breaked'", false);
219  }
220 }
221 
223 {
224  if (--breakedSettingCount == 0) {
225  assert(breakedSetting.get());
226  breakedSetting = nullptr;
227  }
228 
229  removeAllWatchPoints();
230 
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();
237  }
238  }
239 
240  msxcpu.setInterface(nullptr);
241 
242  #ifndef NDEBUG
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;
247  UNREACHABLE;
248  }
249  if (IO_Out[port] != dummyDevice.get()) {
250  std::cout << "Out-port " << port << " still registered "
251  << IO_Out[port]->getName() << std::endl;
252  UNREACHABLE;
253  }
254  }
255  for (int primSlot = 0; primSlot < 4; ++primSlot) {
256  assert(!isExpanded(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());
260  }
261  }
262  }
263  #endif
264 }
265 
266 void MSXCPUInterface::removeAllWatchPoints()
267 {
268  while (!watchPoints.empty()) {
269  removeWatchPoint(watchPoints.back());
270  }
271 
272 }
273 
274 byte MSXCPUInterface::readMemSlow(word address, EmuTime::param time)
275 {
276  // something special in this region?
277  if (unlikely(disallowReadCache[address >> CacheLine::BITS])) {
278  // execute read watches before actual read
279  if (readWatchSet[address >> CacheLine::BITS]
280  [address & CacheLine::LOW]) {
281  executeMemWatch(WatchPoint::READ_MEM, address);
282  }
283  }
284  if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) {
285  return 0xFF ^ subSlotRegister[primarySlotState[3]];
286  } else {
287  return visibleDevices[address >> 14]->readMem(address, time);
288  }
289 }
290 
291 void MSXCPUInterface::writeMemSlow(word address, byte value, EmuTime::param time)
292 {
293  if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) {
294  setSubSlot(primarySlotState[3], value);
295  } else {
296  visibleDevices[address>>14]->writeMem(address, value, time);
297  }
298  // something special in this region?
299  if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
300  // slot-select-ignore writes (Super Lode Runner)
301  for (auto& g : globalWrites) {
302  // very primitive address selection mechanism,
303  // but more than enough for now
304  if (unlikely(g.addr == address)) {
305  g.device->globalWrite(address, value, time);
306  }
307  }
308  // execute write watches after actual write
309  if (writeWatchSet[address >> CacheLine::BITS]
310  [address & CacheLine::LOW]) {
311  executeMemWatch(WatchPoint::WRITE_MEM, address, value);
312  }
313  }
314 }
315 
317 {
318  if (expanded[ps] == 0) {
319  for (int page = 0; page < 4; ++page) {
320  if (slotLayout[ps][0][page] != dummyDevice.get()) {
321  throw MSXException("Can't expand slot because "
322  "it's already in use.");
323  }
324  }
325  }
326  expanded[ps]++;
327  changeExpanded(isExpanded(primarySlotState[3]));
328 }
329 
331  int ps, vector<MSXDevice*> allowed) const
332 {
333  // TODO handle multi-devices
334  allowed.push_back(dummyDevice.get());
335  sort(allowed.begin(), allowed.end()); // for set_difference()
336  assert(isExpanded(ps));
337  if (expanded[ps] != 1) return; // ok, still expanded after this
338 
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()); // for set_difference()
347  } else {
348  devices.push_back(device);
349  }
350  std::set_difference(devices.begin(), devices.end(),
351  allowed.begin(), allowed.end(),
352  std::inserter(inUse, inUse.end()));
353 
354  }
355  }
356  if (inUse.empty()) return; // ok, no more devices in use
357 
358  StringOp::Builder msg;
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();
363  }
364  msg << '.';
365  throw MSXException(msg);
366 }
367 
369 {
370 #ifndef NDEBUG
371  try {
372  vector<MSXDevice*> dummy;
373  testUnsetExpanded(ps, dummy);
374  } catch (...) {
375  UNREACHABLE;
376  }
377 #endif
378  expanded[ps]--;
379  changeExpanded(isExpanded(primarySlotState[3]));
380 }
381 
382 void MSXCPUInterface::changeExpanded(bool isExpanded)
383 {
384  if (isExpanded) {
385  disallowReadCache [0xFF] |= SECUNDARY_SLOT_BIT;
386  disallowWriteCache[0xFF] |= SECUNDARY_SLOT_BIT;
387  } else {
388  disallowReadCache [0xFF] &= ~SECUNDARY_SLOT_BIT;
389  disallowWriteCache[0xFF] &= ~SECUNDARY_SLOT_BIT;
390  }
391  msxcpu.invalidateMemCache(0xFFFF & CacheLine::HIGH, 0x100);
392 }
393 
394 MSXDevice*& MSXCPUInterface::getDevicePtr(byte port, bool isIn)
395 {
396  MSXDevice** devicePtr = isIn ? &IO_In[port] : &IO_Out[port];
397  while (auto watch = dynamic_cast<MSXWatchIODevice*>(*devicePtr)) {
398  devicePtr = &watch->getDevicePtr();
399  }
400  if (*devicePtr == delayDevice.get()) {
401  devicePtr = isIn ? &delayDevice->getInDevicePtr (port)
402  : &delayDevice->getOutDevicePtr(port);
403  }
404  return *devicePtr;
405 }
406 
408 {
409  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
410  register_IO(port, true, devicePtr, device); // in
411 }
412 
414 {
415  MSXDevice*& devicePtr = getDevicePtr(port, true); // in
416  unregister_IO(devicePtr, device);
417 }
418 
420 {
421  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
422  register_IO(port, false, devicePtr, device); // out
423 }
424 
426 {
427  MSXDevice*& devicePtr = getDevicePtr(port, false); // out
428  unregister_IO(devicePtr, device);
429 }
430 
431 void MSXCPUInterface::register_IO(int port, bool isIn,
432  MSXDevice*& devicePtr, MSXDevice* device)
433 {
434  PRT_DEBUG(device->getName() << " registers " << (isIn ? "In" : "Out") <<
435  "-port " << std::hex << port << std::dec);
436 
437  if (devicePtr == dummyDevice.get()) {
438  // first, replace DummyDevice
439  devicePtr = device;
440  } else {
441  if (auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
442  // third or more, add to existing MultiIO device
443  multi->addDevice(device);
444  } else {
445  // second, create a MultiIO device
446  multi = new MSXMultiIODevice(device->getHardwareConfig());
447  multi->addDevice(devicePtr);
448  multi->addDevice(device);
449  devicePtr = multi;
450  }
451  if (isIn) {
452  cliComm.printWarning(
453  "Conflicting input port 0x" +
454  StringOp::toHexString(port, 2) +
455  " for devices " + devicePtr->getName());
456  }
457  }
458 }
459 
460 void MSXCPUInterface::unregister_IO(MSXDevice*& devicePtr, MSXDevice* device)
461 {
462  if (auto multi = dynamic_cast<MSXMultiIODevice*>(devicePtr)) {
463  // remove from MultiIO device
464  multi->removeDevice(device);
465  auto& devices = multi->getDevices();
466  if (devices.size() == 1) {
467  // only one remaining, remove MultiIO device
468  devicePtr = devices.front();
469  devices.pop_back();
470  delete multi;
471  }
472  } else {
473  // remove last, put back DummyDevice
474  assert(devicePtr == device);
475  devicePtr = dummyDevice.get();
476  }
477 }
478 
479 
480 static void reportMemOverlap(int ps, int ss, MSXDevice& dev1, MSXDevice& dev2)
481 {
482  throw MSXException(StringOp::Builder() <<
483  "Overlapping memory devices in slot " << ps << '.' << ss <<
484  ": " << dev1.getName() << " and " << dev2.getName() << '.');
485 }
486 
487 void MSXCPUInterface::testRegisterSlot(
488  MSXDevice& device, int ps, int ss, int base, int size)
489 {
490  int 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 
513 void MSXCPUInterface::registerSlot(
514  MSXDevice& device, int ps, int ss, int base, int size)
515 {
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) {
522  // full 16kb, directly register device (no multiplexer)
523  assert(slot == dummyDevice.get());
524  slot = &device;
525  } else {
526  // partial page
527  if (slot == dummyDevice.get()) {
528  // first
529  MSXMultiMemDevice* multi =
530  new MSXMultiMemDevice(device.getHardwareConfig());
531  multi->add(device, base, size);
532  slot = multi;
533  } else if (auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
534  // second or more
535  assert(multi->canAdd(base, size));
536  multi->add(device, base, size);
537  } else {
538  // conflict with 'full ranged' device
539  assert(false);
540  }
541  }
542  updateVisible(page);
543 }
544 
545 void MSXCPUInterface::unregisterSlot(
546  MSXDevice& device, int ps, int ss, int base, int size)
547 {
548  int page = base >> 14;
549  MSXDevice*& slot = slotLayout[ps][ss][page];
550  if (auto multi = dynamic_cast<MSXMultiMemDevice*>(slot)) {
551  // partial range
552  multi->remove(device, base, size);
553  if (multi->empty()) {
554  delete multi;
555  slot = dummyDevice.get();
556  }
557  } else {
558  // full 16kb range
559  assert(slot == &device);
560  slot = dummyDevice.get();
561  }
562  updateVisible(page);
563 }
564 
566  MSXDevice& device, int ps, int ss, int base_, int size_)
567 {
568  if (!isExpanded(ps) && (ss != 0)) {
570  "Slot " << ps << '.' << ss <<
571  " does not exist because slot is not expanded.");
572  }
573 
574  // split range on 16kb borders
575  // first check if registration is possible
576  int base = base_;
577  int size = size_;
578  while (size > 0) {
579  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
580  testRegisterSlot(device, ps, ss, base, partialSize);
581  base += partialSize;
582  size -= partialSize;
583  }
584  // if all checks are successful, only then actually register
585  base = base_;
586  size = size_;
587  while (size > 0) {
588  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
589  registerSlot(device, ps, ss, base, partialSize);
590  base += partialSize;
591  size -= partialSize;
592  }
593 }
594 
596  MSXDevice& device, int ps, int ss, int base, int size)
597 {
598  // split range on 16kb borders
599  while (size > 0) {
600  int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base);
601  unregisterSlot(device, ps, ss, base, partialSize);
602  base += partialSize;
603  size -= partialSize;
604  }
605 }
606 
608 {
609  GlobalWriteInfo info = { &device, address };
610  globalWrites.push_back(info);
611 
612  disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_WRITE_BIT;
613  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
614 }
615 
617 {
618  GlobalWriteInfo info = { &device, address };
619  auto it = find(globalWrites.begin(), globalWrites.end(), info);
620  assert(it != globalWrites.end());
621  globalWrites.erase(it);
622 
623  for (auto& g : globalWrites) {
624  if ((g.addr >> CacheLine::BITS) ==
625  (address >> CacheLine::BITS)) {
626  // there is still a global write in this region
627  return;
628  }
629  }
630  disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_WRITE_BIT;
631  msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100);
632 }
633 
634 ALWAYS_INLINE void MSXCPUInterface::updateVisible(int page, int ps, int ss)
635 {
636  MSXDevice* newDevice = slotLayout[ps][ss][page];
637  if (visibleDevices[page] != newDevice) {
638  visibleDevices[page] = newDevice;
639  msxcpu.updateVisiblePage(page, ps, ss);
640  }
641 }
642 void MSXCPUInterface::updateVisible(int page)
643 {
644  updateVisible(page, primarySlotState[page], secondarySlotState[page]);
645 }
646 
648 {
649  for (int i = 0; i < 4; ++i) {
650  subSlotRegister[i] = 0;
651  }
652  setPrimarySlots(0);
653 }
654 
656 {
657  return motherBoard.readIRQVector();
658 }
659 
661 {
662  // Change the slot structure.
663  // Originally the code below was a loop over the 4 pages, and the check
664  // for (un)expanded-slot was done unconditionally at the end. I've
665  // completely unrolled the loop and only check for (un)expanded slot
666  // when the slot in page 3 has changed. I've also added checks for slot
667  // changes for the other 3 pages. Usually when this register is written
668  // only one of the 4 pages actually changes, so these extra checks do
669  // pay off. This does make the code a bit more complex (and the
670  // generated code slightly bigger), but it does make a measurable speed
671  // difference. Changing the slots several hundreds of times per
672  // (EmuTime) is not unusual. So this routine ended up quite high
673  // (top-10) in some profile results.
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);
680  }
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);
687  }
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);
694  }
695  int ps3 = (value >> 6) & 3;
696  if (unlikely(primarySlotState[3] != ps3)) {
697  bool oldExpanded = isExpanded(primarySlotState[3]);
698  bool newExpanded = isExpanded(ps3);
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)) {
704  changeExpanded(newExpanded);
705  }
706  }
707 }
708 
709 void MSXCPUInterface::setSubSlot(byte primSlot, byte value)
710 {
711  subSlotRegister[primSlot] = value;
712  for (int page = 0; page < 4; ++page, value >>= 2) {
713  if (primSlot == primarySlotState[page]) {
714  secondarySlotState[page] = value & 3;
715  // Change the visible devices
716  updateVisible(page);
717  }
718  }
719 }
720 
722 {
723  if ((address == 0xFFFF) && isExpanded(primarySlotState[3])) {
724  return 0xFF ^ subSlotRegister[primarySlotState[3]];
725  } else {
726  return visibleDevices[address >> 14]->peekMem(address, time);
727  }
728 }
729 
731 {
732  byte primSlot = (address & 0xC0000) >> 18;
733  byte subSlot = (address & 0x30000) >> 16;
734  byte page = (address & 0x0C000) >> 14;
735  word offset = (address & 0xFFFF); // includes page
736  if (!isExpanded(primSlot)) {
737  subSlot = 0;
738  }
739 
740  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
741  return 0xFF ^ subSlotRegister[primSlot];
742  } else {
743  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
744  }
745 }
746 
748 {
749  byte primSlot = (address & 0xC0000) >> 18;
750  byte subSlot = (address & 0x30000) >> 16;
751  byte page = (address & 0x0C000) >> 14;
752  word offset = (address & 0xFFFF); // includes page
753  if (!isExpanded(primSlot)) {
754  subSlot = 0;
755  }
756 
757  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
758  return 0xFF ^ subSlotRegister[primSlot];
759  } else {
760  return slotLayout[primSlot][subSlot][page]->peekMem(offset, time);
761  }
762 }
763 
764 void MSXCPUInterface::writeSlottedMem(unsigned address, byte value,
765  EmuTime::param time)
766 {
767  byte primSlot = (address & 0xC0000) >> 18;
768  byte subSlot = (address & 0x30000) >> 16;
769  byte page = (address & 0x0C000) >> 14;
770  word offset = (address & 0xFFFF); // includes page
771  if (!isExpanded(primSlot)) {
772  subSlot = 0;
773  }
774 
775  if ((offset == 0xFFFF) && isExpanded(primSlot)) {
776  setSubSlot(primSlot, value);
777  } else {
778  slotLayout[primSlot][subSlot][page]->writeMem(offset, value, time);
779  }
780 }
781 
783 {
784  return *dummyDevice;
785 }
786 
787 
788 void MSXCPUInterface::insertBreakPoint(const shared_ptr<BreakPoint>& bp)
789 {
790  breakPoints.insert(std::make_pair(bp->getAddress(), bp));
791 }
792 
794 {
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);
799  break;
800  }
801  }
802 }
803 
805 {
806  return breakPoints;
807 }
808 
810  std::pair<BreakPoints::const_iterator,
811  BreakPoints::const_iterator> range)
812 {
813  // create copy for the case that breakpoint/condition removes itself
814  // - keeps object alive by holding a shared_ptr to it
815  // - avoids iterating over a changing collection
816  BreakPoints bpCopy(range.first, range.second);
817  for (auto& p : bpCopy) {
818  p.second->checkAndExecute();
819  }
820  auto condCopy = conditions;
821  for (auto& c : condCopy) {
822  c->checkAndExecute();
823  }
824 }
825 
826 
827 void MSXCPUInterface::setWatchPoint(const shared_ptr<WatchPoint>& watchPoint)
828 {
829  watchPoints.push_back(watchPoint);
830  WatchPoint::Type type = watchPoint->getType();
831  switch (type) {
832  case WatchPoint::READ_IO:
833  registerIOWatch(*watchPoint, IO_In);
834  break;
836  registerIOWatch(*watchPoint, IO_Out);
837  break;
840  updateMemWatch(type);
841  break;
842  default:
843  UNREACHABLE; break;
844  }
845 }
846 
847 void MSXCPUInterface::removeWatchPoint(shared_ptr<WatchPoint> watchPoint)
848 {
849  // Pass shared_ptr by value to keep the object alive for the duration
850  // of this function, otherwise it gets deleted as soon as it's removed
851  // from the watchPoints collection.
852  for (auto it = watchPoints.begin(); it != watchPoints.end(); ++it) {
853  if (*it == watchPoint) {
854  // remove before calling updateMemWatch()
855  watchPoints.erase(it);
856  WatchPoint::Type type = watchPoint->getType();
857  switch (type) {
858  case WatchPoint::READ_IO:
859  unregisterIOWatch(*watchPoint, IO_In);
860  break;
862  unregisterIOWatch(*watchPoint, IO_Out);
863  break;
866  updateMemWatch(type);
867  break;
868  default:
869  UNREACHABLE; break;
870  }
871  break;
872  }
873  }
874 }
875 
877 {
878  return watchPoints;
879 }
880 
881 
882 void MSXCPUInterface::setCondition(const shared_ptr<DebugCondition>& cond)
883 {
884  conditions.push_back(cond);
885 }
886 
888 {
889  for (auto it = conditions.begin(); it != conditions.end(); ++it) {
890  if (it->get() == &cond) {
891  conditions.erase(it);
892  break;
893  }
894  }
895 }
896 
898 {
899  return conditions;
900 }
901 
902 
903 void MSXCPUInterface::registerIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
904 {
905  assert(dynamic_cast<WatchIO*>(&watchPoint));
906  auto& ioWatch = static_cast<WatchIO&>(watchPoint);
907  unsigned beginPort = ioWatch.getBeginAddress();
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);
914  }
915 }
916 
917 void MSXCPUInterface::unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices)
918 {
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);
925 
926  for (unsigned port = beginPort; port <= endPort; ++port) {
927  // find pointer to watchpoint
928  MSXDevice** prev = &devices[port];
929  while (*prev != &ioWatch.getDevice(port)) {
930  prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
931  }
932  // remove watchpoint from chain
933  *prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
934  }
935 }
936 
937 void MSXCPUInterface::updateMemWatch(WatchPoint::Type type)
938 {
939  std::bitset<CacheLine::SIZE>* watchSet =
940  (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet;
941  for (unsigned i = 0; i < CacheLine::NUM; ++i) {
942  watchSet[i].reset();
943  }
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);
953  }
954  }
955  }
956  for (unsigned i = 0; i < CacheLine::NUM; ++i) {
957  if (readWatchSet [i].any()) {
958  disallowReadCache [i] |= MEMORY_WATCH_BIT;
959  } else {
960  disallowReadCache [i] &= ~MEMORY_WATCH_BIT;
961  }
962  if (writeWatchSet[i].any()) {
963  disallowWriteCache[i] |= MEMORY_WATCH_BIT;
964  } else {
965  disallowWriteCache[i] &= ~MEMORY_WATCH_BIT;
966  }
967  }
968  msxcpu.invalidateMemCache(0x0000, 0x10000);
969 }
970 
971 void MSXCPUInterface::executeMemWatch(WatchPoint::Type type,
972  unsigned address, unsigned value)
973 {
974  assert(!watchPoints.empty());
975  if (isFastForward()) return;
976 
977  Tcl_Interp* interp = watchPoints.front()->getInterpreter();
978  Tcl_SetVar(interp, "wp_last_address", StringOp::toString(address).c_str(),
979  TCL_GLOBAL_ONLY);
980  if (value != ~0u) {
981  Tcl_SetVar(interp, "wp_last_value", StringOp::toString(value).c_str(),
982  TCL_GLOBAL_ONLY);
983  }
984 
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();
991  }
992  }
993 
994  Tcl_UnsetVar(interp, "wp_last_address", TCL_GLOBAL_ONLY);
995  Tcl_UnsetVar(interp, "wp_last_value", TCL_GLOBAL_ONLY);
996 }
997 
998 
1000 {
1001  assert(!isFastForward());
1002  if (breaked) return;
1003  breaked = true;
1004  msxcpu.exitCPULoopSync();
1005 
1006  Reactor& reactor = motherBoard.getReactor();
1007  reactor.block();
1008  breakedSetting->setReadOnlyValue(true);
1009  reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended");
1011  std::make_shared<SimpleEvent>(OPENMSX_BREAK_EVENT));
1012 }
1013 
1015 {
1016  assert(!isFastForward());
1017  if (breaked) {
1018  step = true;
1019  doContinue2();
1020  }
1021 }
1022 
1024 {
1025  assert(!isFastForward());
1026  if (breaked) {
1027  continued = true;
1028  doContinue2();
1029  }
1030 }
1031 
1032 void MSXCPUInterface::doContinue2()
1033 {
1034  breaked = false;
1035  Reactor& reactor = motherBoard.getReactor();
1036  breakedSetting->setReadOnlyValue(false);
1037  reactor.getCliComm().update(CliComm::STATUS, "cpu", "running");
1038  reactor.unblock();
1039 }
1040 
1042 {
1043  // before the Tcl interpreter is destroyed, we must delete all
1044  // TclObjects. Breakpoints and conditions contain such objects
1045  // for the condition and action.
1046  // TODO it would be nicer if breakpoints and conditions were not
1047  // global objects.
1048  breakPoints.clear();
1049  conditions.clear();
1050 }
1051 
1052 
1053 // class MemoryDebug
1054 
1056  MSXCPUInterface& interface_, MSXMotherBoard& motherBoard)
1057  : SimpleDebuggable(motherBoard, "memory",
1058  "The memory currently visible for the CPU.", 0x10000)
1059  , interface(interface_)
1060 {
1061 }
1062 
1063 byte MemoryDebug::read(unsigned address, EmuTime::param time)
1064 {
1065  return interface.peekMem(address, time);
1066 }
1067 
1068 void MemoryDebug::write(unsigned address, byte value,
1069  EmuTime::param time)
1070 {
1071  return interface.writeMem(address, value, time);
1072 }
1073 
1074 
1075 // class SlottedMemoryDebug
1076 
1078  MSXCPUInterface& interface_, MSXMotherBoard& motherBoard)
1079  : SimpleDebuggable(motherBoard, "slotted memory",
1080  "The memory in slots and subslots.", 0x10000 * 4 * 4)
1081  , interface(interface_)
1082 {
1083 }
1084 
1086 {
1087  return interface.peekSlottedMem(address, time);
1088 }
1089 
1090 void SlottedMemoryDebug::write(unsigned address, byte value,
1091  EmuTime::param time)
1092 {
1093  return interface.writeSlottedMem(address, value, time);
1094 }
1095 
1096 
1097 // class SubSlottedInfo
1098 
1099 static unsigned getSlot(const TclObject& token, const string& itemName)
1100 {
1101  unsigned slot = token.getInt();
1102  if (slot >= 4) {
1103  throw CommandException(itemName + " must be in range 0..3");
1104  }
1105  return slot;
1106 }
1107 
1108 SlotInfo::SlotInfo(InfoCommand& machineInfoCommand,
1109  MSXCPUInterface& interface_)
1110  : InfoTopic(machineInfoCommand, "slot")
1111  , interface(interface_)
1112 {
1113 }
1114 
1115 void SlotInfo::execute(const vector<TclObject>& tokens,
1116  TclObject& result) const
1117 {
1118  if (tokens.size() != 5) {
1119  throw SyntaxError();
1120  }
1121  unsigned ps = getSlot(tokens[2], "Primary slot");
1122  unsigned ss = getSlot(tokens[3], "Secondary slot");
1123  unsigned page = getSlot(tokens[4], "Page");
1124  if (!interface.isExpanded(ps)) {
1125  ss = 0;
1126  }
1127  interface.slotLayout[ps][ss][page]->getNameList(result);
1128 }
1129 
1130 string SlotInfo::help(const vector<string>& /*tokens*/) const
1131 {
1132  return "Retrieve name of the device inserted in given "
1133  "primary slot / secondary slot / page.";
1134 }
1135 
1136 
1137 // class SubSlottedInfo
1138 
1140  MSXCPUInterface& interface_)
1141  : InfoTopic(machineInfoCommand, "issubslotted")
1142  , interface(interface_)
1143 {
1144 }
1145 
1146 void SubSlottedInfo::execute(const vector<TclObject>& tokens,
1147  TclObject& result) const
1148 {
1149  if (tokens.size() != 3) {
1150  throw SyntaxError();
1151  }
1152  result.setInt(interface.isExpanded(getSlot(tokens[2], "Slot")));
1153 }
1154 
1156  const vector<string>& /*tokens*/) const
1157 {
1158  return "Indicates whether a certain primary slot is expanded.";
1159 }
1160 
1161 
1162 // class ExternalSlotInfo
1163 
1165  CartridgeSlotManager& manager_)
1166  : InfoTopic(machineInfoCommand, "isexternalslot")
1167  , manager(manager_)
1168 {
1169 }
1170 
1171 void ExternalSlotInfo::execute(const vector<TclObject>& tokens,
1172  TclObject& result) const
1173 {
1174  int ps = 0;
1175  int ss = 0;
1176  switch (tokens.size()) {
1177  case 4:
1178  ss = getSlot(tokens[3], "Secondary slot");
1179  // Fall-through
1180  case 3:
1181  ps = getSlot(tokens[2], "Primary slot");
1182  break;
1183  default:
1184  throw SyntaxError();
1185  }
1186  result.setInt(manager.isExternalSlot(ps, ss, true));
1187 }
1188 
1190  const vector<string>& /*tokens*/) const
1191 {
1192  return "Indicates whether a certain slot is external or internal.";
1193 }
1194 
1195 
1196 // class IODebug
1197 
1199  MSXMotherBoard& motherBoard)
1200  : SimpleDebuggable(motherBoard, "ioports", "IO ports.", 0x100)
1201  , interface(interface_)
1202 {
1203 }
1204 
1205 byte IODebug::read(unsigned address, EmuTime::param time)
1206 {
1207  return interface.IO_In[address & 0xFF]->peekIO(address, time);
1208 }
1209 
1210 void IODebug::write(unsigned address, byte value, EmuTime::param time)
1211 {
1212  interface.writeIO(word(address), value, time);
1213 }
1214 
1215 
1216 // class IOInfo
1217 
1218 IOInfo::IOInfo(InfoCommand& machineInfoCommand,
1219  MSXCPUInterface& interface_, bool input_)
1220  : InfoTopic(machineInfoCommand, input_ ? "input_port" : "output_port")
1221  , interface(interface_), input(input_)
1222 {
1223 }
1224 
1225 void IOInfo::execute(const vector<TclObject>& tokens,
1226  TclObject& result) const
1227 {
1228  if (tokens.size() != 3) {
1229  throw SyntaxError();
1230  }
1231  unsigned port = tokens[2].getInt();
1232  if (port >= 256) {
1233  throw CommandException("Port must be in range 0..255");
1234  }
1235  MSXDevice** devices = input ? interface.IO_In : interface.IO_Out;
1236  result.setString(devices[port]->getName());
1237 }
1238 
1239 string IOInfo::help(const vector<string>& /*tokens*/) const
1240 {
1241  return "Return the name of the device connected to the given IO port.";
1242 }
1243 
1244 template<typename Archive>
1245 void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/)
1246 {
1247  // TODO watchPoints ???
1248 
1249  // primary and 4 secundary slot select registers
1250  byte prim = 0;
1251  if (!ar.isLoader()) {
1252  for (int i = 0; i < 4; ++i) {
1253  prim |= primarySlotState[i] << (2 * i);
1254  }
1255  }
1256  ar.serialize("primarySlots", prim);
1257  ar.serialize("subSlotRegs", subSlotRegister);
1258  if (ar.isLoader()) {
1259  setPrimarySlots(prim);
1260  for (int i = 0; i < 4; ++i) {
1261  setSubSlot(i, subSlotRegister[i]);
1262  }
1263  }
1264 
1265  if (delayDevice.get()) {
1266  ar.serialize("vdpDelay", *delayDevice);
1267  }
1268 }
1270 
1271 } // namespace openmsx