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 "likely.hh"
14 #include "ranges.hh"
15 #include <bitset>
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 (unlikely(disallowReadCache[address >> CacheLine::BITS])) {
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 (unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
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 (unlikely(disallowReadCache[start >> CacheLine::BITS])) {
175  return nullptr;
176  }
177  return visibleDevices[start >> 14]->getReadCacheLine(start);
178  }
179 
192  [[nodiscard]] inline byte* getWriteCacheLine(word start) const {
194  if (unlikely(disallowWriteCache[start >> CacheLine::BITS])) {
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  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  static void insertBreakPoint(BreakPoint bp);
242  static 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  static void setCondition(DebugCondition cond);
253  static 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]] static bool checkBreakPoints(unsigned pc, MSXMotherBoard& motherBoard)
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, motherBoard);
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  static void checkBreakPoints(std::pair<BreakPoints::const_iterator,
308  BreakPoints::const_iterator> range,
309  MSXMotherBoard& motherBoard);
310  static void removeBreakPoint(unsigned id);
311  static void removeCondition(unsigned id);
312 
313  void removeAllWatchPoints();
314  void updateMemWatch(WatchPoint::Type type);
315  void executeMemWatch(WatchPoint::Type type, unsigned address,
316  unsigned value = ~0u);
317 
318  struct MemoryDebug final : SimpleDebuggable {
319  explicit MemoryDebug(MSXMotherBoard& motherBoard);
320  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
321  void write(unsigned address, byte value, EmuTime::param time) override;
322  } memoryDebug;
323 
324  struct SlottedMemoryDebug final : SimpleDebuggable {
325  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
326  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
327  void write(unsigned address, byte value, EmuTime::param time) override;
328  } slottedMemoryDebug;
329 
330  struct IODebug final : SimpleDebuggable {
331  explicit IODebug(MSXMotherBoard& motherBoard);
332  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
333  void write(unsigned address, byte value, EmuTime::param time) override;
334  } ioDebug;
335 
336  struct SlotInfo final : InfoTopic {
337  explicit SlotInfo(InfoCommand& machineInfoCommand);
338  void execute(span<const TclObject> tokens,
339  TclObject& result) const override;
340  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
341  } slotInfo;
342 
343  struct SubSlottedInfo final : InfoTopic {
344  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
345  void execute(span<const TclObject> tokens,
346  TclObject& result) const override;
347  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
348  } subSlottedInfo;
349 
350  struct ExternalSlotInfo final : InfoTopic {
351  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
352  void execute(span<const TclObject> tokens,
353  TclObject& result) const override;
354  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
355  } externalSlotInfo;
356 
357  struct IOInfo : InfoTopic {
358  IOInfo(InfoCommand& machineInfoCommand, const char* name);
359  void helper(span<const TclObject> tokens,
360  TclObject& result, MSXDevice** devices) const;
361  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
362  protected:
363  ~IOInfo() = default;
364  };
365  struct IInfo final : IOInfo {
366  explicit IInfo(InfoCommand& machineInfoCommand)
367  : IOInfo(machineInfoCommand, "input_port") {}
368  void execute(span<const TclObject> tokens,
369  TclObject& result) const override;
370  } inputPortInfo;
371  struct OInfo final : IOInfo {
372  explicit OInfo(InfoCommand& machineInfoCommand)
373  : IOInfo(machineInfoCommand, "output_port") {}
374  void execute(span<const TclObject> tokens,
375  TclObject& result) const override;
376  } outputPortInfo;
377 
384  void updateVisible(int page);
385  inline void updateVisible(int page, int ps, int ss);
386  void setSubSlot(byte primSlot, byte value);
387 
388  std::unique_ptr<DummyDevice> dummyDevice;
389  MSXCPU& msxcpu;
390  CliComm& cliComm;
391  MSXMotherBoard& motherBoard;
392 
393  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
394 
395  byte disallowReadCache [CacheLine::NUM];
396  byte disallowWriteCache[CacheLine::NUM];
397  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
398  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
399 
400  struct GlobalRwInfo {
401  MSXDevice* device;
402  word addr;
403  [[nodiscard]] bool operator==(const GlobalRwInfo& rhs) const {
404  return (device == rhs.device) &&
405  (addr == rhs.addr);
406  }
407  };
408  std::vector<GlobalRwInfo> globalReads;
409  std::vector<GlobalRwInfo> globalWrites;
410 
411  MSXDevice* IO_In [256];
412  MSXDevice* IO_Out[256];
413  MSXDevice* slotLayout[4][4][4];
414  MSXDevice* visibleDevices[4];
415  byte subSlotRegister[4];
416  byte primarySlotState[4];
417  byte secondarySlotState[4];
418  byte initialPrimarySlots;
419  unsigned expanded[4];
420 
421  bool fastForward; // no need to serialize
422 
423  // All CPUs (Z80 and R800) of all MSX machines share this state.
424  static inline BreakPoints breakPoints; // sorted on address
425  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
426  static inline Conditions conditions; // ordered in creation order
427  static inline bool breaked = false;
428 };
429 
430 
431 // Compile-Time Interval (half-open).
432 // TODO possibly move this to utils/
433 template<unsigned BEGIN, unsigned END = BEGIN + 1>
435 {
436  [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
437  [[nodiscard]] unsigned end() const { return END; } // exclusive
438 };
439 
440 // Execute an 'action' for every element in the given interval(s).
441 template<typename ACTION, typename CT_INTERVAL>
442 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
443 {
444  for (auto i = interval.begin(); i != interval.end(); ++i) {
445  action(i);
446  }
447 }
448 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
449 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
450 {
451  foreach_ct_interval(action, front);
452  foreach_ct_interval(action, tail...);
453 }
454 
455 
456 template<typename MSXDEVICE, typename... CT_INTERVALS>
458 {
459  template<typename ACTION>
460  void execute(ACTION action)
461  {
462  auto& dev = static_cast<MSXDEVICE&>(*this);
463  auto& cpu = dev.getCPUInterface();
465  [&](unsigned addr) { action(cpu, dev, addr); },
466  CT_INTERVALS()...);
467  }
468 };
469 
470 template<typename MSXDEVICE, typename... CT_INTERVALS>
471 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
472 {
474  {
475  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
476  cpu.registerGlobalWrite(dev, addr);
477  });
478  }
479 
481  {
482  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
483  cpu.unregisterGlobalWrite(dev, addr);
484  });
485  }
486 };
487 
488 template<typename MSXDEVICE, typename... CT_INTERVALS>
489 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
490 {
492  {
493  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
494  cpu.registerGlobalRead(dev, addr);
495  });
496  }
497 
499  {
500  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
501  cpu.unregisterGlobalRead(dev, addr);
502  });
503  }
504 };
505 
506 } // namespace openmsx
507 
508 #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)
static 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 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)
static 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 testUnsetExpanded(int ps, span< const std::unique_ptr< MSXDevice >> allowed) const
static bool checkBreakPoints(unsigned pc, MSXMotherBoard &motherBoard)
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)
static 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.
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)
static 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:32
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
Definition: span.hh:126
#define unlikely(x)
Definition: likely.hh:15
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
void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
std::ostream & operator<<(std::ostream &os, EnumTypeName< CacheLineCounters >)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition: ranges.hh:102
size_t size(std::string_view utf8)
unsigned end() const
unsigned begin() const
void execute(ACTION action)