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
85 void register_IO_InOut(byte port, MSXDevice* device);
86 void register_IO_In_range(byte port, unsigned num, MSXDevice* device);
87 void register_IO_Out_range(byte port, unsigned num, MSXDevice* device);
88 void register_IO_InOut_range(byte port, unsigned num, MSXDevice* device);
89 void unregister_IO_InOut(byte port, MSXDevice* device);
90 void unregister_IO_In_range(byte port, unsigned num, MSXDevice* device);
91 void unregister_IO_Out_range(byte port, unsigned num, MSXDevice* device);
92 void unregister_IO_InOut_range(byte port, unsigned num, MSXDevice* device);
93
109 bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
110 bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
111
118 void registerMemDevice(MSXDevice& device,
119 int ps, int ss, unsigned base, unsigned size);
120 void unregisterMemDevice(MSXDevice& device,
121 int ps, int ss, unsigned base, unsigned size);
122
126 void registerGlobalWrite(MSXDevice& device, word address);
127 void unregisterGlobalWrite(MSXDevice& device, word address);
128
132 void registerGlobalRead(MSXDevice& device, word address);
133 void unregisterGlobalRead(MSXDevice& device, word address);
134
138 void reset();
139
143 byte readMem(word address, EmuTime::param time) {
145 if (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
146 return readMemSlow(address, time);
147 }
148 return visibleDevices[address >> 14]->readMem(address, time);
149 }
150
154 void writeMem(word address, byte value, EmuTime::param time) {
156 if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
157 writeMemSlow(address, value, time);
158 return;
159 }
160 visibleDevices[address>>14]->writeMem(address, value, time);
161 }
162
167 byte readIO(word port, EmuTime::param time) {
168 return IO_In[port & 0xFF]->readIO(port, time);
169 }
170
175 void writeIO(word port, byte value, EmuTime::param time) {
176 IO_Out[port & 0xFF]->writeIO(port, value, time);
177 }
178
191 [[nodiscard]] const byte* getReadCacheLine(word start) const {
193 if (disallowReadCache[start >> CacheLine::BITS]) [[unlikely]] {
194 return nullptr;
195 }
196 return visibleDevices[start >> 14]->getReadCacheLine(start);
197 }
198
211 [[nodiscard]] byte* getWriteCacheLine(word start) {
213 if (disallowWriteCache[start >> CacheLine::BITS]) [[unlikely]] {
214 return nullptr;
215 }
216 return visibleDevices[start >> 14]->getWriteCacheLine(start);
217 }
218
223 [[nodiscard]] byte readIRQVector() const;
224
225 /*
226 * Should only be used by PPI
227 * TODO: make private / friend
228 */
229 void setPrimarySlots(byte value);
230
232 void invalidateRWCache(word start, unsigned size, int ps, int ss);
233 void invalidateRCache (word start, unsigned size, int ps, int ss);
234 void invalidateWCache (word start, unsigned size, int ps, int ss);
235
237 void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
238 void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
239 void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
240
245 [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
246 [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
247 byte readSlottedMem(unsigned address, EmuTime::param time);
248 void writeSlottedMem(unsigned address, byte value,
249 EmuTime::param time);
250
251 void setExpanded(int ps);
252 void unsetExpanded(int ps);
253 void testUnsetExpanded(int ps,
254 std::span<const std::unique_ptr<MSXDevice>> allowed) const;
255 [[nodiscard]] bool isExpanded(int ps) const { return expanded[ps] != 0; }
256 void changeExpanded(bool newExpanded);
257
258 [[nodiscard]] auto getPrimarySlot (int page) const { return primarySlotState[page]; }
259 [[nodiscard]] auto getSecondarySlot(int page) const { return secondarySlotState[page]; }
260
261 [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
262
264 void removeBreakPoint(const BreakPoint& bp);
265 void removeBreakPoint(unsigned id);
266 using BreakPoints = std::vector<BreakPoint>;
267 [[nodiscard]] static BreakPoints& getBreakPoints() { return breakPoints; }
268
269 void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
270 void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
271 void removeWatchPoint(unsigned id);
272 // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
273 using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
274 [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
275 [[nodiscard]] WatchPoints& getWatchPoints() { return watchPoints; }
276
277 // Temporarily unregister and then re-register a watchpoint. E.g.
278 // because you want to change the type or address, and then it needs to
279 // be registered in a different way.
281 ScopedChangeWatchpoint(MSXCPUInterface& interface_, std::shared_ptr<WatchPoint> wp_)
282 : interface(interface_), wp(std::move(wp_)) {
283 interface.unregisterWatchPoint(*wp);
284 }
286 interface.registerWatchPoint(*wp);
287 }
288 private:
289 MSXCPUInterface& interface;
290 std::shared_ptr<WatchPoint> wp;
291 };
292 [[nodiscard]] auto getScopedChangeWatchpoint(std::shared_ptr<WatchPoint> wp) {
293 return ScopedChangeWatchpoint(*this, std::move(wp));
294 }
295
296 void setCondition(DebugCondition cond);
297 void removeCondition(const DebugCondition& cond);
298 void removeCondition(unsigned id);
299 using Conditions = std::vector<DebugCondition>;
300 [[nodiscard]] static Conditions& getConditions() { return conditions; }
301
302 [[nodiscard]] static bool isBreaked() { return breaked; }
303 void doBreak();
304 void doStep();
305 void doContinue();
306
307 // breakpoint methods used by CPUCore
308 [[nodiscard]] static bool anyBreakPoints()
309 {
310 return !breakPoints.empty() || !conditions.empty();
311 }
312 [[nodiscard]] bool checkBreakPoints(unsigned pc);
313
314 // cleanup global variables
315 static void cleanup();
316
317 // In fast-forward mode, breakpoints, watchpoints and conditions should
318 // not trigger.
319 void setFastForward(bool fastForward_) { fastForward = fastForward_; }
320 [[nodiscard]] bool isFastForward() const { return fastForward; }
321
322 [[nodiscard]] MSXDevice* getMSXDevice(int ps, int ss, int page);
323 [[nodiscard]] MSXDevice* getVisibleMSXDevice(int page) { return visibleDevices[page]; }
324
325 template<typename Archive>
326 void serialize(Archive& ar, unsigned version);
327
328private:
329 byte readMemSlow(word address, EmuTime::param time);
330 void writeMemSlow(word address, byte value, EmuTime::param time);
331
332 MSXDevice*& getDevicePtr(byte port, bool isIn);
333
334 void register_IO (int port, bool isIn,
335 MSXDevice*& devicePtr, MSXDevice* device);
336 void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
337 void testRegisterSlot(const MSXDevice& device,
338 int ps, int ss, unsigned base, unsigned size);
339 void registerSlot(MSXDevice& device,
340 int ps, int ss, unsigned base, unsigned size);
341 void unregisterSlot(MSXDevice& device,
342 int ps, int ss, unsigned base, unsigned size);
343
344 void registerWatchPoint(WatchPoint& wp);
345 void unregisterWatchPoint(WatchPoint& wp);
346
347 void removeAllWatchPoints();
348 void updateMemWatch(WatchPoint::Type type);
349 void executeMemWatch(WatchPoint::Type type, unsigned address,
350 unsigned value = ~0u);
351
352 struct MemoryDebug final : SimpleDebuggable {
353 explicit MemoryDebug(MSXMotherBoard& motherBoard);
354 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
355 void write(unsigned address, byte value, EmuTime::param time) override;
356 } memoryDebug;
357
358 struct SlottedMemoryDebug final : SimpleDebuggable {
359 explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
360 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
361 void write(unsigned address, byte value, EmuTime::param time) override;
362 } slottedMemoryDebug;
363
364 struct IODebug final : SimpleDebuggable {
365 explicit IODebug(MSXMotherBoard& motherBoard);
366 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
367 void write(unsigned address, byte value, EmuTime::param time) override;
368 } ioDebug;
369
370 struct SlotInfo final : InfoTopic {
371 explicit SlotInfo(InfoCommand& machineInfoCommand);
372 void execute(std::span<const TclObject> tokens,
373 TclObject& result) const override;
374 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
375 } slotInfo;
376
377 struct SubSlottedInfo final : InfoTopic {
378 explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
379 void execute(std::span<const TclObject> tokens,
380 TclObject& result) const override;
381 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
382 } subSlottedInfo;
383
384 struct ExternalSlotInfo final : InfoTopic {
385 explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
386 void execute(std::span<const TclObject> tokens,
387 TclObject& result) const override;
388 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
389 } externalSlotInfo;
390
391 struct IOInfo : InfoTopic {
392 IOInfo(InfoCommand& machineInfoCommand, const char* name);
393 void helper(std::span<const TclObject> tokens,
394 TclObject& result, std::span<MSXDevice*, 256> devices) const;
395 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
396 protected:
397 ~IOInfo() = default;
398 };
399 struct IInfo final : IOInfo {
400 explicit IInfo(InfoCommand& machineInfoCommand)
401 : IOInfo(machineInfoCommand, "input_port") {}
402 void execute(std::span<const TclObject> tokens,
403 TclObject& result) const override;
404 } inputPortInfo;
405 struct OInfo final : IOInfo {
406 explicit OInfo(InfoCommand& machineInfoCommand)
407 : IOInfo(machineInfoCommand, "output_port") {}
408 void execute(std::span<const TclObject> tokens,
409 TclObject& result) const override;
410 } outputPortInfo;
411
418 void updateVisible(byte page);
419 inline void updateVisible(byte page, byte ps, byte ss);
420 void setSubSlot(byte primSlot, byte value);
421
422 std::unique_ptr<DummyDevice> dummyDevice;
423 MSXCPU& msxcpu;
424 CliComm& cliComm;
425 MSXMotherBoard& motherBoard;
426 BooleanSetting& pauseSetting;
427
428 std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
429
430 std::array<byte, CacheLine::NUM> disallowReadCache;
431 std::array<byte, CacheLine::NUM> disallowWriteCache;
432 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> readWatchSet;
433 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> writeWatchSet;
434
435 struct GlobalRwInfo {
436 MSXDevice* device;
437 word addr;
438 [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
439 };
440 std::vector<GlobalRwInfo> globalReads;
441 std::vector<GlobalRwInfo> globalWrites;
442
443 std::array<MSXDevice*, 256> IO_In;
444 std::array<MSXDevice*, 256> IO_Out;
445 std::array<std::array<std::array<MSXDevice*, 4>, 4>, 4> slotLayout;
446 std::array<MSXDevice*, 4> visibleDevices;
447 std::array<byte, 4> subSlotRegister;
448 std::array<byte, 4> primarySlotState;
449 std::array<byte, 4> secondarySlotState;
450 byte initialPrimarySlots;
451 std::array<unsigned, 4> expanded;
452
453 bool fastForward = false; // no need to serialize
454
455 // All CPUs (Z80 and R800) of all MSX machines share this state.
456 static inline BreakPoints breakPoints; // unsorted
457 WatchPoints watchPoints; // ordered in creation order, TODO must also be static
458 static inline Conditions conditions; // ordered in creation order
459 static inline bool breaked = false;
460};
461
462
463// Compile-Time Interval (half-open).
464// TODO possibly move this to utils/
465template<unsigned BEGIN, unsigned END = BEGIN + 1>
467{
468 [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
469 [[nodiscard]] unsigned end() const { return END; } // exclusive
470};
471
472// Execute an 'action' for every element in the given interval(s).
473inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
474{
475 for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
476 action(i);
477 }
478}
479inline void foreach_ct_interval(std::invocable<unsigned> auto action,
480 auto front_interval, auto... tail_intervals)
481{
482 foreach_ct_interval(action, front_interval);
483 foreach_ct_interval(action, tail_intervals...);
484}
485
486
487template<typename MSXDEVICE, typename... CT_INTERVALS>
489{
490 void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
491 {
492 auto& dev = static_cast<MSXDEVICE&>(*this);
493 auto& cpu = dev.getCPUInterface();
495 [&](unsigned addr) { action(cpu, dev, addr); },
496 CT_INTERVALS()...);
497 }
498};
499
500template<typename MSXDEVICE, typename... CT_INTERVALS>
501struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
502{
507
509 {
510 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
511 cpu.registerGlobalWrite(dev, narrow<word>(addr));
512 });
513 }
514
516 {
517 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
518 cpu.unregisterGlobalWrite(dev, narrow<word>(addr));
519 });
520 }
521};
522
523template<typename MSXDEVICE, typename... CT_INTERVALS>
524struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
525{
530
532 {
533 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
534 cpu.registerGlobalRead(dev, narrow<word>(addr));
535 });
536 }
537
539 {
540 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
541 cpu.unregisterGlobalRead(dev, narrow<word>(addr));
542 });
543 }
544};
545
546} // namespace openmsx
547
548#endif
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 unregister_IO_In_range(byte port, unsigned num, MSXDevice *device)
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
void invalidateRWCache(word start, unsigned size, int ps, int ss)
void insertBreakPoint(BreakPoint bp)
void unregister_IO_Out_range(byte port, unsigned num, MSXDevice *device)
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)
auto getScopedChangeWatchpoint(std::shared_ptr< WatchPoint > wp)
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)
byte * getWriteCacheLine(word start)
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
void unregister_IO_InOut_range(byte port, unsigned num, MSXDevice *device)
void unregisterMemDevice(MSXDevice &device, int ps, int ss, unsigned base, unsigned size)
void register_IO_In_range(byte port, unsigned num, MSXDevice *device)
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.
static BreakPoints & getBreakPoints()
byte readIO(word port, EmuTime::param time)
This read a byte from the given IO-port.
void unregister_IO_InOut(byte port, MSXDevice *device)
void testUnsetExpanded(int ps, std::span< const std::unique_ptr< MSXDevice > > allowed) const
MSXCPUInterface & operator=(MSXCPUInterface &&)=delete
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)
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
void register_IO_InOut(byte port, MSXDevice *device)
Convenience methods for {un}register_IO_{In,Out}.
bool checkBreakPoints(unsigned pc)
MSXCPUInterface & operator=(const MSXCPUInterface &)=delete
void serialize(Archive &ar, unsigned version)
void register_IO_Out_range(byte port, unsigned num, MSXDevice *device)
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.
void removeBreakPoint(const BreakPoint &bp)
static Conditions & getConditions()
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
void register_IO_InOut_range(byte port, unsigned num, MSXDevice *device)
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)
STL namespace.
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
ScopedChangeWatchpoint(MSXCPUInterface &interface_, std::shared_ptr< WatchPoint > wp_)