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 
292  void removeAllWatchPoints();
293  void registerIOWatch (WatchPoint& watchPoint, MSXDevice** devices);
294  void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices);
295  void updateMemWatch(WatchPoint::Type type);
296  void executeMemWatch(WatchPoint::Type type, unsigned address,
297  unsigned value = ~0u);
298 
299  void doContinue2();
300 
301  struct MemoryDebug final : SimpleDebuggable {
302  explicit MemoryDebug(MSXMotherBoard& motherBoard);
303  byte read(unsigned address, EmuTime::param time) override;
304  void write(unsigned address, byte value, EmuTime::param time) override;
305  } memoryDebug;
306 
307  struct SlottedMemoryDebug final : SimpleDebuggable {
308  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
309  byte read(unsigned address, EmuTime::param time) override;
310  void write(unsigned address, byte value, EmuTime::param time) override;
311  } slottedMemoryDebug;
312 
313  struct IODebug final : SimpleDebuggable {
314  explicit IODebug(MSXMotherBoard& motherBoard);
315  byte read(unsigned address, EmuTime::param time) override;
316  void write(unsigned address, byte value, EmuTime::param time) override;
317  } ioDebug;
318 
319  struct SlotInfo final : InfoTopic {
320  explicit SlotInfo(InfoCommand& machineInfoCommand);
321  void execute(span<const TclObject> tokens,
322  TclObject& result) const override;
323  std::string help(const std::vector<std::string>& tokens) const override;
324  } slotInfo;
325 
326  struct SubSlottedInfo final : InfoTopic {
327  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
328  void execute(span<const TclObject> tokens,
329  TclObject& result) const override;
330  std::string help(const std::vector<std::string>& tokens) const override;
331  } subSlottedInfo;
332 
333  struct ExternalSlotInfo final : InfoTopic {
334  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
335  void execute(span<const TclObject> tokens,
336  TclObject& result) const override;
337  std::string help(const std::vector<std::string>& tokens) const override;
338  } externalSlotInfo;
339 
340  struct IOInfo : InfoTopic {
341  IOInfo(InfoCommand& machineInfoCommand, const char* name);
342  void helper(span<const TclObject> tokens,
343  TclObject& result, MSXDevice** devices) const;
344  std::string help(const std::vector<std::string>& tokens) const override;
345  protected:
346  ~IOInfo() = default;
347  };
348  struct IInfo final : IOInfo {
349  explicit IInfo(InfoCommand& machineInfoCommand)
350  : IOInfo(machineInfoCommand, "input_port") {}
351  void execute(span<const TclObject> tokens,
352  TclObject& result) const override;
353  } inputPortInfo;
354  struct OInfo final : IOInfo {
355  explicit OInfo(InfoCommand& machineInfoCommand)
356  : IOInfo(machineInfoCommand, "output_port") {}
357  void execute(span<const TclObject> tokens,
358  TclObject& result) const override;
359  } outputPortInfo;
360 
367  void updateVisible(int page);
368  inline void updateVisible(int page, int ps, int ss);
369  void setSubSlot(byte primSlot, byte value);
370 
371  std::unique_ptr<DummyDevice> dummyDevice;
372  MSXCPU& msxcpu;
373  CliComm& cliComm;
374  MSXMotherBoard& motherBoard;
375 
376  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
377 
378  byte disallowReadCache [CacheLine::NUM];
379  byte disallowWriteCache[CacheLine::NUM];
380  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
381  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
382 
383  struct GlobalRwInfo {
384  MSXDevice* device;
385  word addr;
386  bool operator==(const GlobalRwInfo& rhs) const {
387  return (device == rhs.device) &&
388  (addr == rhs.addr);
389  }
390  };
391  std::vector<GlobalRwInfo> globalReads;
392  std::vector<GlobalRwInfo> globalWrites;
393 
394  MSXDevice* IO_In [256];
395  MSXDevice* IO_Out[256];
396  MSXDevice* slotLayout[4][4][4];
397  MSXDevice* visibleDevices[4];
398  byte subSlotRegister[4];
399  byte primarySlotState[4];
400  byte secondarySlotState[4];
401  byte initialPrimarySlots;
402  unsigned expanded[4];
403 
404  bool fastForward; // no need to serialize
405 
406  // All CPUs (Z80 and R800) of all MSX machines share this state.
407  static BreakPoints breakPoints; // sorted on address
408  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
409  static Conditions conditions; // ordered in creation order
410  static bool breaked;
411  static bool continued;
412  static bool step;
413 };
414 
415 
416 // Compile-Time Interval (half-open).
417 // TODO possibly move this to utils/
418 template<unsigned BEGIN, unsigned END = BEGIN + 1>
420 {
421  unsigned begin() const { return BEGIN; } // inclusive
422  unsigned end() const { return END; } // exclusive
423 };
424 
425 // Execute an 'action' for every element in the given interval(s).
426 template<typename ACTION, typename CT_INTERVAL>
427 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
428 {
429  for (auto i = interval.begin(); i != interval.end(); ++i) {
430  action(i);
431  }
432 }
433 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
434 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
435 {
436  foreach_ct_interval(action, front);
437  foreach_ct_interval(action, tail...);
438 }
439 
440 
441 template<typename MSXDEVICE, typename... CT_INTERVALS>
443 {
444  template<typename ACTION>
445  void execute(ACTION action)
446  {
447  auto& dev = static_cast<MSXDEVICE&>(*this);
448  auto& cpu = dev.getCPUInterface();
450  [&](unsigned addr) { action(cpu, dev, addr); },
451  CT_INTERVALS()...);
452  }
453 };
454 
455 template<typename MSXDEVICE, typename... CT_INTERVALS>
456 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
457 {
459  {
460  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
461  cpu.registerGlobalWrite(dev, addr);
462  });
463  }
464 
466  {
467  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
468  cpu.unregisterGlobalWrite(dev, addr);
469  });
470  }
471 };
472 
473 template<typename MSXDEVICE, typename... CT_INTERVALS>
474 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
475 {
477  {
478  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
479  cpu.registerGlobalRead(dev, addr);
480  });
481  }
482 
484  {
485  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
486  cpu.unregisterGlobalRead(dev, addr);
487  });
488  }
489 };
490 
491 } // namespace openmsx
492 
493 #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)