openMSX
MSXCPUInterface.hh
Go to the documentation of this file.
1 #ifndef MSXCPUINTERFACE_HH
2 #define MSXCPUINTERFACE_HH
3 
4 #include "SimpleDebuggable.hh"
5 #include "InfoTopic.hh"
6 #include "CacheLine.hh"
7 #include "MSXDevice.hh"
8 #include "BreakPoint.hh"
9 #include "WatchPoint.hh"
10 #include "openmsx.hh"
11 #include "likely.hh"
12 #include <algorithm>
13 #include <bitset>
14 #include <vector>
15 #include <memory>
16 
17 namespace openmsx {
18 
19 class VDPIODelay;
20 class DummyDevice;
21 class MSXMotherBoard;
22 class MSXCPU;
23 class CliComm;
24 class BreakPoint;
25 class DebugCondition;
26 class CartridgeSlotManager;
27 
29  bool operator()(const BreakPoint& x, const BreakPoint& y) const {
30  return x.getAddress() < y.getAddress();
31  }
32  bool operator()(const BreakPoint& x, word y) const {
33  return x.getAddress() < y;
34  }
35  bool operator()(word x, const BreakPoint& y) const {
36  return x < y.getAddress();
37  }
38 };
39 
41 {
42 public:
43  MSXCPUInterface(const MSXCPUInterface&) = delete;
44  MSXCPUInterface& operator=(const MSXCPUInterface&) = delete;
45 
46  explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
47  ~MSXCPUInterface();
48 
54  void register_IO_In(byte port, MSXDevice* device);
55  void unregister_IO_In(byte port, MSXDevice* device);
56 
62  void register_IO_Out(byte port, MSXDevice* device);
63  void unregister_IO_Out(byte port, MSXDevice* device);
64 
80  bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
81  bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
82 
89  void registerMemDevice(MSXDevice& device,
90  int ps, int ss, int base, int size);
91  void unregisterMemDevice(MSXDevice& device,
92  int ps, int ss, int base, int size);
93 
97  void registerGlobalWrite(MSXDevice& device, word address);
98  void unregisterGlobalWrite(MSXDevice& device, word address);
99 
103  void registerGlobalRead(MSXDevice& device, word address);
104  void unregisterGlobalRead(MSXDevice& device, word address);
105 
109  void reset();
110 
114  inline byte readMem(word address, EmuTime::param time) {
115  if (unlikely(disallowReadCache[address >> CacheLine::BITS])) {
116  return readMemSlow(address, time);
117  }
118  return visibleDevices[address >> 14]->readMem(address, time);
119  }
120 
124  inline void writeMem(word address, byte value, EmuTime::param time) {
125  if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
126  writeMemSlow(address, value, time);
127  return;
128  }
129  visibleDevices[address>>14]->writeMem(address, value, time);
130  }
131 
136  inline byte readIO(word port, EmuTime::param time) {
137  return IO_In[port & 0xFF]->readIO(port, time);
138  }
139 
144  inline void writeIO(word port, byte value, EmuTime::param time) {
145  IO_Out[port & 0xFF]->writeIO(port, value, time);
146  }
147 
160  inline const byte* getReadCacheLine(word start) const {
161  if (unlikely(disallowReadCache[start >> CacheLine::BITS])) {
162  return nullptr;
163  }
164  return visibleDevices[start >> 14]->getReadCacheLine(start);
165  }
166 
179  inline byte* getWriteCacheLine(word start) const {
180  if (unlikely(disallowWriteCache[start >> CacheLine::BITS])) {
181  return nullptr;
182  }
183  return visibleDevices[start >> 14]->getWriteCacheLine(start);
184  }
185 
190  byte readIRQVector();
191 
192  /*
193  * Should only be used by PPI
194  * TODO: make private / friend
195  */
196  void setPrimarySlots(byte value);
197 
202  byte peekMem(word address, EmuTime::param time) const;
203  byte peekSlottedMem(unsigned address, EmuTime::param time) const;
204  byte readSlottedMem(unsigned address, EmuTime::param time);
205  void writeSlottedMem(unsigned address, byte value,
206  EmuTime::param time);
207 
208  void setExpanded(int ps);
209  void unsetExpanded(int ps);
210  void testUnsetExpanded(int ps, std::vector<MSXDevice*> allowed) const;
211  inline bool isExpanded(int ps) const { return expanded[ps] != 0; }
212  void changeExpanded(bool newExpanded);
213 
214  DummyDevice& getDummyDevice() { return *dummyDevice; }
215 
216  static void insertBreakPoint(const BreakPoint& bp);
217  static void removeBreakPoint(const BreakPoint& bp);
218  using BreakPoints = std::vector<BreakPoint>;
219  static const BreakPoints& getBreakPoints() { return breakPoints; }
220 
221  void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
222  void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
223  // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
224  using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
225  const WatchPoints& getWatchPoints() const { return watchPoints; }
226 
227  static void setCondition(const DebugCondition& cond);
228  static void removeCondition(const DebugCondition& cond);
229  using Conditions = std::vector<DebugCondition>;
230  static const Conditions& getConditions() { return conditions; }
231 
232  static bool isBreaked() { return breaked; }
233  void doBreak();
234  void doStep();
235  void doContinue();
236 
237  // should only be used by CPUCore
238  static bool isStep() { return step; }
239  static void setStep (bool x) { step = x; }
240  static bool isContinue() { return continued; }
241  static void setContinue(bool x) { continued = x; }
242 
243  // breakpoint methods used by CPUCore
244  static bool anyBreakPoints()
245  {
246  return !breakPoints.empty() || !conditions.empty();
247  }
248  static bool checkBreakPoints(unsigned pc, MSXMotherBoard& motherBoard)
249  {
250  auto range = equal_range(begin(breakPoints), end(breakPoints),
251  pc, CompareBreakpoints());
252  if (conditions.empty() && (range.first == range.second)) {
253  return false;
254  }
255 
256  // slow path non-inlined
257  checkBreakPoints(range, motherBoard);
258  return isBreaked();
259  }
260 
261  // cleanup global variables
262  static void cleanup();
263 
264  // In fast-forward mode, breakpoints, watchpoints and conditions should
265  // not trigger.
266  void setFastForward(bool fastForward_) { fastForward = fastForward_; }
267  bool isFastForward() const { return fastForward; }
268 
269  template<typename Archive>
270  void serialize(Archive& ar, unsigned version);
271 
272 private:
273  byte readMemSlow(word address, EmuTime::param time);
274  void writeMemSlow(word address, byte value, EmuTime::param time);
275 
276  MSXDevice*& getDevicePtr(byte port, bool isIn);
277 
278  void register_IO (int port, bool isIn,
279  MSXDevice*& devicePtr, MSXDevice* device);
280  void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
281  void testRegisterSlot(MSXDevice& device,
282  int ps, int ss, int base, int size);
283  void registerSlot(MSXDevice& device,
284  int ps, int ss, int base, int size);
285  void unregisterSlot(MSXDevice& device,
286  int ps, int ss, int base, int size);
287 
288 
289  static void checkBreakPoints(std::pair<BreakPoints::const_iterator,
290  BreakPoints::const_iterator> range,
291  MSXMotherBoard& motherBoard);
292 
293  void removeAllWatchPoints();
294  void registerIOWatch (WatchPoint& watchPoint, MSXDevice** devices);
295  void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices);
296  void updateMemWatch(WatchPoint::Type type);
297  void executeMemWatch(WatchPoint::Type type, unsigned address,
298  unsigned value = ~0u);
299 
300  void doContinue2();
301 
302  struct MemoryDebug final : SimpleDebuggable {
303  explicit MemoryDebug(MSXMotherBoard& motherBoard);
304  byte read(unsigned address, EmuTime::param time) override;
305  void write(unsigned address, byte value, EmuTime::param time) override;
306  } memoryDebug;
307 
308  struct SlottedMemoryDebug final : SimpleDebuggable {
309  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
310  byte read(unsigned address, EmuTime::param time) override;
311  void write(unsigned address, byte value, EmuTime::param time) override;
312  } slottedMemoryDebug;
313 
314  struct IODebug final : SimpleDebuggable {
315  explicit IODebug(MSXMotherBoard& motherBoard);
316  byte read(unsigned address, EmuTime::param time) override;
317  void write(unsigned address, byte value, EmuTime::param time) override;
318  } ioDebug;
319 
320  struct SlotInfo final : InfoTopic {
321  explicit SlotInfo(InfoCommand& machineInfoCommand);
322  void execute(array_ref<TclObject> tokens,
323  TclObject& result) const override;
324  std::string help(const std::vector<std::string>& tokens) const override;
325  } slotInfo;
326 
327  struct SubSlottedInfo final : InfoTopic {
328  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
329  void execute(array_ref<TclObject> tokens,
330  TclObject& result) const override;
331  std::string help(const std::vector<std::string>& tokens) const override;
332  } subSlottedInfo;
333 
334  struct ExternalSlotInfo final : InfoTopic {
335  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
336  void execute(array_ref<TclObject> tokens,
337  TclObject& result) const override;
338  std::string help(const std::vector<std::string>& tokens) const override;
339  } externalSlotInfo;
340 
341  struct IOInfo : InfoTopic {
342  IOInfo(InfoCommand& machineInfoCommand, const char* name);
343  void helper(array_ref<TclObject> tokens,
344  TclObject& result, MSXDevice** devices) const;
345  std::string help(const std::vector<std::string>& tokens) const override;
346  };
347  struct IInfo final : IOInfo {
348  explicit IInfo(InfoCommand& machineInfoCommand)
349  : IOInfo(machineInfoCommand, "input_port") {}
350  void execute(array_ref<TclObject> tokens,
351  TclObject& result) const override;
352  } inputPortInfo;
353  struct OInfo final : IOInfo {
354  explicit OInfo(InfoCommand& machineInfoCommand)
355  : IOInfo(machineInfoCommand, "output_port") {}
356  void execute(array_ref<TclObject> tokens,
357  TclObject& result) const override;
358  } outputPortInfo;
359 
366  void updateVisible(int page);
367  inline void updateVisible(int page, int ps, int ss);
368  void setSubSlot(byte primSlot, byte value);
369 
370  std::unique_ptr<DummyDevice> dummyDevice;
371  MSXCPU& msxcpu;
372  CliComm& cliComm;
373  MSXMotherBoard& motherBoard;
374 
375  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
376 
377  byte disallowReadCache [CacheLine::NUM];
378  byte disallowWriteCache[CacheLine::NUM];
379  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
380  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
381 
382  struct GlobalRwInfo {
383  MSXDevice* device;
384  word addr;
385  bool operator==(const GlobalRwInfo& rhs) const {
386  return (device == rhs.device) &&
387  (addr == rhs.addr);
388  }
389  };
390  std::vector<GlobalRwInfo> globalReads;
391  std::vector<GlobalRwInfo> globalWrites;
392 
393  MSXDevice* IO_In [256];
394  MSXDevice* IO_Out[256];
395  MSXDevice* slotLayout[4][4][4];
396  MSXDevice* visibleDevices[4];
397  byte subSlotRegister[4];
398  byte primarySlotState[4];
399  byte secondarySlotState[4];
400  byte initialPrimarySlots;
401  unsigned expanded[4];
402 
403  bool fastForward; // no need to serialize
404 
405  // All CPUs (Z80 and R800) of all MSX machines share this state.
406  static BreakPoints breakPoints; // sorted on address
407  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
408  static Conditions conditions; // ordered in creation order
409  static bool breaked;
410  static bool continued;
411  static bool step;
412 };
413 
414 
415 // Compile-Time Interval (half-open).
416 // TODO possibly move this to utils/
417 template<unsigned BEGIN, unsigned END = BEGIN + 1>
419 {
420  unsigned begin() const { return BEGIN; } // inclusive
421  unsigned end() const { return END; } // exclusive
422 };
423 
424 // Execute an 'action' for every element in the given interval(s).
425 template<typename ACTION, typename CT_INTERVAL>
426 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
427 {
428  for (auto i = interval.begin(); i != interval.end(); ++i) {
429  action(i);
430  }
431 }
432 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
433 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
434 {
435  foreach_ct_interval(action, front);
436  foreach_ct_interval(action, tail...);
437 }
438 
439 
440 template<typename MSXDEVICE, typename... CT_INTERVALS>
442 {
443  template<typename ACTION>
444  void execute(ACTION action)
445  {
446  auto& dev = static_cast<MSXDEVICE&>(*this);
447  auto& cpu = dev.getCPUInterface();
449  [&](unsigned addr) { action(cpu, dev, addr); },
450  CT_INTERVALS()...);
451  }
452 };
453 
454 template<typename MSXDEVICE, typename... CT_INTERVALS>
455 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
456 {
458  {
459  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
460  cpu.registerGlobalWrite(dev, addr);
461  });
462  }
463 
465  {
466  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
467  cpu.unregisterGlobalWrite(dev, addr);
468  });
469  }
470 };
471 
472 template<typename MSXDEVICE, typename... CT_INTERVALS>
473 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
474 {
476  {
477  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
478  cpu.registerGlobalRead(dev, addr);
479  });
480  }
481 
483  {
484  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
485  cpu.unregisterGlobalRead(dev, addr);
486  });
487  }
488 };
489 
490 } // namespace openmsx
491 
492 #endif
std::vector< BreakPoint > BreakPoints
static bool checkBreakPoints(unsigned pc, MSXMotherBoard &motherBoard)
string_view::const_iterator begin(const string_view &x)
Definition: string_view.hh:152
void unregisterGlobalWrite(MSXDevice &device, word address)
#define unlikely(x)
Definition: likely.hh:15
void unregisterGlobalRead(MSXDevice &device, word address)
void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
bool operator==(string_view x, string_view y)
Definition: string_view.hh:115
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void setFastForward(bool fastForward_)
void writeMem(word address, byte value, EmuTime::param time)
This writes a byte to the currently selected device.
const byte * getReadCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
void registerGlobalRead(MSXDevice &device, word address)
(Un)register global read.
void execute(ACTION action)
static const BreakPoints & getBreakPoints()
const WatchPoints & getWatchPoints() const
word getAddress() const
Definition: BreakPoint.hh:18
size_t size(string_view utf8)
std::vector< std::shared_ptr< WatchPoint > > WatchPoints
Base class for CPU breakpoints.
Definition: BreakPoint.hh:13
unsigned end() const
This class implements a subset of the proposal for std::array_ref (proposed for the next c++ standard...
Definition: array_ref.hh:19
Base class for CPU breakpoints.
Definition: WatchPoint.hh:12
unsigned begin() const
string_view::const_iterator end(const string_view &x)
Definition: string_view.hh:153
void writeIO(word port, byte value, EmuTime::param time)
This writes a byte to the given IO-port.
bool operator()(word x, const BreakPoint &y) const
byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
General debugger condition Like breakpoints, but not tied to a specifc address.
static void setStep(bool x)
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
DummyDevice & getDummyDevice()
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:31
bool operator()(const BreakPoint &x, const BreakPoint &y) const
static const Conditions & getConditions()
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
bool isExpanded(int ps) const
void registerGlobalWrite(MSXDevice &device, word address)
(Un)register global writes.
byte readIO(word port, EmuTime::param time)
This read a byte from the given IO-port.
bool operator()(const BreakPoint &x, word y) const
std::vector< DebugCondition > Conditions
byte readMem(word address, EmuTime::param time)
This reads a byte from the currently selected device.
static void setContinue(bool x)
void serialize(Archive &ar, T &t, unsigned version)