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 "ProfileCounters.hh"
12 #include "openmsx.hh"
13 #include "ranges.hh"
14 #include <bitset>
15 #include <concepts>
16 #include <vector>
17 #include <memory>
18 
19 namespace openmsx {
20 
21 class VDPIODelay;
22 class DummyDevice;
23 class MSXMotherBoard;
24 class MSXCPU;
25 class CliComm;
26 class BreakPoint;
27 
28 constexpr bool PROFILE_CACHELINES = false;
45  NUM // must be last
46 };
47 std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>);
48 std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn);
49 
50 class MSXCPUInterface : public ProfileCounters<PROFILE_CACHELINES, CacheLineCounters>
51 {
52 public:
53  MSXCPUInterface(const MSXCPUInterface&) = delete;
55 
56  explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
58 
64  void register_IO_In(byte port, MSXDevice* device);
65  void unregister_IO_In(byte port, MSXDevice* device);
66 
72  void register_IO_Out(byte port, MSXDevice* device);
73  void unregister_IO_Out(byte port, MSXDevice* device);
74 
90  bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
91  bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
92 
99  void registerMemDevice(MSXDevice& device,
100  int ps, int ss, int base, int size);
101  void unregisterMemDevice(MSXDevice& device,
102  int ps, int ss, int base, int size);
103 
107  void registerGlobalWrite(MSXDevice& device, word address);
108  void unregisterGlobalWrite(MSXDevice& device, word address);
109 
113  void registerGlobalRead(MSXDevice& device, word address);
114  void unregisterGlobalRead(MSXDevice& device, word address);
115 
119  void reset();
120 
124  inline byte readMem(word address, EmuTime::param time) {
126  if (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
127  return readMemSlow(address, time);
128  }
129  return visibleDevices[address >> 14]->readMem(address, time);
130  }
131 
135  inline void writeMem(word address, byte value, EmuTime::param time) {
137  if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
138  writeMemSlow(address, value, time);
139  return;
140  }
141  visibleDevices[address>>14]->writeMem(address, value, time);
142  }
143 
148  inline byte readIO(word port, EmuTime::param time) {
149  return IO_In[port & 0xFF]->readIO(port, time);
150  }
151 
156  inline void writeIO(word port, byte value, EmuTime::param time) {
157  IO_Out[port & 0xFF]->writeIO(port, value, time);
158  }
159 
172  [[nodiscard]] inline const byte* getReadCacheLine(word start) const {
174  if (disallowReadCache[start >> CacheLine::BITS]) [[unlikely]] {
175  return nullptr;
176  }
177  return visibleDevices[start >> 14]->getReadCacheLine(start);
178  }
179 
192  [[nodiscard]] inline byte* getWriteCacheLine(word start) const {
194  if (disallowWriteCache[start >> CacheLine::BITS]) [[unlikely]] {
195  return nullptr;
196  }
197  return visibleDevices[start >> 14]->getWriteCacheLine(start);
198  }
199 
204  [[nodiscard]] byte readIRQVector();
205 
206  /*
207  * Should only be used by PPI
208  * TODO: make private / friend
209  */
210  void setPrimarySlots(byte value);
211 
213  void invalidateRWCache(word start, unsigned size, int ps, int ss);
214  void invalidateRCache (word start, unsigned size, int ps, int ss);
215  void invalidateWCache (word start, unsigned size, int ps, int ss);
216 
218  void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
219  void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
220  void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
221 
226  [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
227  [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
228  byte readSlottedMem(unsigned address, EmuTime::param time);
229  void writeSlottedMem(unsigned address, byte value,
230  EmuTime::param time);
231 
232  void setExpanded(int ps);
233  void unsetExpanded(int ps);
234  void testUnsetExpanded(int ps,
235  std::span<const std::unique_ptr<MSXDevice>> allowed) const;
236  [[nodiscard]] inline bool isExpanded(int ps) const { return expanded[ps] != 0; }
237  void changeExpanded(bool newExpanded);
238 
239  [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
240 
241  void insertBreakPoint(BreakPoint bp);
242  void removeBreakPoint(const BreakPoint& bp);
243  using BreakPoints = std::vector<BreakPoint>;
244  [[nodiscard]] static const BreakPoints& getBreakPoints() { return breakPoints; }
245 
246  void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
247  void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
248  // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
249  using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
250  [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
251 
252  void setCondition(DebugCondition cond);
253  void removeCondition(const DebugCondition& cond);
254  using Conditions = std::vector<DebugCondition>;
255  [[nodiscard]] static const Conditions& getConditions() { return conditions; }
256 
257  [[nodiscard]] static bool isBreaked() { return breaked; }
258  void doBreak();
259  void doStep();
260  void doContinue();
261 
262  // breakpoint methods used by CPUCore
263  [[nodiscard]] static bool anyBreakPoints()
264  {
265  return !breakPoints.empty() || !conditions.empty();
266  }
267  [[nodiscard]] bool checkBreakPoints(unsigned pc)
268  {
269  auto range = ranges::equal_range(breakPoints, pc, {}, &BreakPoint::getAddress);
270  if (conditions.empty() && (range.first == range.second)) {
271  return false;
272  }
273 
274  // slow path non-inlined
275  checkBreakPoints(range);
276  return isBreaked();
277  }
278 
279  // cleanup global variables
280  static void cleanup();
281 
282  // In fast-forward mode, breakpoints, watchpoints and conditions should
283  // not trigger.
284  void setFastForward(bool fastForward_) { fastForward = fastForward_; }
285  [[nodiscard]] bool isFastForward() const { return fastForward; }
286 
287  template<typename Archive>
288  void serialize(Archive& ar, unsigned version);
289 
290 private:
291  byte readMemSlow(word address, EmuTime::param time);
292  void writeMemSlow(word address, byte value, EmuTime::param time);
293 
294  MSXDevice*& getDevicePtr(byte port, bool isIn);
295 
296  void register_IO (int port, bool isIn,
297  MSXDevice*& devicePtr, MSXDevice* device);
298  void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
299  void testRegisterSlot(MSXDevice& device,
300  int ps, int ss, int base, int size);
301  void registerSlot(MSXDevice& device,
302  int ps, int ss, int base, int size);
303  void unregisterSlot(MSXDevice& device,
304  int ps, int ss, int base, int size);
305 
306 
307  void checkBreakPoints(std::pair<BreakPoints::const_iterator,
308  BreakPoints::const_iterator> range);
309  void removeBreakPoint(unsigned id);
310  void removeCondition(unsigned id);
311 
312  void removeAllWatchPoints();
313  void updateMemWatch(WatchPoint::Type type);
314  void executeMemWatch(WatchPoint::Type type, unsigned address,
315  unsigned value = ~0u);
316 
317  struct MemoryDebug final : SimpleDebuggable {
318  explicit MemoryDebug(MSXMotherBoard& motherBoard);
319  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
320  void write(unsigned address, byte value, EmuTime::param time) override;
321  } memoryDebug;
322 
323  struct SlottedMemoryDebug final : SimpleDebuggable {
324  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
325  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
326  void write(unsigned address, byte value, EmuTime::param time) override;
327  } slottedMemoryDebug;
328 
329  struct IODebug final : SimpleDebuggable {
330  explicit IODebug(MSXMotherBoard& motherBoard);
331  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
332  void write(unsigned address, byte value, EmuTime::param time) override;
333  } ioDebug;
334 
335  struct SlotInfo final : InfoTopic {
336  explicit SlotInfo(InfoCommand& machineInfoCommand);
337  void execute(std::span<const TclObject> tokens,
338  TclObject& result) const override;
339  [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
340  } slotInfo;
341 
342  struct SubSlottedInfo final : InfoTopic {
343  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
344  void execute(std::span<const TclObject> tokens,
345  TclObject& result) const override;
346  [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
347  } subSlottedInfo;
348 
349  struct ExternalSlotInfo final : InfoTopic {
350  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
351  void execute(std::span<const TclObject> tokens,
352  TclObject& result) const override;
353  [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
354  } externalSlotInfo;
355 
356  struct IOInfo : InfoTopic {
357  IOInfo(InfoCommand& machineInfoCommand, const char* name);
358  void helper(std::span<const TclObject> tokens,
359  TclObject& result, MSXDevice** devices) const;
360  [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
361  protected:
362  ~IOInfo() = default;
363  };
364  struct IInfo final : IOInfo {
365  explicit IInfo(InfoCommand& machineInfoCommand)
366  : IOInfo(machineInfoCommand, "input_port") {}
367  void execute(std::span<const TclObject> tokens,
368  TclObject& result) const override;
369  } inputPortInfo;
370  struct OInfo final : IOInfo {
371  explicit OInfo(InfoCommand& machineInfoCommand)
372  : IOInfo(machineInfoCommand, "output_port") {}
373  void execute(std::span<const TclObject> tokens,
374  TclObject& result) const override;
375  } outputPortInfo;
376 
383  void updateVisible(int page);
384  inline void updateVisible(int page, int ps, int ss);
385  void setSubSlot(byte primSlot, byte value);
386 
387  std::unique_ptr<DummyDevice> dummyDevice;
388  MSXCPU& msxcpu;
389  CliComm& cliComm;
390  MSXMotherBoard& motherBoard;
391 
392  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
393 
394  byte disallowReadCache [CacheLine::NUM];
395  byte disallowWriteCache[CacheLine::NUM];
396  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
397  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
398 
399  struct GlobalRwInfo {
400  MSXDevice* device;
401  word addr;
402  [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
403  };
404  std::vector<GlobalRwInfo> globalReads;
405  std::vector<GlobalRwInfo> globalWrites;
406 
407  MSXDevice* IO_In [256];
408  MSXDevice* IO_Out[256];
409  MSXDevice* slotLayout[4][4][4];
410  MSXDevice* visibleDevices[4];
411  byte subSlotRegister[4];
412  byte primarySlotState[4];
413  byte secondarySlotState[4];
414  byte initialPrimarySlots;
415  unsigned expanded[4];
416 
417  bool fastForward; // no need to serialize
418 
419  // All CPUs (Z80 and R800) of all MSX machines share this state.
420  static inline BreakPoints breakPoints; // sorted on address
421  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
422  static inline Conditions conditions; // ordered in creation order
423  static inline bool breaked = false;
424 };
425 
426 
427 // Compile-Time Interval (half-open).
428 // TODO possibly move this to utils/
429 template<unsigned BEGIN, unsigned END = BEGIN + 1>
431 {
432  [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
433  [[nodiscard]] unsigned end() const { return END; } // exclusive
434 };
435 
436 // Execute an 'action' for every element in the given interval(s).
437 inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
438 {
439  for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
440  action(i);
441  }
442 }
443 inline void foreach_ct_interval(std::invocable<unsigned> auto action,
444  auto front_interval, auto... tail_intervals)
445 {
446  foreach_ct_interval(action, front_interval);
447  foreach_ct_interval(action, tail_intervals...);
448 }
449 
450 
451 template<typename MSXDEVICE, typename... CT_INTERVALS>
453 {
454  void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
455  {
456  auto& dev = static_cast<MSXDEVICE&>(*this);
457  auto& cpu = dev.getCPUInterface();
459  [&](unsigned addr) { action(cpu, dev, addr); },
460  CT_INTERVALS()...);
461  }
462 };
463 
464 template<typename MSXDEVICE, typename... CT_INTERVALS>
465 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
466 {
468  {
469  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
470  cpu.registerGlobalWrite(dev, addr);
471  });
472  }
473 
475  {
476  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
477  cpu.unregisterGlobalWrite(dev, addr);
478  });
479  }
480 };
481 
482 template<typename MSXDEVICE, typename... CT_INTERVALS>
483 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
484 {
486  {
487  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
488  cpu.registerGlobalRead(dev, addr);
489  });
490  }
491 
493  {
494  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
495  cpu.unregisterGlobalRead(dev, addr);
496  });
497  }
498 };
499 
500 } // namespace openmsx
501 
502 #endif
Base class for CPU breakpoints.
Definition: BreakPoint.hh:14
word getAddress() const
Definition: BreakPoint.hh:21
General debugger condition Like breakpoints, but not tied to a specifc address.
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
void invalidateRWCache(word start, unsigned size, int ps, int ss)
void insertBreakPoint(BreakPoint bp)
DummyDevice & getDummyDevice()
std::vector< DebugCondition > Conditions
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss)
void testUnsetExpanded(int ps, std::span< const std::unique_ptr< MSXDevice >> allowed) const
void unregister_IO_In(byte port, MSXDevice *device)
void writeIO(word port, byte value, EmuTime::param time)
This writes a byte to the given IO-port.
bool replace_IO_Out(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
void changeExpanded(bool newExpanded)
void removeCondition(const DebugCondition &cond)
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
static const BreakPoints & getBreakPoints()
byte readIO(word port, EmuTime::param time)
This read a byte from the given IO-port.
const WatchPoints & getWatchPoints() const
void writeSlottedMem(unsigned address, byte value, EmuTime::param time)
void setPrimarySlots(byte value)
void invalidateRCache(word start, unsigned size, int ps, int ss)
void unregisterGlobalWrite(MSXDevice &device, word address)
byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
void unregisterGlobalRead(MSXDevice &device, word address)
byte peekSlottedMem(unsigned address, EmuTime::param time) const
void removeWatchPoint(std::shared_ptr< WatchPoint > watchPoint)
std::vector< std::shared_ptr< WatchPoint > > WatchPoints
byte readMem(word address, EmuTime::param time)
This reads a byte from the currently selected device.
void fillRWCache(unsigned start, unsigned size, const byte *rData, byte *wData, int ps, int ss)
MSXCPUInterface & operator=(const MSXCPUInterface &)=delete
void reset()
Reset (the slot state)
void setCondition(DebugCondition cond)
void registerMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
Devices can register themself in the MSX slotstructure.
void invalidateWCache(word start, unsigned size, int ps, int ss)
byte readIRQVector()
CPU uses this method to read 'extra' data from the databus used in interrupt routines.
bool checkBreakPoints(unsigned pc)
void serialize(Archive &ar, unsigned version)
void unregisterMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
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 writeMem(word address, byte value, EmuTime::param time)
This writes a byte to the currently selected device.
byte readSlottedMem(unsigned address, EmuTime::param time)
void removeBreakPoint(const BreakPoint &bp)
void unregister_IO_Out(byte port, MSXDevice *device)
void registerGlobalWrite(MSXDevice &device, word address)
(Un)register global writes.
std::vector< BreakPoint > BreakPoints
bool replace_IO_In(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
These methods replace a previously registered device with a new one.
static const Conditions & getConditions()
void setFastForward(bool fastForward_)
MSXCPUInterface(const MSXCPUInterface &)=delete
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss)
bool isExpanded(int ps) const
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
virtual const byte * getReadCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition: MSXDevice.cc:426
virtual void writeIO(word port, byte value, EmuTime::param time)
Write a byte to a given IO port at a certain time to this device.
Definition: MSXDevice.cc:409
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:420
virtual void writeMem(word address, byte value, EmuTime::param time)
Write a given byte to a given location at a certain time to this device.
Definition: MSXDevice.cc:431
virtual byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: MSXDevice.cc:460
virtual byte readIO(word port, EmuTime::param time)
Read a byte from an IO port at a certain time from this device.
Definition: MSXDevice.cc:403
byte read(unsigned address) override
void write(unsigned address, byte value) override
constexpr unsigned NUM
Definition: CacheLine.hh:8
constexpr unsigned BITS
Definition: CacheLine.hh:6
This file implemented 3 utility functions:
Definition: Autofire.cc:9
bool operator==(const Event &x, const Event &y)
Definition: Event.cc:11
@ InvalidateAllSlots
@ DisallowCacheWrite
@ InvalidateReadWrite
constexpr bool PROFILE_CACHELINES
std::ostream & operator<<(std::ostream &os, EnumTypeName< CacheLineCounters >)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void foreach_ct_interval(std::invocable< unsigned > auto action, auto ct_interval)
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:117
size_t size(std::string_view utf8)
unsigned end() const
unsigned begin() const
void execute(std::invocable< MSXCPUInterface &, MSXDevice &, unsigned > auto action)