| 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 |
|
| 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); |
| 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); |
| 155 |
auto end = endAddrStr.getString().empty() |
| 156 |
? begin |
| 157 |
: endAddrStr.eval(interp).getInt(interp); |
| 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; |
| 172 |
std::optional<uint16_t> endAddr; |
| 173 |
|
| 174 |
std::vector<std::unique_ptr<MSXWatchIODevice>> ios; |
| 175 |
bool registered = false; |
| 176 |
|
| 177 |
friend class MSXWatchIODevice; |
| 178 |
}; |
| 179 |
|
| 180 |
} |
| 181 |
|
| 182 |
#endif |