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