| 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 |
|
| 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 |
|
| 28 |
auto& dev = ios.emplace_back(std::make_unique<MSXWatchIODevice>( |
| 29 |
*motherBoard.getMachineConfig(), *this)); |
| 30 |
|
| 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 |
|
| 45 |
MSXDevice** prev = &devices[port]; |
| 46 |
while (*prev != ios[port - *beginAddr].get()) { |
| 47 |
prev = &checked_cast<MSXWatchIODevice*>(*prev)->getDevicePtr(); |
| 48 |
} |
| 49 |
|
| 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 |
|
| 67 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 135 |
device->writeIO(port, value, time); |
| 136 |
wp.doWriteCallback(getMotherBoard(), port, value); |
| 137 |
} |
| 138 |
|
| 139 |
} |