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 "ranges.hh"
14#include <array>
15#include <bitset>
16#include <concepts>
17#include <vector>
18#include <memory>
19
20namespace openmsx {
21
22class VDPIODelay;
23class DummyDevice;
24class MSXMotherBoard;
25class MSXCPU;
26class CliComm;
27class BreakPoint;
28
29inline constexpr bool PROFILE_CACHELINES = false;
46 NUM // must be last
47};
48std::ostream& operator<<(std::ostream& os, EnumTypeName<CacheLineCounters>);
49std::ostream& operator<<(std::ostream& os, EnumValueName<CacheLineCounters> evn);
50
51class MSXCPUInterface : public ProfileCounters<PROFILE_CACHELINES, CacheLineCounters>
52{
53public:
56
57 explicit MSXCPUInterface(MSXMotherBoard& motherBoard);
59
65 void register_IO_In(byte port, MSXDevice* device);
66 void unregister_IO_In(byte port, MSXDevice* device);
67
73 void register_IO_Out(byte port, MSXDevice* device);
74 void unregister_IO_Out(byte port, MSXDevice* device);
75
91 bool replace_IO_In (byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
92 bool replace_IO_Out(byte port, MSXDevice* oldDevice, MSXDevice* newDevice);
93
100 void registerMemDevice(MSXDevice& device,
101 int ps, int ss, unsigned base, unsigned size);
102 void unregisterMemDevice(MSXDevice& device,
103 int ps, int ss, unsigned base, unsigned size);
104
108 void registerGlobalWrite(MSXDevice& device, word address);
109 void unregisterGlobalWrite(MSXDevice& device, word address);
110
114 void registerGlobalRead(MSXDevice& device, word address);
115 void unregisterGlobalRead(MSXDevice& device, word address);
116
120 void reset();
121
125 inline byte readMem(word address, EmuTime::param time) {
127 if (disallowReadCache[address >> CacheLine::BITS]) [[unlikely]] {
128 return readMemSlow(address, time);
129 }
130 return visibleDevices[address >> 14]->readMem(address, time);
131 }
132
136 inline void writeMem(word address, byte value, EmuTime::param time) {
138 if (disallowWriteCache[address >> CacheLine::BITS]) [[unlikely]] {
139 writeMemSlow(address, value, time);
140 return;
141 }
142 visibleDevices[address>>14]->writeMem(address, value, time);
143 }
144
149 inline byte readIO(word port, EmuTime::param time) {
150 return IO_In[port & 0xFF]->readIO(port, time);
151 }
152
157 inline void writeIO(word port, byte value, EmuTime::param time) {
158 IO_Out[port & 0xFF]->writeIO(port, value, time);
159 }
160
173 [[nodiscard]] inline const byte* getReadCacheLine(word start) const {
175 if (disallowReadCache[start >> CacheLine::BITS]) [[unlikely]] {
176 return nullptr;
177 }
178 return visibleDevices[start >> 14]->getReadCacheLine(start);
179 }
180
193 [[nodiscard]] inline byte* getWriteCacheLine(word start) const {
195 if (disallowWriteCache[start >> CacheLine::BITS]) [[unlikely]] {
196 return nullptr;
197 }
198 return visibleDevices[start >> 14]->getWriteCacheLine(start);
199 }
200
205 [[nodiscard]] byte readIRQVector();
206
207 /*
208 * Should only be used by PPI
209 * TODO: make private / friend
210 */
211 void setPrimarySlots(byte value);
212
214 void invalidateRWCache(word start, unsigned size, int ps, int ss);
215 void invalidateRCache (word start, unsigned size, int ps, int ss);
216 void invalidateWCache (word start, unsigned size, int ps, int ss);
217
219 void fillRWCache(unsigned start, unsigned size, const byte* rData, byte* wData, int ps, int ss);
220 void fillRCache (unsigned start, unsigned size, const byte* rData, int ps, int ss);
221 void fillWCache (unsigned start, unsigned size, byte* wData, int ps, int ss);
222
227 [[nodiscard]] byte peekMem(word address, EmuTime::param time) const;
228 [[nodiscard]] byte peekSlottedMem(unsigned address, EmuTime::param time) const;
229 byte readSlottedMem(unsigned address, EmuTime::param time);
230 void writeSlottedMem(unsigned address, byte value,
231 EmuTime::param time);
232
233 void setExpanded(int ps);
234 void unsetExpanded(int ps);
235 void testUnsetExpanded(int ps,
236 std::span<const std::unique_ptr<MSXDevice>> allowed) const;
237 [[nodiscard]] inline bool isExpanded(int ps) const { return expanded[ps] != 0; }
238 void changeExpanded(bool newExpanded);
239
240 [[nodiscard]] DummyDevice& getDummyDevice() { return *dummyDevice; }
241
243 void removeBreakPoint(const BreakPoint& bp);
244 using BreakPoints = std::vector<BreakPoint>;
245 [[nodiscard]] static const BreakPoints& getBreakPoints() { return breakPoints; }
246
247 void setWatchPoint(const std::shared_ptr<WatchPoint>& watchPoint);
248 void removeWatchPoint(std::shared_ptr<WatchPoint> watchPoint);
249 // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback()
250 using WatchPoints = std::vector<std::shared_ptr<WatchPoint>>;
251 [[nodiscard]] const WatchPoints& getWatchPoints() const { return watchPoints; }
252
253 void setCondition(DebugCondition cond);
254 void removeCondition(const DebugCondition& cond);
255 using Conditions = std::vector<DebugCondition>;
256 [[nodiscard]] static const Conditions& getConditions() { return conditions; }
257
258 [[nodiscard]] static bool isBreaked() { return breaked; }
259 void doBreak();
260 void doStep();
261 void doContinue();
262
263 // breakpoint methods used by CPUCore
264 [[nodiscard]] static bool anyBreakPoints()
265 {
266 return !breakPoints.empty() || !conditions.empty();
267 }
268 [[nodiscard]] bool checkBreakPoints(unsigned pc)
269 {
270 auto range = ranges::equal_range(breakPoints, pc, {}, &BreakPoint::getAddress);
271 if (conditions.empty() && (range.first == range.second)) {
272 return false;
273 }
274
275 // slow path non-inlined
276 checkBreakPoints(range);
277 return isBreaked();
278 }
279
280 // cleanup global variables
281 static void cleanup();
282
283 // In fast-forward mode, breakpoints, watchpoints and conditions should
284 // not trigger.
285 void setFastForward(bool fastForward_) { fastForward = fastForward_; }
286 [[nodiscard]] bool isFastForward() const { return fastForward; }
287
288 [[nodiscard]] MSXDevice* getMSXDevice(int ps, int ss, int page);
289
290 template<typename Archive>
291 void serialize(Archive& ar, unsigned version);
292
293private:
294 byte readMemSlow(word address, EmuTime::param time);
295 void writeMemSlow(word address, byte value, EmuTime::param time);
296
297 MSXDevice*& getDevicePtr(byte port, bool isIn);
298
299 void register_IO (int port, bool isIn,
300 MSXDevice*& devicePtr, MSXDevice* device);
301 void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device);
302 void testRegisterSlot(MSXDevice& device,
303 int ps, int ss, unsigned base, unsigned size);
304 void registerSlot(MSXDevice& device,
305 int ps, int ss, unsigned base, unsigned size);
306 void unregisterSlot(MSXDevice& device,
307 int ps, int ss, unsigned base, unsigned size);
308
309
310 void checkBreakPoints(std::pair<BreakPoints::const_iterator,
311 BreakPoints::const_iterator> range);
312 void removeBreakPoint(unsigned id);
313 void removeCondition(unsigned id);
314
315 void removeAllWatchPoints();
316 void updateMemWatch(WatchPoint::Type type);
317 void executeMemWatch(WatchPoint::Type type, unsigned address,
318 unsigned value = ~0u);
319
320 struct MemoryDebug final : SimpleDebuggable {
321 explicit MemoryDebug(MSXMotherBoard& motherBoard);
322 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
323 void write(unsigned address, byte value, EmuTime::param time) override;
324 } memoryDebug;
325
326 struct SlottedMemoryDebug final : SimpleDebuggable {
327 explicit SlottedMemoryDebug(MSXMotherBoard& motherBoard);
328 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
329 void write(unsigned address, byte value, EmuTime::param time) override;
330 } slottedMemoryDebug;
331
332 struct IODebug final : SimpleDebuggable {
333 explicit IODebug(MSXMotherBoard& motherBoard);
334 [[nodiscard]] byte read(unsigned address, EmuTime::param time) override;
335 void write(unsigned address, byte value, EmuTime::param time) override;
336 } ioDebug;
337
338 struct SlotInfo final : InfoTopic {
339 explicit SlotInfo(InfoCommand& machineInfoCommand);
340 void execute(std::span<const TclObject> tokens,
341 TclObject& result) const override;
342 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
343 } slotInfo;
344
345 struct SubSlottedInfo final : InfoTopic {
346 explicit SubSlottedInfo(InfoCommand& machineInfoCommand);
347 void execute(std::span<const TclObject> tokens,
348 TclObject& result) const override;
349 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
350 } subSlottedInfo;
351
352 struct ExternalSlotInfo final : InfoTopic {
353 explicit ExternalSlotInfo(InfoCommand& machineInfoCommand);
354 void execute(std::span<const TclObject> tokens,
355 TclObject& result) const override;
356 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
357 } externalSlotInfo;
358
359 struct IOInfo : InfoTopic {
360 IOInfo(InfoCommand& machineInfoCommand, const char* name);
361 void helper(std::span<const TclObject> tokens,
362 TclObject& result, std::span<MSXDevice*, 256> devices) const;
363 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
364 protected:
365 ~IOInfo() = default;
366 };
367 struct IInfo final : IOInfo {
368 explicit IInfo(InfoCommand& machineInfoCommand)
369 : IOInfo(machineInfoCommand, "input_port") {}
370 void execute(std::span<const TclObject> tokens,
371 TclObject& result) const override;
372 } inputPortInfo;
373 struct OInfo final : IOInfo {
374 explicit OInfo(InfoCommand& machineInfoCommand)
375 : IOInfo(machineInfoCommand, "output_port") {}
376 void execute(std::span<const TclObject> tokens,
377 TclObject& result) const override;
378 } outputPortInfo;
379
386 void updateVisible(unsigned page);
387 inline void updateVisible(unsigned page, int ps, int ss);
388 void setSubSlot(byte primSlot, byte value);
389
390 std::unique_ptr<DummyDevice> dummyDevice;
391 MSXCPU& msxcpu;
392 CliComm& cliComm;
393 MSXMotherBoard& motherBoard;
394
395 std::unique_ptr<VDPIODelay> delayDevice; // can be nullptr
396
397 std::array<byte, CacheLine::NUM> disallowReadCache;
398 std::array<byte, CacheLine::NUM> disallowWriteCache;
399 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> readWatchSet;
400 std::array<std::bitset<CacheLine::SIZE>, CacheLine::NUM> writeWatchSet;
401
402 struct GlobalRwInfo {
403 MSXDevice* device;
404 word addr;
405 [[nodiscard]] constexpr bool operator==(const GlobalRwInfo&) const = default;
406 };
407 std::vector<GlobalRwInfo> globalReads;
408 std::vector<GlobalRwInfo> globalWrites;
409
410 std::array<MSXDevice*, 256> IO_In;
411 std::array<MSXDevice*, 256> IO_Out;
412 std::array<std::array<std::array<MSXDevice*, 4>, 4>, 4> slotLayout;
413 std::array<MSXDevice*, 4> visibleDevices;
414 std::array<byte, 4> subSlotRegister;
415 std::array<byte, 4> primarySlotState;
416 std::array<byte, 4> secondarySlotState;
417 byte initialPrimarySlots;
418 std::array<unsigned, 4> expanded;
419
420 bool fastForward = false; // no need to serialize
421
422 // All CPUs (Z80 and R800) of all MSX machines share this state.
423 static inline BreakPoints breakPoints; // sorted on address
424 WatchPoints watchPoints; // ordered in creation order, TODO must also be static
425 static inline Conditions conditions; // ordered in creation order
426 static inline bool breaked = false;
427};
428
429
430// Compile-Time Interval (half-open).
431// TODO possibly move this to utils/
432template<unsigned BEGIN, unsigned END = BEGIN + 1>
434{
435 [[nodiscard]] unsigned begin() const { return BEGIN; } // inclusive
436 [[nodiscard]] unsigned end() const { return END; } // exclusive
437};
438
439// Execute an 'action' for every element in the given interval(s).
440inline void foreach_ct_interval(std::invocable<unsigned> auto action, auto ct_interval)
441{
442 for (auto i = ct_interval.begin(); i != ct_interval.end(); ++i) {
443 action(i);
444 }
445}
446inline void foreach_ct_interval(std::invocable<unsigned> auto action,
447 auto front_interval, auto... tail_intervals)
448{
449 foreach_ct_interval(action, front_interval);
450 foreach_ct_interval(action, tail_intervals...);
451}
452
453
454template<typename MSXDEVICE, typename... CT_INTERVALS>
456{
457 void execute(std::invocable<MSXCPUInterface&, MSXDevice&, unsigned> auto action)
458 {
459 auto& dev = static_cast<MSXDEVICE&>(*this);
460 auto& cpu = dev.getCPUInterface();
462 [&](unsigned addr) { action(cpu, dev, addr); },
463 CT_INTERVALS()...);
464 }
465};
466
467template<typename MSXDEVICE, typename... CT_INTERVALS>
468struct GlobalWriteClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
469{
471 {
472 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
473 cpu.registerGlobalWrite(dev, addr);
474 });
475 }
476
478 {
479 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
480 cpu.unregisterGlobalWrite(dev, addr);
481 });
482 }
483};
484
485template<typename MSXDEVICE, typename... CT_INTERVALS>
486struct GlobalReadClient : GlobalRWHelper<MSXDEVICE, CT_INTERVALS...>
487{
489 {
490 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
491 cpu.registerGlobalRead(dev, addr);
492 });
493 }
494
496 {
497 this->execute([](MSXCPUInterface& cpu, MSXDevice& dev, unsigned addr) {
498 cpu.unregisterGlobalRead(dev, addr);
499 });
500 }
501};
502
503} // namespace openmsx
504
505#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)
void unregister_IO_In(byte port, MSXDevice *device)
DummyDevice & getDummyDevice()
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
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
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:34
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 Event &x, const Event &y)
Definition: Event.cc:11
@ InvalidateAllSlots
@ DisallowCacheWrite
@ InvalidateReadWrite
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
size_t size(std::string_view utf8)
unsigned end() const
unsigned begin() const
void execute(std::invocable< MSXCPUInterface &, MSXDevice &, unsigned > auto action)