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  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(span<const TclObject> tokens,
338  TclObject& result) const override;
339  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
340  } slotInfo;
341 
342  struct SubSlottedInfo final : InfoTopic {
343  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
344  void execute(span<const TclObject> tokens,
345  TclObject& result) const override;
346  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
347  } subSlottedInfo;
348 
349  struct ExternalSlotInfo final : InfoTopic {
350  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
351  void execute(span<const TclObject> tokens,
352  TclObject& result) const override;
353  [[nodiscard]] std::string help(span<const TclObject> tokens) const override;
354  } externalSlotInfo;
355 
356  struct IOInfo : InfoTopic {
357  IOInfo(InfoCommand& machineInfoCommand, const char* name);
358  void helper(span<const TclObject> tokens,
359  TclObject& result, MSXDevice** devices) const;
360  [[nodiscard]] std::string help(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(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(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]] bool operator==(const GlobalRwInfo& rhs) const {
403  return (device == rhs.device) &&
404  (addr == rhs.addr);
405  }
406  };
407  std::vector<GlobalRwInfo> globalReads;
408  std::vector<GlobalRwInfo> globalWrites;
409 
410  MSXDevice* IO_In [256];
411  MSXDevice* IO_Out[256];
412  MSXDevice* slotLayout[4][4][4];
413  MSXDevice* visibleDevices[4];
414  byte subSlotRegister[4];
415  byte primarySlotState[4];
416  byte secondarySlotState[4];
417  byte initialPrimarySlots;
418  unsigned expanded[4];
419 
420  bool fastForward; // no need to serialize
421 
422  // All CPUs (Z80 and R800) of all MSX machines share this state.
423  static inline BreakPoints breakPoints; // sorted on address
424  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
425  static inline Conditions conditions; // ordered in creation order
426  static inline bool breaked = false;
427 };
428 
429 
430 // Compile-Time Interval (half-open).
431 // TODO possibly move this to utils/
432 template<unsigned BEGIN, unsigned END = BEGIN + 1>
434 {
435  [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
436  [[nodiscard]] unsigned end() const { return END; } // exclusive
437 };
438 
439 // Execute an 'action' for every element in the given interval(s).
440 template<typename ACTION, typename CT_INTERVAL>
441 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
442 {
443  for (auto i = interval.begin(); i != interval.end(); ++i) {
444  action(i);
445  }
446 }
447 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
448 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
449 {
450  foreach_ct_interval(action, front);
451  foreach_ct_interval(action, tail...);
452 }
453 
454 
455 template<typename MSXDEVICE, typename... CT_INTERVALS>
457 {
458  template<typename ACTION>
459  void execute(ACTION action)
460  {
461  auto& dev = static_cast<MSXDEVICE&>(*this);
462  auto& cpu = dev.getCPUInterface();
464  [&](unsigned addr) { action(cpu, dev, addr); },
465  CT_INTERVALS()...);
466  }
467 };
468 
469 template<typename MSXDEVICE, typename... CT_INTERVALS>
470 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
471 {
473  {
474  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
475  cpu.registerGlobalWrite(dev, addr);
476  });
477  }
478 
480  {
481  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
482  cpu.unregisterGlobalWrite(dev, addr);
483  });
484  }
485 };
486 
487 template<typename MSXDEVICE, typename... CT_INTERVALS>
488 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
489 {
491  {
492  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
493  cpu.registerGlobalRead(dev, addr);
494  });
495  }
496 
498  {
499  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
500  cpu.unregisterGlobalRead(dev, addr);
501  });
502  }
503 };
504 
505 } // namespace openmsx
506 
507 #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 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 testUnsetExpanded(int ps, span< const std::unique_ptr< MSXDevice >> allowed) 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
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:118
size_t size(std::string_view utf8)
unsigned end() const
unsigned begin() const
void execute(ACTION action)