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:
61
62 explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
64
70 void register_IO_In(byte port, MSXDevice* device);
71 void unregister_IO_In(byte port, MSXDevice* device);
72
78 void register_IO_Out(byte port, MSXDevice* device);
79 void unregister_IO_Out(byte port, MSXDevice* device);
80
96 bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
97 bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
98
105 void registerMemDevice(MSXDevice& device,
106 int ps, int ss, unsigned base, unsigned size);
107 void unregisterMemDevice(MSXDevice& device,
108 int ps, int ss, unsigned base, unsigned size);
109
113 void registerGlobalWrite(MSXDevice& device, word address);
114 void unregisterGlobalWrite(MSXDevice& device, word address);
115
119 void registerGlobalRead(MSXDevice& device, word address);
120 void unregisterGlobalRead(MSXDevice& device, word address);
121
125 void reset();
126
130 byte readMem(word address, EmuTime::param time) {
132 if (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
133 return readMemSlow(address, time);
134 }
135 return visibleDevices[address >> 14]->readMem(address, time);
136 }
137
141 void writeMem(word address, byte value, EmuTime::param time) {
143 if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
144 writeMemSlow(address, value, time);
145 return;
146 }
147 visibleDevices[address>>14]->writeMem(address, value, time);
148 }
149
154 byte readIO(word port, EmuTime::param time) {
155 return IO_In[port & 0xFF]->readIO(port, time);
156 }
157
162 void writeIO(word port, byte value, EmuTime::param time) {
163 IO_Out[port & 0xFF]->writeIO(port, value, time);
164 }
165
178 [[nodiscard]] const byte* getReadCacheLine(word start) const {
180 if (disallowReadCache[start >> CacheLine::BITS]) [[unlikely]] {
181 return nullptr;
182 }
183 return visibleDevices[start >> 14]->getReadCacheLine(start);
184 }
185
198 [[nodiscard]] byte* getWriteCacheLine(word start) const {
200 if (disallowWriteCache[start >> CacheLine::BITS]) [[unlikely]] {
201 return nullptr;
202 }
203 return visibleDevices[start >> 14]->getWriteCacheLine(start);
204 }
205
210 [[nodiscard]] byte readIRQVector();
211
212 /*
213 * Should only be used by PPI
214 * TODO: make private / friend
215 */
216 void setPrimarySlots(byte value);
217
219 void invalidateRWCache(word start, unsigned size, int ps, int ss);
220 void invalidateRCache (word start, unsigned size, int ps, int ss);
221 void invalidateWCache (word start, unsigned size, int ps, int ss);
222
224 void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
225 void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
226 void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
227
232 [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
233 [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
234 byte readSlottedMem(unsigned address, EmuTime::param time);
235 void writeSlottedMem(unsigned address, byte value,
236 EmuTime::param time);
237
238 void setExpanded(int ps);
239 void unsetExpanded(int ps);
240 void testUnsetExpanded(int ps,
241 std::span<const std::unique_ptr<MSXDevice>> allowed) const;
242 [[nodiscard]] bool isExpanded(int ps) const { return expanded[ps] != 0; }
243 void changeExpanded(bool newExpanded);
244
245 [[nodiscard]] auto getPrimarySlot (int page) const { return primarySlotState[page]; }
246 [[nodiscard]] auto getSecondarySlot(int page) const { return secondarySlotState[page]; }
247
248 [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
249
251 void removeBreakPoint(const BreakPoint& bp);
252 void removeBreakPoint(unsigned id);
253 using BreakPoints = std::vector<BreakPoint>;
254 [[nodiscard]] static const BreakPoints& getBreakPoints() { return breakPoints; }
255
256 void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
257 void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
258 void removeWatchPoint(unsigned id);
259 // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
260 using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
261 [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
262
263 void setCondition(DebugCondition cond);
264 void removeCondition(const DebugCondition& cond);
265 void removeCondition(unsigned id);
266 using Conditions = std::vector<DebugCondition>;
267 [[nodiscard]] static const Conditions& getConditions() { return conditions; }
268
269 [[nodiscard]] static bool isBreaked() { return breaked; }
270 void doBreak();
271 void doStep();
272 void doContinue();
273
274 // breakpoint methods used by CPUCore
275 [[nodiscard]] static bool anyBreakPoints()
276 {
277 return !breakPoints.empty() || !conditions.empty();
278 }
279 [[nodiscard]] bool checkBreakPoints(unsigned pc)
280 {
281 auto range = ranges::equal_range(breakPoints, pc, {}, &BreakPoint::getAddress);
282 if (conditions.empty() && (range.first == range.second)) {
283 return false;
284 }
285
286 // slow path non-inlined
287 checkBreakPoints(range);
288 return isBreaked();
289 }
290
291 // cleanup global variables
292 static void cleanup();
293
294 // In fast-forward mode, breakpoints, watchpoints and conditions should
295 // not trigger.
296 void setFastForward(bool fastForward_) { fastForward = fastForward_; }
297 [[nodiscard]] bool isFastForward() const { return fastForward; }
298
299 [[nodiscard]] MSXDevice* getMSXDevice(int ps, int ss, int page);
300 [[nodiscard]] MSXDevice* getVisibleMSXDevice(int page) { return visibleDevices[page]; }
301
302 template<typename Archive>
303 void serialize(Archive& ar, unsigned version);
304
305private:
306 byte readMemSlow(word address, EmuTime::param time);
307 void writeMemSlow(word address, byte value, EmuTime::param time);
308
309 MSXDevice*& getDevicePtr(byte port, bool isIn);
310
311 void register_IO (int port, bool isIn,
312 MSXDevice*& devicePtr, MSXDevice* device);
313 void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
314 void testRegisterSlot(MSXDevice& device,
315 int ps, int ss, unsigned base, unsigned size);
316 void registerSlot(MSXDevice& device,
317 int ps, int ss, unsigned base, unsigned size);
318 void unregisterSlot(MSXDevice& device,
319 int ps, int ss, unsigned base, unsigned size);
320
321
322 void checkBreakPoints(std::pair<BreakPoints::const_iterator,
323 BreakPoints::const_iterator> range);
324
325 void removeAllWatchPoints();
326 void updateMemWatch(WatchPoint::Type type);
327 void executeMemWatch(WatchPoint::Type type, unsigned address,
328 unsigned value = ~0u);
329
330 struct MemoryDebug final : SimpleDebuggable {
331 explicit MemoryDebug(MSXMotherBoard& motherBoard);
332 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
333 void write(unsigned address, byte value, EmuTime::param time) override;
334 } memoryDebug;
335
336 struct SlottedMemoryDebug final : SimpleDebuggable {
337 explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
338 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
339 void write(unsigned address, byte value, EmuTime::param time) override;
340 } slottedMemoryDebug;
341
342 struct IODebug final : SimpleDebuggable {
343 explicit IODebug(MSXMotherBoard& motherBoard);
344 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
345 void write(unsigned address, byte value, EmuTime::param time) override;
346 } ioDebug;
347
348 struct SlotInfo final : InfoTopic {
349 explicit SlotInfo(InfoCommand& machineInfoCommand);
350 void execute(std::span<const TclObject> tokens,
351 TclObject& result) const override;
352 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
353 } slotInfo;
354
355 struct SubSlottedInfo final : InfoTopic {
356 explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
357 void execute(std::span<const TclObject> tokens,
358 TclObject& result) const override;
359 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
360 } subSlottedInfo;
361
362 struct ExternalSlotInfo final : InfoTopic {
363 explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
364 void execute(std::span<const TclObject> tokens,
365 TclObject& result) const override;
366 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
367 } externalSlotInfo;
368
369 struct IOInfo : InfoTopic {
370 IOInfo(InfoCommand& machineInfoCommand, const char* name);
371 void helper(std::span<const TclObject> tokens,
372 TclObject& result, std::span<MSXDevice*, 256> devices) const;
373 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
374 protected:
375 ~IOInfo() = default;
376 };
377 struct IInfo final : IOInfo {
378 explicit IInfo(InfoCommand& machineInfoCommand)
379 : IOInfo(machineInfoCommand, "input_port") {}
380 void execute(std::span<const TclObject> tokens,
381 TclObject& result) const override;
382 } inputPortInfo;
383 struct OInfo final : IOInfo {
384 explicit OInfo(InfoCommand& machineInfoCommand)
385 : IOInfo(machineInfoCommand, "output_port") {}
386 void execute(std::span<const TclObject> tokens,
387 TclObject& result) const override;
388 } outputPortInfo;
389
396 void updateVisible(byte page);
397 inline void updateVisible(byte page, byte ps, byte ss);
398 void setSubSlot(byte primSlot, byte value);
399
400 std::unique_ptr<DummyDevice> dummyDevice;
401 MSXCPU& msxcpu;
402 CliComm& cliComm;
403 MSXMotherBoard& motherBoard;
404 BooleanSetting& pauseSetting;
405
406 std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
407
408 std::array<byte, CacheLine::NUM> disallowReadCache;
409 std::array<byte, CacheLine::NUM> disallowWriteCache;
410 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> readWatchSet;
411 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> writeWatchSet;
412
413 struct GlobalRwInfo {
414 MSXDevice* device;
415 word addr;
416 [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
417 };
418 std::vector<GlobalRwInfo> globalReads;
419 std::vector<GlobalRwInfo> globalWrites;
420
421 std::array<MSXDevice*, 256> IO_In;
422 std::array<MSXDevice*, 256> IO_Out;
423 std::array<std::array<std::array<MSXDevice*, 4>, 4>, 4> slotLayout;
424 std::array<MSXDevice*, 4> visibleDevices;
425 std::array<byte, 4> subSlotRegister;
426 std::array<byte, 4> primarySlotState;
427 std::array<byte, 4> secondarySlotState;
428 byte initialPrimarySlots;
429 std::array<unsigned, 4> expanded;
430
431 bool fastForward = false; // no need to serialize
432
433 // All CPUs (Z80 and R800) of all MSX machines share this state.
434 static inline BreakPoints breakPoints; // sorted on address
435 WatchPoints watchPoints; // ordered in creation order, TODO must also be static
436 static inline Conditions conditions; // ordered in creation order
437 static inline bool breaked = false;
438};
439
440
441// Compile-Time Interval (half-open).
442// TODO possibly move this to utils/
443template<unsigned BEGIN, unsigned END = BEGIN + 1>
445{
446 [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
447 [[nodiscard]] unsigned end() const { return END; } // exclusive
448};
449
450// Execute an 'action' for every element in the given interval(s).
451inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
452{
453 for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
454 action(i);
455 }
456}
457inline void foreach_ct_interval(std::invocable<unsigned> auto action,
458 auto front_interval, auto... tail_intervals)
459{
460 foreach_ct_interval(action, front_interval);
461 foreach_ct_interval(action, tail_intervals...);
462}
463
464
465template<typename MSXDEVICE, typename... CT_INTERVALS>
467{
468 void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
469 {
470 auto& dev = static_cast<MSXDEVICE&>(*this);
471 auto& cpu = dev.getCPUInterface();
473 [&](unsigned addr) { action(cpu, dev, addr); },
474 CT_INTERVALS()...);
475 }
476};
477
478template<typename MSXDEVICE, typename... CT_INTERVALS>
479struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
480{
482 {
483 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
484 cpu.registerGlobalWrite(dev, narrow<word>(addr));
485 });
486 }
487
489 {
490 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
491 cpu.unregisterGlobalWrite(dev, narrow<word>(addr));
492 });
493 }
494};
495
496template<typename MSXDEVICE, typename... CT_INTERVALS>
497struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
498{
500 {
501 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
502 cpu.registerGlobalRead(dev, narrow<word>(addr));
503 });
504 }
505
507 {
508 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
509 cpu.unregisterGlobalRead(dev, narrow<word>(addr));
510 });
511 }
512};
513
514} // namespace openmsx
515
516#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 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
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
byte readIRQVector()
CPU uses this method to read 'extra' data from the data bus used in interrupt routines.
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.
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:9
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)