openMSX
WatchPoint.hh
Go to the documentation of this file.
1#ifndef WATCHPOINT_HH
2#define WATCHPOINT_HH
3
4#include "BreakPointBase.hh"
5#include "CommandException.hh"
6#include "MSXMultiDevice.hh"
7
8#include "narrow.hh"
9#include "one_of.hh"
10#include "strCat.hh"
11#include "unreachable.hh"
12
13#include <cassert>
14#include <cstdint>
15#include <memory>
16#include <optional>
17#include <string_view>
18
19namespace openmsx {
20
21class WatchPoint;
22
23class MSXWatchIODevice final : public MSXMultiDevice
24{
25public:
26 MSXWatchIODevice(const HardwareConfig& hwConf, WatchPoint& wp);
27
28 [[nodiscard]] MSXDevice*& getDevicePtr() { return device; }
29
30private:
31 // MSXDevice
32 [[nodiscard]] const std::string& getName() const override;
33 [[nodiscard]] byte readIO(word port, EmuTime::param time) override;
34 [[nodiscard]] byte peekIO(word port, EmuTime::param time) const override;
35 void writeIO(word port, byte value, EmuTime::param time) override;
36
37private:
38 WatchPoint& wp;
39 MSXDevice* device = nullptr;
40};
41
42class WatchPoint final : public BreakPointBase<WatchPoint>
43 , public std::enable_shared_from_this<WatchPoint>
44{
45public:
46 static constexpr std::string_view prefix = "wp#";
47
49
50 static std::string_view format(Type type)
51 {
52 switch (type) {
53 using enum Type;
54 case READ_IO: return "read_io";
55 case WRITE_IO: return "write_io";
56 case READ_MEM: return "read_mem";
57 case WRITE_MEM: return "write_mem";
58 }
60 }
61 static Type parseType(std::string_view str)
62 {
63 using enum Type;
64 if (str == "read_io") return READ_IO;
65 if (str == "write_io") return WRITE_IO;
66 if (str == "read_mem") return READ_MEM;
67 if (str == "write_mem") return WRITE_MEM;
68 throw CommandException("Invalid type: ", str);
69 }
70 static unsigned rangeForType(Type type)
71 {
72 return (type == one_of(Type::READ_IO, Type::WRITE_IO)) ? 0x100 : 0x10000;
73 }
74 static std::pair<TclObject, TclObject> parseAddress(Interpreter& interp, const TclObject& a)
75 {
76 auto len = a.getListLength(interp);
77 if (len != one_of(1u, 2u)) {
78 throw CommandException("Invalid address: must be a single address, or a begin/end pair");
79 }
80 auto begin = a.getListIndex(interp, 0);
81 auto end = (len == 2) ? a.getListIndex(interp, 1) : TclObject{};
82 return {begin, end};
83 }
84
85public:
86 WatchPoint() = default;
87
88 struct clone_tag {};
90 : BreakPointBase(wp)
91 , type(wp.type)
92 , beginAddrStr(wp.beginAddrStr), endAddrStr(wp.endAddrStr)
93 , beginAddr(wp.beginAddr), endAddr(wp.endAddr)
94 {
95 id = wp.id;
96 assert(ios.empty());
97 assert(!registered);
98 }
99
100 [[nodiscard]] Type getType() const { return type; }
101 void setType(Interpreter& interp, Type t) {
102 type = t;
103 evaluateAddress(interp); // because range may have changed
104 }
105 void setType(Interpreter& interp, const TclObject& t) {
106 setType(interp, parseType(t.getString()));
107 }
108
109 [[nodiscard]] auto getBeginAddress() const { return beginAddr; }
110 [[nodiscard]] auto getEndAddress() const { return endAddr; }
111 [[nodiscard]] auto getBeginAddressString() const { return beginAddrStr; }
112 [[nodiscard]] auto getEndAddressString() const { return endAddrStr; }
114 beginAddrStr = s;
115 evaluateAddress(interp);
116 }
117 void setEndAddressString(Interpreter& interp, const TclObject& s) {
118 endAddrStr = s;
119 evaluateAddress(interp);
120 }
121 void setAddress(Interpreter& interp, const TclObject& a) {
122 std::tie(beginAddrStr, endAddrStr) = parseAddress(interp, a);
123 evaluateAddress(interp);
124 }
125
127 assert(!registered);
128 try {
129 auto [b, e] = parseAddress(interp);
130 beginAddr = b;
131 endAddr = e;
132 } catch (MSXException&) {
133 beginAddr = endAddr = {};
134 }
135 }
136
137 [[nodiscard]] std::string parseAddressError(Interpreter& interp) const {
138 try {
139 parseAddress(interp);
140 return {};
141 } catch (MSXException& e) {
142 return e.getMessage();
143 }
144 }
145
146 void registerIOWatch(MSXMotherBoard& motherBoard, std::span<MSXDevice*, 256> devices);
147 void unregisterIOWatch(std::span<MSXDevice*, 256> devices);
148
149private:
150 void doReadCallback(MSXMotherBoard& motherBoard, unsigned port);
151 void doWriteCallback(MSXMotherBoard& motherBoard, unsigned port, unsigned value);
152
153 std::pair<uint16_t, uint16_t> parseAddress(Interpreter& interp) const {
154 auto begin = beginAddrStr.eval(interp).getInt(interp); // may throw
155 auto end = endAddrStr.getString().empty()
156 ? begin
157 : endAddrStr.eval(interp).getInt(interp); // may throw
158 if (end < begin) {
159 throw CommandException("begin may not be larger than end: ", begin, " > ", end);
160 }
161 auto max = rangeForType(type);
162 if ((begin < 0) || (end >= int(max))) {
163 throw CommandException("address outside of range 0...", max - 1);
164 }
165 return {narrow<uint16_t>(begin), narrow<uint16_t>(end)};
166 }
167private:
168 Type type = Type::WRITE_MEM;
169 TclObject beginAddrStr;
170 TclObject endAddrStr;
171 std::optional<uint16_t> beginAddr; // begin and end address are inclusive (IOW range = [begin, end])
172 std::optional<uint16_t> endAddr;
173
174 std::vector<std::unique_ptr<MSXWatchIODevice>> ios;
175 bool registered = false; // for debugging only
176
177 friend class MSXWatchIODevice;
178};
179
180} // namespace openmsx
181
182#endif
TclObject t
CRTP base class for CPU break and watch points.
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
MSXDevice *& getDevicePtr()
Definition WatchPoint.hh:28
unsigned getListLength(Interpreter &interp) const
Definition TclObject.cc:155
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition TclObject.cc:173
TclObject eval(Interpreter &interp) const
Definition TclObject.cc:238
int getInt(Interpreter &interp) const
Definition TclObject.cc:69
zstring_view getString() const
Definition TclObject.cc:141
static std::string_view format(Type type)
Definition WatchPoint.hh:50
auto getEndAddress() const
void setEndAddressString(Interpreter &interp, const TclObject &s)
void registerIOWatch(MSXMotherBoard &motherBoard, std::span< MSXDevice *, 256 > devices)
Definition WatchPoint.cc:20
WatchPoint(clone_tag, const WatchPoint &wp)
Definition WatchPoint.hh:89
auto getEndAddressString() const
auto getBeginAddress() const
std::string parseAddressError(Interpreter &interp) const
Type getType() const
static Type parseType(std::string_view str)
Definition WatchPoint.hh:61
static std::pair< TclObject, TclObject > parseAddress(Interpreter &interp, const TclObject &a)
Definition WatchPoint.hh:74
void setAddress(Interpreter &interp, const TclObject &a)
static unsigned rangeForType(Type type)
Definition WatchPoint.hh:70
void setType(Interpreter &interp, Type t)
static constexpr std::string_view prefix
Definition WatchPoint.hh:46
auto getBeginAddressString() const
void setType(Interpreter &interp, const TclObject &t)
void evaluateAddress(Interpreter &interp)
void setBeginAddressString(Interpreter &interp, const TclObject &s)
void unregisterIOWatch(std::span< MSXDevice *, 256 > devices)
Definition WatchPoint.cc:37
constexpr auto empty() const
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
#define UNREACHABLE
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)