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 "ranges.hh"
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 = ranges::equal_range(breakPoints, pc, CompareBreakpoints());
251  if (conditions.empty() && (range.first == range.second)) {
252  return false;
253  }
254 
255  // slow path non-inlined
256  checkBreakPoints(range, motherBoard);
257  return isBreaked();
258  }
259 
260  // cleanup global variables
261  static void cleanup();
262 
263  // In fast-forward mode, breakpoints, watchpoints and conditions should
264  // not trigger.
265  void setFastForward(bool fastForward_) { fastForward = fastForward_; }
266  bool isFastForward() const { return fastForward; }
267 
268  template<typename Archive>
269  void serialize(Archive& ar, unsigned version);
270 
271 private:
272  byte readMemSlow(word address, EmuTime::param time);
273  void writeMemSlow(word address, byte value, EmuTime::param time);
274 
275  MSXDevice*& getDevicePtr(byte port, bool isIn);
276 
277  void register_IO (int port, bool isIn,
278  MSXDevice*& devicePtr, MSXDevice* device);
279  void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
280  void testRegisterSlot(MSXDevice& device,
281  int ps, int ss, int base, int size);
282  void registerSlot(MSXDevice& device,
283  int ps, int ss, int base, int size);
284  void unregisterSlot(MSXDevice& device,
285  int ps, int ss, int base, int size);
286 
287 
288  static void checkBreakPoints(std::pair<BreakPoints::const_iterator,
289  BreakPoints::const_iterator> range,
290  MSXMotherBoard& motherBoard);
291  static void removeBreakPoint(unsigned id);
292  static void removeCondition(unsigned id);
293 
294  void removeAllWatchPoints();
295  void registerIOWatch (WatchPoint& watchPoint, MSXDevice** devices);
296  void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices);
297  void updateMemWatch(WatchPoint::Type type);
298  void executeMemWatch(WatchPoint::Type type, unsigned address,
299  unsigned value = ~0u);
300 
301  void doContinue2();
302 
303  struct MemoryDebug final : SimpleDebuggable {
304  explicit MemoryDebug(MSXMotherBoard& motherBoard);
305  byte read(unsigned address, EmuTime::param time) override;
306  void write(unsigned address, byte value, EmuTime::param time) override;
307  } memoryDebug;
308 
309  struct SlottedMemoryDebug final : SimpleDebuggable {
310  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
311  byte read(unsigned address, EmuTime::param time) override;
312  void write(unsigned address, byte value, EmuTime::param time) override;
313  } slottedMemoryDebug;
314 
315  struct IODebug final : SimpleDebuggable {
316  explicit IODebug(MSXMotherBoard& motherBoard);
317  byte read(unsigned address, EmuTime::param time) override;
318  void write(unsigned address, byte value, EmuTime::param time) override;
319  } ioDebug;
320 
321  struct SlotInfo final : InfoTopic {
322  explicit SlotInfo(InfoCommand& machineInfoCommand);
323  void execute(span<const TclObject> tokens,
324  TclObject& result) const override;
325  std::string help(const std::vector<std::string>& tokens) const override;
326  } slotInfo;
327 
328  struct SubSlottedInfo final : InfoTopic {
329  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
330  void execute(span<const TclObject> tokens,
331  TclObject& result) const override;
332  std::string help(const std::vector<std::string>& tokens) const override;
333  } subSlottedInfo;
334 
335  struct ExternalSlotInfo final : InfoTopic {
336  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
337  void execute(span<const TclObject> tokens,
338  TclObject& result) const override;
339  std::string help(const std::vector<std::string>& tokens) const override;
340  } externalSlotInfo;
341 
342  struct IOInfo : InfoTopic {
343  IOInfo(InfoCommand& machineInfoCommand, const char* name);
344  void helper(span<const TclObject> tokens,
345  TclObject& result, MSXDevice** devices) const;
346  std::string help(const std::vector<std::string>& tokens) const override;
347  protected:
348  ~IOInfo() = default;
349  };
350  struct IInfo final : IOInfo {
351  explicit IInfo(InfoCommand& machineInfoCommand)
352  : IOInfo(machineInfoCommand, "input_port") {}
353  void execute(span<const TclObject> tokens,
354  TclObject& result) const override;
355  } inputPortInfo;
356  struct OInfo final : IOInfo {
357  explicit OInfo(InfoCommand& machineInfoCommand)
358  : IOInfo(machineInfoCommand, "output_port") {}
359  void execute(span<const TclObject> tokens,
360  TclObject& result) const override;
361  } outputPortInfo;
362 
369  void updateVisible(int page);
370  inline void updateVisible(int page, int ps, int ss);
371  void setSubSlot(byte primSlot, byte value);
372 
373  std::unique_ptr<DummyDevice> dummyDevice;
374  MSXCPU& msxcpu;
375  CliComm& cliComm;
376  MSXMotherBoard& motherBoard;
377 
378  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
379 
380  byte disallowReadCache [CacheLine::NUM];
381  byte disallowWriteCache[CacheLine::NUM];
382  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
383  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
384 
385  struct GlobalRwInfo {
386  MSXDevice* device;
387  word addr;
388  bool operator==(const GlobalRwInfo& rhs) const {
389  return (device == rhs.device) &&
390  (addr == rhs.addr);
391  }
392  };
393  std::vector<GlobalRwInfo> globalReads;
394  std::vector<GlobalRwInfo> globalWrites;
395 
396  MSXDevice* IO_In [256];
397  MSXDevice* IO_Out[256];
398  MSXDevice* slotLayout[4][4][4];
399  MSXDevice* visibleDevices[4];
400  byte subSlotRegister[4];
401  byte primarySlotState[4];
402  byte secondarySlotState[4];
403  byte initialPrimarySlots;
404  unsigned expanded[4];
405 
406  bool fastForward; // no need to serialize
407 
408  // All CPUs (Z80 and R800) of all MSX machines share this state.
409  static BreakPoints breakPoints; // sorted on address
410  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
411  static Conditions conditions; // ordered in creation order
412  static bool breaked;
413  static bool continued;
414  static bool step;
415 };
416 
417 
418 // Compile-Time Interval (half-open).
419 // TODO possibly move this to utils/
420 template<unsigned BEGIN, unsigned END = BEGIN + 1>
422 {
423  unsigned begin() const { return BEGIN; } // inclusive
424  unsigned end() const { return END; } // exclusive
425 };
426 
427 // Execute an 'action' for every element in the given interval(s).
428 template<typename ACTION, typename CT_INTERVAL>
429 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
430 {
431  for (auto i = interval.begin(); i != interval.end(); ++i) {
432  action(i);
433  }
434 }
435 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
436 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
437 {
438  foreach_ct_interval(action, front);
439  foreach_ct_interval(action, tail...);
440 }
441 
442 
443 template<typename MSXDEVICE, typename... CT_INTERVALS>
445 {
446  template<typename ACTION>
447  void execute(ACTION action)
448  {
449  auto& dev = static_cast<MSXDEVICE&>(*this);
450  auto& cpu = dev.getCPUInterface();
452  [&](unsigned addr) { action(cpu, dev, addr); },
453  CT_INTERVALS()...);
454  }
455 };
456 
457 template<typename MSXDEVICE, typename... CT_INTERVALS>
458 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
459 {
461  {
462  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
463  cpu.registerGlobalWrite(dev, addr);
464  });
465  }
466 
468  {
469  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
470  cpu.unregisterGlobalWrite(dev, addr);
471  });
472  }
473 };
474 
475 template<typename MSXDEVICE, typename... CT_INTERVALS>
476 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
477 {
479  {
480  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
481  cpu.registerGlobalRead(dev, addr);
482  });
483  }
484 
486  {
487  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
488  cpu.unregisterGlobalRead(dev, addr);
489  });
490  }
491 };
492 
493 } // namespace openmsx
494 
495 #endif
std::vector< BreakPoint > BreakPoints
static bool checkBreakPoints(unsigned pc, MSXMotherBoard &motherBoard)
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)
constexpr bool operator==(const optional< T > &x, const optional< T > &y)
Definition: optional.hh:503
Definition: span.hh:34
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
std::vector< std::shared_ptr< WatchPoint > > WatchPoints
Base class for CPU breakpoints.
Definition: BreakPoint.hh:13
unsigned end() const
Base class for CPU breakpoints.
Definition: WatchPoint.hh:12
auto equal_range(ForwardRange &&range, const T &value)
Definition: ranges.hh:95
unsigned begin() const
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.
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
static void setContinue(bool x)
void serialize(Archive &ar, T &t, unsigned version)