OXYGENHUB
◆ main openmsx / src / input / Paddle.cc
2.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "Paddle.hh"
2
3
#include "MSXEventDistributor.hh"
4
#include "StateChange.hh"
5
#include "StateChangeDistributor.hh"
6
7
#include "Event.hh"
8
#include "serialize.hh"
9
#include "serialize_meta.hh"
10
11
#include <algorithm>
12
13
namespace openmsx {
14
15
Paddle::Paddle(MSXEventDistributor& eventDistributor_,
16
               StateChangeDistributor& stateChangeDistributor_)
17
	: eventDistributor(eventDistributor_)
18
	, stateChangeDistributor(stateChangeDistributor_)
19
{
20
}
21
22
Paddle::~Paddle()
23
{
24
	if (isPluggedIn()) {
25
		Paddle::unplugHelper(EmuTime::dummy());
26
	}
27
}
28
29
30
// Pluggable
31
zstring_view Paddle::getName() const
32
{
33
	return "paddle";
34
}
35
36
zstring_view Paddle::getDescription() const
37
{
38
	return "MSX Paddle";
39
}
40
41
void Paddle::plugHelper(Connector& /*connector*/, EmuTime /*time*/)
42
{
43
	eventDistributor.registerEventListener(*this);
44
	stateChangeDistributor.registerListener(*this);
45
}
46
47
void Paddle::unplugHelper(EmuTime /*time*/)
48
{
49
	stateChangeDistributor.unregisterListener(*this);
50
	eventDistributor.unregisterEventListener(*this);
51
}
52
53
// JoystickDevice
54
uint8_t Paddle::read(EmuTime time)
55
{
56
	// The loop in the BIOS routine that reads the paddle status takes
57
	// 41 Z80 cycles per iteration.
58
	static constexpr auto TICK = EmuDuration::hz(3579545) * 41;
59
60
	assert(time >= lastPulse);
61
	bool before = (time - lastPulse) < (TICK * analogValue);
62
	bool output = before && !(lastInput & 4);
63
	return output ? 0x3F : 0x3E; // pin1 (up)
64
}
65
66
void Paddle::write(uint8_t value, EmuTime time)
67
{
68
	uint8_t diff = lastInput ^ value;
69
	lastInput = value;
70
	if ((diff & 4) && !(lastInput & 4)) { // high->low edge
71
		lastPulse = time;
72
	}
73
}
74
75
// MSXEventListener
76
void Paddle::signalMSXEvent(const Event& event,
77
                            EmuTime time) noexcept
78
{
79
	visit(overloaded{
80
		[&](const MouseMotionEvent& e) {
81
			constexpr int SCALE = 2;
82
			if (int delta = e.getX() / SCALE) {
83
				stateChangeDistributor.distributeNew<PaddleState>(
84
					time, delta);
85
			}
86
		},
87
		[](const EventBase&) { /*ignore*/ }
88
	}, event);
89
}
90
91
// StateChangeListener
92
void Paddle::signalStateChange(const StateChange& event)
93
{
94
	const auto* ps = std::get_if<PaddleState>(&event);
95
	if (!ps) return;
96
	analogValue = narrow_cast<uint8_t>(std::clamp(analogValue + ps->getDelta(), 0, 255));
97
}
98
99
void Paddle::stopReplay(EmuTime /*time*/) noexcept
100
{
101
}
102
103
template<typename Archive>
104
void Paddle::serialize(Archive& ar, unsigned /*version*/)
105
{
106
	ar.serialize("lastPulse",   lastPulse,
107
	             "analogValue", analogValue,
108
	             "lastInput",   lastInput);
109
110
	if constexpr (Archive::IS_LOADER) {
111
		if (isPluggedIn()) {
112
			plugHelper(*getConnector(), EmuTime::dummy());
113
		}
114
	}
115
}
116
INSTANTIATE_SERIALIZE_METHODS(Paddle);
117
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, Paddle, "Paddle");
118
119
} // namespace openmsx