OXYGENHUB
◆ main openmsx / src / cpu / WatchPoint.cc
4.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "WatchPoint.hh"
2
3
#include "Interpreter.hh"
4
#include "MSXCPUInterface.hh"
5
#include "MSXMotherBoard.hh"
6
#include "Reactor.hh"
7
#include "StateChangeDistributor.hh"
8
#include "TclObject.hh"
9
10
#include "checked_cast.hh"
11
#include "one_of.hh"
12
13
#include <cassert>
14
#include <memory>
15
16
namespace openmsx {
17
18
// class WatchPoint
19
20
void WatchPoint::registerIOWatch(MSXMotherBoard& motherBoard, std::span<MSXDevice*, 256> devices)
21
{
22
	if (!beginAddr || !endAddr || !isEnabled()) return;
23
	assert(getType() == one_of(Type::READ_IO, Type::WRITE_IO));
24
	assert(!registered);
25
	assert(*beginAddr < 0x100 && *endAddr < 0x100 && *beginAddr <= *endAddr);
26
	for (unsigned port = *beginAddr; port <= *endAddr; ++port) {
27
		// create new MSXWatchIOdevice ...
28
		auto& dev = ios.emplace_back(std::make_unique<MSXWatchIODevice>(
29
			*motherBoard.getMachineConfig(), *this));
30
		// ... and insert (prepend) in chain
31
		dev->getDevicePtr() = devices[port];
32
		devices[port] = dev.get();
33
	}
34
	registered = true;
35
}
36
37
void WatchPoint::unregisterIOWatch(std::span<MSXDevice*, 256> devices)
38
{
39
	if (!beginAddr || !endAddr || !isEnabled()) return;
40
	assert(getType() == one_of(Type::READ_IO, Type::WRITE_IO));
41
	assert(registered);
42
	assert(*beginAddr < 0x100 && *endAddr < 0x100 && *beginAddr <= *endAddr);
43
	for (unsigned port = *beginAddr; port <= *endAddr; ++port) {
44
		// find pointer to watchpoint
45
		MSXDevice** prev = &devices[port];
46
		while (*prev != ios[port - *beginAddr].get()) {
47
			prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
48
		}
49
		// remove watchpoint from chain
50
		*prev = checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr();
51
	}
52
	ios.clear();
53
	registered = false;
54
}
55
56
void WatchPoint::doReadCallback(MSXMotherBoard& motherBoard, unsigned port)
57
{
58
	auto& cpuInterface = motherBoard.getCPUInterface();
59
	if (cpuInterface.isFastForward()) return;
60
61
	auto& reactor = motherBoard.getReactor();
62
	auto& cliComm = reactor.getGlobalCliComm();
63
	auto& interp  = reactor.getInterpreter();
64
	interp.setVariable(TclObject("wp_last_address"), TclObject(int(port)));
65
66
	// keep this object alive by holding a shared_ptr to it, for the case
67
	// this watchpoint deletes itself in checkAndExecute()
68
	auto keepAlive = shared_from_this();
69
	auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
70
	if (bool remove = checkAndExecute(cliComm, interp); remove) {
71
		cpuInterface.removeWatchPoint(keepAlive);
72
	}
73
74
	interp.unsetVariable("wp_last_address");
75
}
76
77
void WatchPoint::doWriteCallback(MSXMotherBoard& motherBoard, unsigned port, unsigned value)
78
{
79
	auto& cpuInterface = motherBoard.getCPUInterface();
80
	if (cpuInterface.isFastForward()) return;
81
82
	auto& reactor = motherBoard.getReactor();
83
	auto& cliComm = reactor.getGlobalCliComm();
84
	auto& interp  = reactor.getInterpreter();
85
	interp.setVariable(TclObject("wp_last_address"), TclObject(int(port)));
86
	interp.setVariable(TclObject("wp_last_value"),   TclObject(int(value)));
87
88
	// see comment in doReadCallback() above
89
	auto keepAlive = shared_from_this();
90
	auto scopedBlock = motherBoard.getStateChangeDistributor().tempBlockNewEventsDuringReplay();
91
	if (bool remove = checkAndExecute(cliComm, interp); remove) {
92
		cpuInterface.removeWatchPoint(keepAlive);
93
	}
94
95
	interp.unsetVariable("wp_last_address");
96
	interp.unsetVariable("wp_last_value");
97
}
98
99
100
// class MSXWatchIODevice
101
102
MSXWatchIODevice::MSXWatchIODevice(
103
		HardwareConfig& hwConf, WatchPoint& wp_)
104
	: MSXMultiDevice(hwConf)
105
	, wp(wp_)
106
{
107
}
108
109
const std::string& MSXWatchIODevice::getName() const
110
{
111
	assert(device);
112
	return device->getName();
113
}
114
115
uint8_t MSXWatchIODevice::peekIO(uint16_t port, EmuTime time) const
116
{
117
	assert(device);
118
	return device->peekIO(port, time);
119
}
120
121
uint8_t MSXWatchIODevice::readIO(uint16_t port, EmuTime time)
122
{
123
	assert(device);
124
125
	// first trigger watchpoint, then read from device
126
	wp.doReadCallback(getMotherBoard(), port);
127
	return device->readIO(port, time);
128
}
129
130
void MSXWatchIODevice::writeIO(uint16_t port, uint8_t value, EmuTime time)
131
{
132
	assert(device);
133
134
	// first write to device, then trigger watchpoint
135
	device->writeIO(port, value, time);
136
	wp.doWriteCallback(getMotherBoard(), port, value);
137
}
138
139
} // namespace openmsx