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