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 class CartridgeSlotManager;
28 
30  [[nodiscard]] bool operator()(const BreakPoint& x, const BreakPoint& y) const {
31  return x.getAddress() < y.getAddress();
32  }
33  [[nodiscard]] bool operator()(const BreakPoint& x, word y) const {
34  return x.getAddress() < y;
35  }
36  [[nodiscard]] bool operator()(word x, const BreakPoint& y) const {
37  return x < y.getAddress();
38  }
39 };
40 
41 constexpr bool PROFILE_CACHELINES = false;
58  NUM // must be last
59 };
60 std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>);
61 std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn);
62 
63 class MSXCPUInterface : public ProfileCounters<PROFILE_CACHELINES, CacheLineCounters>
64 {
65 public:
66  MSXCPUInterface(const MSXCPUInterface&) = delete;
68 
69  explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
71 
77  void register_IO_In(byte port, MSXDevice* device);
78  void unregister_IO_In(byte port, MSXDevice* device);
79 
85  void register_IO_Out(byte port, MSXDevice* device);
86  void unregister_IO_Out(byte port, MSXDevice* device);
87 
103  bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
104  bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
105 
112  void registerMemDevice(MSXDevice& device,
113  int ps, int ss, int base, int size);
114  void unregisterMemDevice(MSXDevice& device,
115  int ps, int ss, int base, int size);
116 
120  void registerGlobalWrite(MSXDevice& device, word address);
121  void unregisterGlobalWrite(MSXDevice& device, word address);
122 
126  void registerGlobalRead(MSXDevice& device, word address);
127  void unregisterGlobalRead(MSXDevice& device, word address);
128 
132  void reset();
133 
137  inline byte readMem(word address, EmuTime::param time) {
139  if (unlikely(disallowReadCache[address >> CacheLine::BITS])) {
140  return readMemSlow(address, time);
141  }
142  return visibleDevices[address >> 14]->readMem(address, time);
143  }
144 
148  inline void writeMem(word address, byte value, EmuTime::param time) {
150  if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) {
151  writeMemSlow(address, value, time);
152  return;
153  }
154  visibleDevices[address>>14]->writeMem(address, value, time);
155  }
156 
161  inline byte readIO(word port, EmuTime::param time) {
162  return IO_In[port & 0xFF]->readIO(port, time);
163  }
164 
169  inline void writeIO(word port, byte value, EmuTime::param time) {
170  IO_Out[port & 0xFF]->writeIO(port, value, time);
171  }
172 
185  [[nodiscard]] inline const byte* getReadCacheLine(word start) const {
187  if (unlikely(disallowReadCache[start >> CacheLine::BITS])) {
188  return nullptr;
189  }
190  return visibleDevices[start >> 14]->getReadCacheLine(start);
191  }
192 
205  [[nodiscard]] inline byte* getWriteCacheLine(word start) const {
207  if (unlikely(disallowWriteCache[start >> CacheLine::BITS])) {
208  return nullptr;
209  }
210  return visibleDevices[start >> 14]->getWriteCacheLine(start);
211  }
212 
217  [[nodiscard]] byte readIRQVector();
218 
219  /*
220  * Should only be used by PPI
221  * TODO: make private / friend
222  */
223  void setPrimarySlots(byte value);
224 
226  void invalidateRWCache(word start, unsigned size, int ps, int ss);
227  void invalidateRCache (word start, unsigned size, int ps, int ss);
228  void invalidateWCache (word start, unsigned size, int ps, int ss);
229 
231  void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
232  void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
233  void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
234 
239  [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
240  [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
241  byte readSlottedMem(unsigned address, EmuTime::param time);
242  void writeSlottedMem(unsigned address, byte value,
243  EmuTime::param time);
244 
245  void setExpanded(int ps);
246  void unsetExpanded(int ps);
247  void testUnsetExpanded(int ps,
248  span<const std::unique_ptr<MSXDevice>> allowed) const;
249  [[nodiscard]] inline bool isExpanded(int ps) const { return expanded[ps] != 0; }
250  void changeExpanded(bool newExpanded);
251 
252  [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
253 
254  static void insertBreakPoint(BreakPoint bp);
255  static void removeBreakPoint(const BreakPoint& bp);
256  using BreakPoints = std::vector<BreakPoint>;
257  [[nodiscard]] static const BreakPoints& getBreakPoints() { return breakPoints; }
258 
259  void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
260  void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
261  // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
262  using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
263  [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
264 
265  static void setCondition(DebugCondition cond);
266  static void removeCondition(const DebugCondition& cond);
267  using Conditions = std::vector<DebugCondition>;
268  [[nodiscard]] static const Conditions& getConditions() { return conditions; }
269 
270  [[nodiscard]] static bool isBreaked() { return breaked; }
271  void doBreak();
272  void doStep();
273  void doContinue();
274 
275  // breakpoint methods used by CPUCore
276  [[nodiscard]] static bool anyBreakPoints()
277  {
278  return !breakPoints.empty() || !conditions.empty();
279  }
280  [[nodiscard]] static bool checkBreakPoints(unsigned pc, MSXMotherBoard& motherBoard)
281  {
282  auto range = ranges::equal_range(breakPoints, pc, CompareBreakpoints());
283  if (conditions.empty() && (range.first == range.second)) {
284  return false;
285  }
286 
287  // slow path non-inlined
288  checkBreakPoints(range, motherBoard);
289  return isBreaked();
290  }
291 
292  // cleanup global variables
293  static void cleanup();
294 
295  // In fast-forward mode, breakpoints, watchpoints and conditions should
296  // not trigger.
297  void setFastForward(bool fastForward_) { fastForward = fastForward_; }
298  [[nodiscard]] bool isFastForward() const { return fastForward; }
299 
300  template<typename Archive>
301  void serialize(Archive& ar, unsigned version);
302 
303 private:
304  byte readMemSlow(word address, EmuTime::param time);
305  void writeMemSlow(word address, byte value, EmuTime::param time);
306 
307  MSXDevice*& getDevicePtr(byte port, bool isIn);
308 
309  void register_IO (int port, bool isIn,
310  MSXDevice*& devicePtr, MSXDevice* device);
311  void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
312  void testRegisterSlot(MSXDevice& device,
313  int ps, int ss, int base, int size);
314  void registerSlot(MSXDevice& device,
315  int ps, int ss, int base, int size);
316  void unregisterSlot(MSXDevice& device,
317  int ps, int ss, int base, int size);
318 
319 
320  static void checkBreakPoints(std::pair<BreakPoints::const_iterator,
321  BreakPoints::const_iterator> range,
322  MSXMotherBoard& motherBoard);
323  static void removeBreakPoint(unsigned id);
324  static void removeCondition(unsigned id);
325 
326  void removeAllWatchPoints();
327  void updateMemWatch(WatchPoint::Type type);
328  void executeMemWatch(WatchPoint::Type type, unsigned address,
329  unsigned value = ~0u);
330 
331  struct MemoryDebug final : SimpleDebuggable {
332  explicit MemoryDebug(MSXMotherBoard& motherBoard);
333  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
334  void write(unsigned address, byte value, EmuTime::param time) override;
335  } memoryDebug;
336 
337  struct SlottedMemoryDebug final : SimpleDebuggable {
338  explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
339  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
340  void write(unsigned address, byte value, EmuTime::param time) override;
341  } slottedMemoryDebug;
342 
343  struct IODebug final : SimpleDebuggable {
344  explicit IODebug(MSXMotherBoard& motherBoard);
345  [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
346  void write(unsigned address, byte value, EmuTime::param time) override;
347  } ioDebug;
348 
349  struct SlotInfo final : InfoTopic {
350  explicit SlotInfo(InfoCommand& machineInfoCommand);
351  void execute(span<const TclObject> tokens,
352  TclObject& result) const override;
353  [[nodiscard]] std::string help(const std::vector<std::string>& tokens) const override;
354  } slotInfo;
355 
356  struct SubSlottedInfo final : InfoTopic {
357  explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
358  void execute(span<const TclObject> tokens,
359  TclObject& result) const override;
360  [[nodiscard]] std::string help(const std::vector<std::string>& tokens) const override;
361  } subSlottedInfo;
362 
363  struct ExternalSlotInfo final : InfoTopic {
364  explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
365  void execute(span<const TclObject> tokens,
366  TclObject& result) const override;
367  [[nodiscard]] std::string help(const std::vector<std::string>& tokens) const override;
368  } externalSlotInfo;
369 
370  struct IOInfo : InfoTopic {
371  IOInfo(InfoCommand& machineInfoCommand, const char* name);
372  void helper(span<const TclObject> tokens,
373  TclObject& result, MSXDevice** devices) const;
374  [[nodiscard]] std::string help(const std::vector<std::string>& tokens) const override;
375  protected:
376  ~IOInfo() = default;
377  };
378  struct IInfo final : IOInfo {
379  explicit IInfo(InfoCommand& machineInfoCommand)
380  : IOInfo(machineInfoCommand, "input_port") {}
381  void execute(span<const TclObject> tokens,
382  TclObject& result) const override;
383  } inputPortInfo;
384  struct OInfo final : IOInfo {
385  explicit OInfo(InfoCommand& machineInfoCommand)
386  : IOInfo(machineInfoCommand, "output_port") {}
387  void execute(span<const TclObject> tokens,
388  TclObject& result) const override;
389  } outputPortInfo;
390 
397  void updateVisible(int page);
398  inline void updateVisible(int page, int ps, int ss);
399  void setSubSlot(byte primSlot, byte value);
400 
401  std::unique_ptr<DummyDevice> dummyDevice;
402  MSXCPU& msxcpu;
403  CliComm& cliComm;
404  MSXMotherBoard& motherBoard;
405 
406  std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
407 
408  byte disallowReadCache [CacheLine::NUM];
409  byte disallowWriteCache[CacheLine::NUM];
410  std::bitset<CacheLine::SIZE> readWatchSet [CacheLine::NUM];
411  std::bitset<CacheLine::SIZE> writeWatchSet[CacheLine::NUM];
412 
413  struct GlobalRwInfo {
414  MSXDevice* device;
415  word addr;
416  [[nodiscard]] bool operator==(const GlobalRwInfo& rhs) const {
417  return (device == rhs.device) &&
418  (addr == rhs.addr);
419  }
420  };
421  std::vector<GlobalRwInfo> globalReads;
422  std::vector<GlobalRwInfo> globalWrites;
423 
424  MSXDevice* IO_In [256];
425  MSXDevice* IO_Out[256];
426  MSXDevice* slotLayout[4][4][4];
427  MSXDevice* visibleDevices[4];
428  byte subSlotRegister[4];
429  byte primarySlotState[4];
430  byte secondarySlotState[4];
431  byte initialPrimarySlots;
432  unsigned expanded[4];
433 
434  bool fastForward; // no need to serialize
435 
436  // All CPUs (Z80 and R800) of all MSX machines share this state.
437  static inline BreakPoints breakPoints; // sorted on address
438  WatchPoints watchPoints; // ordered in creation order, TODO must also be static
439  static inline Conditions conditions; // ordered in creation order
440  static inline bool breaked = false;
441 };
442 
443 
444 // Compile-Time Interval (half-open).
445 // TODO possibly move this to utils/
446 template<unsigned BEGIN, unsigned END = BEGIN + 1>
448 {
449  [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
450  [[nodiscard]] unsigned end() const { return END; } // exclusive
451 };
452 
453 // Execute an 'action' for every element in the given interval(s).
454 template<typename ACTION, typename CT_INTERVAL>
455 inline void foreach_ct_interval(ACTION action, CT_INTERVAL interval)
456 {
457  for (auto i = interval.begin(); i != interval.end(); ++i) {
458  action(i);
459  }
460 }
461 template<typename ACTION, typename CT_INTERVAL, typename... CT_INTERVALS>
462 inline void foreach_ct_interval(ACTION action, CT_INTERVAL front, CT_INTERVALS... tail)
463 {
464  foreach_ct_interval(action, front);
465  foreach_ct_interval(action, tail...);
466 }
467 
468 
469 template<typename MSXDEVICE, typename... CT_INTERVALS>
471 {
472  template<typename ACTION>
473  void execute(ACTION action)
474  {
475  auto& dev = static_cast<MSXDEVICE&>(*this);
476  auto& cpu = dev.getCPUInterface();
478  [&](unsigned addr) { action(cpu, dev, addr); },
479  CT_INTERVALS()...);
480  }
481 };
482 
483 template<typename MSXDEVICE, typename... CT_INTERVALS>
484 struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
485 {
487  {
488  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
489  cpu.registerGlobalWrite(dev, addr);
490  });
491  }
492 
494  {
495  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
496  cpu.unregisterGlobalWrite(dev, addr);
497  });
498  }
499 };
500 
501 template<typename MSXDEVICE, typename... CT_INTERVALS>
502 struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
503 {
505  {
506  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
507  cpu.registerGlobalRead(dev, addr);
508  });
509  }
510 
512  {
513  this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
514  cpu.unregisterGlobalRead(dev, addr);
515  });
516  }
517 };
518 
519 } // namespace openmsx
520 
521 #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: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:431
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:414
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:425
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:436
virtual byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: MSXDevice.cc:465
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:408
byte read(unsigned address) override
void write(unsigned address, byte value) override
Definition: span.hh:126
#define unlikely(x)
Definition: likely.hh:15
constexpr bool operator==(PoolIndex i, PoolIndex j)
Definition: hash_set.hh:34
constexpr unsigned NUM
Definition: CacheLine.hh:8
constexpr unsigned BITS
Definition: CacheLine.hh:6
This file implemented 3 utility functions:
Definition: Autofire.cc:5
@ 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
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:124
auto equal_range(ForwardRange &&range, const T &value)
Definition: ranges.hh:95
size_t size(std::string_view utf8)
unsigned end() const
unsigned begin() const
bool operator()(const BreakPoint &x, word y) const
bool operator()(const BreakPoint &x, const BreakPoint &y) const
bool operator()(word x, const BreakPoint &y) const
void execute(ACTION action)