OXYGENHUB
◆ main openmsx / src / cpu / WatchPoint.hh
5.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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
19
namespace openmsx {
20
21
class WatchPoint;
22
23
class MSXWatchIODevice final : public MSXMultiDevice
24
{
25
public:
26
	MSXWatchIODevice(HardwareConfig& hwConf, WatchPoint& wp);
27
28
	[[nodiscard]] MSXDevice*& getDevicePtr() { return device; }
29
30
private:
31
	// MSXDevice
32
	[[nodiscard]] const std::string& getName() const override;
33
	[[nodiscard]] uint8_t readIO(uint16_t port, EmuTime time) override;
34
	[[nodiscard]] uint8_t peekIO(uint16_t port, EmuTime time) const override;
35
	void writeIO(uint16_t port, uint8_t value, EmuTime time) override;
36
37
private:
38
	WatchPoint& wp;
39
	MSXDevice* device = nullptr;
40
};
41
42
class WatchPoint final : public BreakPointBase<WatchPoint>
43
                       , public std::enable_shared_from_this<WatchPoint>
44
{
45
public:
46
	static constexpr std::string_view prefix = "wp#";
47
48
	enum class Type : uint8_t { READ_IO, WRITE_IO, READ_MEM, WRITE_MEM };
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
		}
59
		UNREACHABLE;
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
85
public:
86
	WatchPoint() = default;
87
88
	struct clone_tag {};
89
	WatchPoint(clone_tag, const WatchPoint& wp)
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; }
113
	void setBeginAddressString(Interpreter& interp, const TclObject& s) {
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
126
	void evaluateAddress(Interpreter& interp) {
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
149
private:
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
	}
167
private:
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