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 const 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
276 void setCondition(DebugCondition cond);
277 void removeCondition(const DebugCondition& cond);
278 void removeCondition(unsigned id);
279 using Conditions = std::vector<DebugCondition>;
280 [[nodiscard]] static const Conditions& getConditions() { return conditions; }
281
282 [[nodiscard]] static bool isBreaked() { return breaked; }
283 void doBreak();
284 void doStep();
285 void doContinue();
286
287 // breakpoint methods used by CPUCore
288 [[nodiscard]] static bool anyBreakPoints()
289 {
290 return !breakPoints.empty() || !conditions.empty();
291 }
292 [[nodiscard]] bool checkBreakPoints(unsigned pc)
293 {
294 auto range = ranges::equal_range(breakPoints, pc, {}, &BreakPoint::getAddress);
295 if (conditions.empty() && (range.first == range.second)) {
296 return false;
297 }
298
299 // slow path non-inlined
300 checkBreakPoints(range);
301 return isBreaked();
302 }
303
304 // cleanup global variables
305 static void cleanup();
306
307 // In fast-forward mode, breakpoints, watchpoints and conditions should
308 // not trigger.
309 void setFastForward(bool fastForward_) { fastForward = fastForward_; }
310 [[nodiscard]] bool isFastForward() const { return fastForward; }
311
312 [[nodiscard]] MSXDevice* getMSXDevice(int ps, int ss, int page);
313 [[nodiscard]] MSXDevice* getVisibleMSXDevice(int page) { return visibleDevices[page]; }
314
315 template<typename Archive>
316 void serialize(Archive& ar, unsigned version);
317
318private:
319 byte readMemSlow(word address, EmuTime::param time);
320 void writeMemSlow(word address, byte value, EmuTime::param time);
321
322 MSXDevice*& getDevicePtr(byte port, bool isIn);
323
324 void register_IO (int port, bool isIn,
325 MSXDevice*& devicePtr, MSXDevice* device);
326 void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
327 void testRegisterSlot(const MSXDevice& device,
328 int ps, int ss, unsigned base, unsigned size);
329 void registerSlot(MSXDevice& device,
330 int ps, int ss, unsigned base, unsigned size);
331 void unregisterSlot(MSXDevice& device,
332 int ps, int ss, unsigned base, unsigned size);
333
334
335 void checkBreakPoints(std::pair<BreakPoints::const_iterator,
336 BreakPoints::const_iterator> range);
337
338 void removeAllWatchPoints();
339 void updateMemWatch(WatchPoint::Type type);
340 void executeMemWatch(WatchPoint::Type type, unsigned address,
341 unsigned value = ~0u);
342
343 struct MemoryDebug final : SimpleDebuggable {
344 explicit MemoryDebug(MSXMotherBoard& motherBoard);
345 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
346 void write(unsigned address, byte value, EmuTime::param time) override;
347 } memoryDebug;
348
349 struct SlottedMemoryDebug final : SimpleDebuggable {
350 explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
351 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
352 void write(unsigned address, byte value, EmuTime::param time) override;
353 } slottedMemoryDebug;
354
355 struct IODebug final : SimpleDebuggable {
356 explicit IODebug(MSXMotherBoard& motherBoard);
357 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
358 void write(unsigned address, byte value, EmuTime::param time) override;
359 } ioDebug;
360
361 struct SlotInfo final : InfoTopic {
362 explicit SlotInfo(InfoCommand& machineInfoCommand);
363 void execute(std::span<const TclObject> tokens,
364 TclObject& result) const override;
365 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
366 } slotInfo;
367
368 struct SubSlottedInfo final : InfoTopic {
369 explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
370 void execute(std::span<const TclObject> tokens,
371 TclObject& result) const override;
372 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
373 } subSlottedInfo;
374
375 struct ExternalSlotInfo final : InfoTopic {
376 explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
377 void execute(std::span<const TclObject> tokens,
378 TclObject& result) const override;
379 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
380 } externalSlotInfo;
381
382 struct IOInfo : InfoTopic {
383 IOInfo(InfoCommand& machineInfoCommand, const char* name);
384 void helper(std::span<const TclObject> tokens,
385 TclObject& result, std::span<MSXDevice*, 256> devices) const;
386 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
387 protected:
388 ~IOInfo() = default;
389 };
390 struct IInfo final : IOInfo {
391 explicit IInfo(InfoCommand& machineInfoCommand)
392 : IOInfo(machineInfoCommand, "input_port") {}
393 void execute(std::span<const TclObject> tokens,
394 TclObject& result) const override;
395 } inputPortInfo;
396 struct OInfo final : IOInfo {
397 explicit OInfo(InfoCommand& machineInfoCommand)
398 : IOInfo(machineInfoCommand, "output_port") {}
399 void execute(std::span<const TclObject> tokens,
400 TclObject& result) const override;
401 } outputPortInfo;
402
409 void updateVisible(byte page);
410 inline void updateVisible(byte page, byte ps, byte ss);
411 void setSubSlot(byte primSlot, byte value);
412
413 std::unique_ptr<DummyDevice> dummyDevice;
414 MSXCPU& msxcpu;
415 CliComm& cliComm;
416 MSXMotherBoard& motherBoard;
417 BooleanSetting& pauseSetting;
418
419 std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
420
421 std::array<byte, CacheLine::NUM> disallowReadCache;
422 std::array<byte, CacheLine::NUM> disallowWriteCache;
423 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> readWatchSet;
424 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> writeWatchSet;
425
426 struct GlobalRwInfo {
427 MSXDevice* device;
428 word addr;
429 [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
430 };
431 std::vector<GlobalRwInfo> globalReads;
432 std::vector<GlobalRwInfo> globalWrites;
433
434 std::array<MSXDevice*, 256> IO_In;
435 std::array<MSXDevice*, 256> IO_Out;
436 std::array<std::array<std::array<MSXDevice*, 4>, 4>, 4> slotLayout;
437 std::array<MSXDevice*, 4> visibleDevices;
438 std::array<byte, 4> subSlotRegister;
439 std::array<byte, 4> primarySlotState;
440 std::array<byte, 4> secondarySlotState;
441 byte initialPrimarySlots;
442 std::array<unsigned, 4> expanded;
443
444 bool fastForward = false; // no need to serialize
445
446 // All CPUs (Z80 and R800) of all MSX machines share this state.
447 static inline BreakPoints breakPoints; // sorted on address
448 WatchPoints watchPoints; // ordered in creation order, TODO must also be static
449 static inline Conditions conditions; // ordered in creation order
450 static inline bool breaked = false;
451};
452
453
454// Compile-Time Interval (half-open).
455// TODO possibly move this to utils/
456template<unsigned BEGIN, unsigned END = BEGIN + 1>
458{
459 [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
460 [[nodiscard]] unsigned end() const { return END; } // exclusive
461};
462
463// Execute an 'action' for every element in the given interval(s).
464inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
465{
466 for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
467 action(i);
468 }
469}
470inline void foreach_ct_interval(std::invocable<unsigned> auto action,
471 auto front_interval, auto... tail_intervals)
472{
473 foreach_ct_interval(action, front_interval);
474 foreach_ct_interval(action, tail_intervals...);
475}
476
477
478template<typename MSXDEVICE, typename... CT_INTERVALS>
480{
481 void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
482 {
483 auto& dev = static_cast<MSXDEVICE&>(*this);
484 auto& cpu = dev.getCPUInterface();
486 [&](unsigned addr) { action(cpu, dev, addr); },
487 CT_INTERVALS()...);
488 }
489};
490
491template<typename MSXDEVICE, typename... CT_INTERVALS>
492struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
493{
498
500 {
501 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
502 cpu.registerGlobalWrite(dev, narrow<word>(addr));
503 });
504 }
505
507 {
508 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
509 cpu.unregisterGlobalWrite(dev, narrow<word>(addr));
510 });
511 }
512};
513
514template<typename MSXDEVICE, typename... CT_INTERVALS>
515struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
516{
521
523 {
524 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
525 cpu.registerGlobalRead(dev, narrow<word>(addr));
526 });
527 }
528
530 {
531 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
532 cpu.unregisterGlobalRead(dev, narrow<word>(addr));
533 });
534 }
535};
536
537} // namespace openmsx
538
539#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 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)
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.
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
static const Conditions & getConditions()
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.
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
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)
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition ranges.hh:135
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