openMSX
MSXCPUInterface.hh
Go to the documentation of this file.
1#ifndef MSXCPUINTERFACE_HH
2#define MSXCPUINTERFACE_HH
3
4#include "BreakPoint.hh"
5#include "CacheLine.hh"
6#include "DebugCondition.hh"
7#include "WatchPoint.hh"
8
9#include "SimpleDebuggable.hh"
10#include "InfoTopic.hh"
11#include "MSXDevice.hh"
12#include "ProfileCounters.hh"
13#include "openmsx.hh"
14
15#include "narrow.hh"
16#include "ranges.hh"
17
18#include <array>
19#include <bitset>
20#include <concepts>
21#include <memory>
22#include <vector>
23
24namespace openmsx {
25
26class BooleanSetting;
27class BreakPoint;
28class CliComm;
29class DummyDevice;
30class MSXCPU;
31class MSXMotherBoard;
32class VDPIODelay;
33
34inline constexpr bool PROFILE_CACHELINES = false;
53std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>);
54std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn);
55
56class MSXCPUInterface : public ProfileCounters<PROFILE_CACHELINES, CacheLineCounters>
57{
58public:
59 explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
65
71 void register_IO_In(byte port, MSXDevice* device);
72 void unregister_IO_In(byte port, MSXDevice* device);
73
79 void register_IO_Out(byte port, MSXDevice* device);
80 void unregister_IO_Out(byte port, MSXDevice* device);
81
97 bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
98 bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
99
106 void registerMemDevice(MSXDevice& device,
107 int ps, int ss, unsigned base, unsigned size);
108 void unregisterMemDevice(MSXDevice& device,
109 int ps, int ss, unsigned base, unsigned size);
110
114 void registerGlobalWrite(MSXDevice& device, word address);
115 void unregisterGlobalWrite(MSXDevice& device, word address);
116
120 void registerGlobalRead(MSXDevice& device, word address);
121 void unregisterGlobalRead(MSXDevice& device, word address);
122
126 void reset();
127
131 byte readMem(word address, EmuTime::param time) {
133 if (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
134 return readMemSlow(address, time);
135 }
136 return visibleDevices[address >> 14]->readMem(address, time);
137 }
138
142 void writeMem(word address, byte value, EmuTime::param time) {
144 if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
145 writeMemSlow(address, value, time);
146 return;
147 }
148 visibleDevices[address>>14]->writeMem(address, value, time);
149 }
150
155 byte readIO(word port, EmuTime::param time) {
156 return IO_In[port & 0xFF]->readIO(port, time);
157 }
158
163 void writeIO(word port, byte value, EmuTime::param time) {
164 IO_Out[port & 0xFF]->writeIO(port, value, time);
165 }
166
179 [[nodiscard]] const byte* getReadCacheLine(word start) const {
181 if (disallowReadCache[start >> CacheLine::BITS]) [[unlikely]] {
182 return nullptr;
183 }
184 return visibleDevices[start >> 14]->getReadCacheLine(start);
185 }
186
199 [[nodiscard]] byte* getWriteCacheLine(word start) const {
201 if (disallowWriteCache[start >> CacheLine::BITS]) [[unlikely]] {
202 return nullptr;
203 }
204 return visibleDevices[start >> 14]->getWriteCacheLine(start);
205 }
206
211 [[nodiscard]] byte readIRQVector() const;
212
213 /*
214 * Should only be used by PPI
215 * TODO: make private / friend
216 */
217 void setPrimarySlots(byte value);
218
220 void invalidateRWCache(word start, unsigned size, int ps, int ss);
221 void invalidateRCache (word start, unsigned size, int ps, int ss);
222 void invalidateWCache (word start, unsigned size, int ps, int ss);
223
225 void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
226 void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
227 void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
228
233 [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
234 [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
235 byte readSlottedMem(unsigned address, EmuTime::param time);
236 void writeSlottedMem(unsigned address, byte value,
237 EmuTime::param time);
238
239 void setExpanded(int ps);
240 void unsetExpanded(int ps);
241 void testUnsetExpanded(int ps,
242 std::span<const std::unique_ptr<MSXDevice>> allowed) const;
243 [[nodiscard]] bool isExpanded(int ps) const { return expanded[ps] != 0; }
244 void changeExpanded(bool newExpanded);
245
246 [[nodiscard]] auto getPrimarySlot (int page) const { return primarySlotState[page]; }
247 [[nodiscard]] auto getSecondarySlot(int page) const { return secondarySlotState[page]; }
248
249 [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
250
252 void removeBreakPoint(const BreakPoint& bp);
253 void removeBreakPoint(unsigned id);
254 using BreakPoints = std::vector<BreakPoint>;
255 [[nodiscard]] static const BreakPoints& getBreakPoints() { return breakPoints; }
256
257 void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
258 void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
259 void removeWatchPoint(unsigned id);
260 // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
261 using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
262 [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
263
264 void setCondition(DebugCondition cond);
265 void removeCondition(const DebugCondition& cond);
266 void removeCondition(unsigned id);
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]] bool checkBreakPoints(unsigned pc)
281 {
282 auto range = ranges::equal_range(breakPoints, pc, {}, &BreakPoint::getAddress);
283 if (conditions.empty() && (range.first == range.second)) {
284 return false;
285 }
286
287 // slow path non-inlined
288 checkBreakPoints(range);
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 [[nodiscard]] MSXDevice* getMSXDevice(int ps, int ss, int page);
301 [[nodiscard]] MSXDevice* getVisibleMSXDevice(int page) { return visibleDevices[page]; }
302
303 template<typename Archive>
304 void serialize(Archive& ar, unsigned version);
305
306private:
307 byte readMemSlow(word address, EmuTime::param time);
308 void writeMemSlow(word address, byte value, EmuTime::param time);
309
310 MSXDevice*& getDevicePtr(byte port, bool isIn);
311
312 void register_IO (int port, bool isIn,
313 MSXDevice*& devicePtr, MSXDevice* device);
314 void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
315 void testRegisterSlot(const MSXDevice& device,
316 int ps, int ss, unsigned base, unsigned size);
317 void registerSlot(MSXDevice& device,
318 int ps, int ss, unsigned base, unsigned size);
319 void unregisterSlot(MSXDevice& device,
320 int ps, int ss, unsigned base, unsigned size);
321
322
323 void checkBreakPoints(std::pair<BreakPoints::const_iterator,
324 BreakPoints::const_iterator> range);
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(std::span<const TclObject> tokens,
352 TclObject& result) const override;
353 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
354 } slotInfo;
355
356 struct SubSlottedInfo final : InfoTopic {
357 explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
358 void execute(std::span<const TclObject> tokens,
359 TclObject& result) const override;
360 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
361 } subSlottedInfo;
362
363 struct ExternalSlotInfo final : InfoTopic {
364 explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
365 void execute(std::span<const TclObject> tokens,
366 TclObject& result) const override;
367 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
368 } externalSlotInfo;
369
370 struct IOInfo : InfoTopic {
371 IOInfo(InfoCommand& machineInfoCommand, const char* name);
372 void helper(std::span<const TclObject> tokens,
373 TclObject& result, std::span<MSXDevice*, 256> devices) const;
374 [[nodiscard]] std::string help(std::span<const TclObject> 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(std::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(std::span<const TclObject> tokens,
388 TclObject& result) const override;
389 } outputPortInfo;
390
397 void updateVisible(byte page);
398 inline void updateVisible(byte page, byte ps, byte 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 BooleanSetting& pauseSetting;
406
407 std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
408
409 std::array<byte, CacheLine::NUM> disallowReadCache;
410 std::array<byte, CacheLine::NUM> disallowWriteCache;
411 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> readWatchSet;
412 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> writeWatchSet;
413
414 struct GlobalRwInfo {
415 MSXDevice* device;
416 word addr;
417 [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
418 };
419 std::vector<GlobalRwInfo> globalReads;
420 std::vector<GlobalRwInfo> globalWrites;
421
422 std::array<MSXDevice*, 256> IO_In;
423 std::array<MSXDevice*, 256> IO_Out;
424 std::array<std::array<std::array<MSXDevice*, 4>, 4>, 4> slotLayout;
425 std::array<MSXDevice*, 4> visibleDevices;
426 std::array<byte, 4> subSlotRegister;
427 std::array<byte, 4> primarySlotState;
428 std::array<byte, 4> secondarySlotState;
429 byte initialPrimarySlots;
430 std::array<unsigned, 4> expanded;
431
432 bool fastForward = false; // no need to serialize
433
434 // All CPUs (Z80 and R800) of all MSX machines share this state.
435 static inline BreakPoints breakPoints; // sorted on address
436 WatchPoints watchPoints; // ordered in creation order, TODO must also be static
437 static inline Conditions conditions; // ordered in creation order
438 static inline bool breaked = false;
439};
440
441
442// Compile-Time Interval (half-open).
443// TODO possibly move this to utils/
444template<unsigned BEGIN, unsigned END = BEGIN + 1>
446{
447 [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
448 [[nodiscard]] unsigned end() const { return END; } // exclusive
449};
450
451// Execute an 'action' for every element in the given interval(s).
452inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
453{
454 for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
455 action(i);
456 }
457}
458inline void foreach_ct_interval(std::invocable<unsigned> auto action,
459 auto front_interval, auto... tail_intervals)
460{
461 foreach_ct_interval(action, front_interval);
462 foreach_ct_interval(action, tail_intervals...);
463}
464
465
466template<typename MSXDEVICE, typename... CT_INTERVALS>
468{
469 void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
470 {
471 auto& dev = static_cast<MSXDEVICE&>(*this);
472 auto& cpu = dev.getCPUInterface();
474 [&](unsigned addr) { action(cpu, dev, addr); },
475 CT_INTERVALS()...);
476 }
477};
478
479template<typename MSXDEVICE, typename... CT_INTERVALS>
480struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
481{
486
488 {
489 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
490 cpu.registerGlobalWrite(dev, narrow<word>(addr));
491 });
492 }
493
495 {
496 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
497 cpu.unregisterGlobalWrite(dev, narrow<word>(addr));
498 });
499 }
500};
501
502template<typename MSXDEVICE, typename... CT_INTERVALS>
503struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
504{
509
511 {
512 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
513 cpu.registerGlobalRead(dev, narrow<word>(addr));
514 });
515 }
516
518 {
519 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
520 cpu.unregisterGlobalRead(dev, narrow<word>(addr));
521 });
522 }
523};
524
525} // namespace openmsx
526
527#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 specific 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)
std::vector< DebugCondition > Conditions
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
MSXDevice * getMSXDevice(int ps, int ss, int page)
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss)
auto getPrimarySlot(int page) 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 unregisterMemDevice(MSXDevice &device, int ps, int ss, unsigned base, unsigned size)
void removeCondition(const DebugCondition &cond)
byte peekMem(word address, EmuTime::param time) const
Peek memory location.
byte readIRQVector() const
CPU uses this method to read 'extra' data from the data bus used in interrupt routines.
byte readIO(word port, EmuTime::param time)
This read a byte from the given IO-port.
void testUnsetExpanded(int ps, std::span< const std::unique_ptr< MSXDevice > > allowed) const
MSXCPUInterface & operator=(MSXCPUInterface &&)=delete
static const Conditions & getConditions()
void writeSlottedMem(unsigned address, byte value, EmuTime::param time)
void setPrimarySlots(byte value)
byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
void invalidateRCache(word start, unsigned size, int ps, int ss)
void unregisterGlobalWrite(MSXDevice &device, word address)
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)
void reset()
Reset (the slot state)
void setCondition(DebugCondition cond)
void invalidateWCache(word start, unsigned size, int ps, int ss)
const WatchPoints & getWatchPoints() const
bool checkBreakPoints(unsigned pc)
MSXCPUInterface & operator=(const MSXCPUInterface &)=delete
void serialize(Archive &ar, unsigned version)
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 registerMemDevice(MSXDevice &device, int ps, int ss, unsigned base, unsigned size)
Devices can register themself in the MSX slot structure.
static const BreakPoints & getBreakPoints()
void removeBreakPoint(const BreakPoint &bp)
void unregister_IO_Out(byte port, MSXDevice *device)
void registerGlobalWrite(MSXDevice &device, word address)
(Un)register global writes.
MSXCPUInterface(MSXCPUInterface &&)=delete
std::vector< BreakPoint > BreakPoints
MSXDevice * getVisibleMSXDevice(int page)
bool replace_IO_In(byte port, MSXDevice *oldDevice, MSXDevice *newDevice)
These methods replace a previously registered device with a new one.
void setFastForward(bool fastForward_)
const byte * getReadCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
MSXCPUInterface(const MSXCPUInterface &)=delete
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss)
bool isExpanded(int ps) const
auto getSecondarySlot(int page) const
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
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:11
bool operator==(const BooleanInput &x, const BooleanInput &y)
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:133
unsigned begin() const
void execute(std::invocable< MSXCPUInterface &, MSXDevice &, unsigned > auto action)
GlobalReadClient & operator=(GlobalReadClient &&)=delete
GlobalReadClient(const GlobalReadClient &)=delete
GlobalReadClient & operator=(const GlobalReadClient &)=delete
GlobalReadClient(GlobalReadClient &&)=delete
GlobalWriteClient(const GlobalWriteClient &)=delete
GlobalWriteClient(GlobalWriteClient &&)=delete
GlobalWriteClient & operator=(GlobalWriteClient &&)=delete
GlobalWriteClient & operator=(const GlobalWriteClient &)=delete