openMSX
Paddle.cc
Go to the documentation of this file.
1 #include "Paddle.hh"
2 #include "MSXEventDistributor.hh"
4 #include "Event.hh"
5 #include "StateChange.hh"
6 #include "serialize.hh"
7 #include "serialize_meta.hh"
8 #include <algorithm>
9 
10 namespace openmsx {
11 
12 class PaddleState final : public StateChange
13 {
14 public:
15  PaddleState() = default; // for serialize
16  PaddleState(EmuTime::param time_, int delta_)
17  : StateChange(time_), delta(delta_) {}
18  [[nodiscard]] int getDelta() const { return delta; }
19 
20  template<typename Archive> void serialize(Archive& ar, unsigned /*version*/)
21  {
22  ar.template serializeBase<StateChange>(*this);
23  ar.serialize("delta", delta);
24  }
25 private:
26  int delta;
27 };
29 
30 
32  StateChangeDistributor& stateChangeDistributor_)
33  : eventDistributor(eventDistributor_)
34  , stateChangeDistributor(stateChangeDistributor_)
35  , lastPulse(EmuTime::zero())
36  , analogValue(128)
37  , lastInput(0)
38 {
39 }
40 
42 {
43  if (isPluggedIn()) {
44  Paddle::unplugHelper(EmuTime::dummy());
45  }
46 }
47 
48 
49 // Pluggable
50 std::string_view Paddle::getName() const
51 {
52  return "paddle";
53 }
54 
55 std::string_view Paddle::getDescription() const
56 {
57  return "MSX Paddle";
58 }
59 
60 void Paddle::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/)
61 {
62  eventDistributor.registerEventListener(*this);
63  stateChangeDistributor.registerListener(*this);
64 }
65 
66 void Paddle::unplugHelper(EmuTime::param /*time*/)
67 {
68  stateChangeDistributor.unregisterListener(*this);
69  eventDistributor.unregisterEventListener(*this);
70 }
71 
72 // JoystickDevice
73 byte Paddle::read(EmuTime::param time)
74 {
75  // The loop in the BIOS routine that reads the paddle status takes
76  // 41 Z80 cycles per iteration.
77  static constexpr auto TICK = EmuDuration::hz(3579545) * 41;
78 
79  assert(time >= lastPulse);
80  bool before = (time - lastPulse) < (TICK * analogValue);
81  bool output = before && !(lastInput & 4);
82  return output ? 0x3F : 0x3E; // pin1 (up)
83 }
84 
85 void Paddle::write(byte value, EmuTime::param time)
86 {
87  byte diff = lastInput ^ value;
88  lastInput = value;
89  if ((diff & 4) && !(lastInput & 4)) { // high->low edge
90  lastPulse = time;
91  }
92 }
93 
94 // MSXEventListener
95 void Paddle::signalMSXEvent(const Event& event,
96  EmuTime::param time) noexcept
97 {
99  [&](const MouseMotionEvent& e) {
100  constexpr int SCALE = 2;
101  if (int delta = e.getX() / SCALE) {
102  stateChangeDistributor.distributeNew<PaddleState>(
103  time, delta);
104  }
105  },
106  [](const EventBase&) { /*ignore*/ }
107  }, event);
108 }
109 
110 // StateChangeListener
111 void Paddle::signalStateChange(const StateChange& event)
112 {
113  const auto* ps = dynamic_cast<const PaddleState*>(&event);
114  if (!ps) return;
115  analogValue = std::clamp(analogValue + ps->getDelta(), 0, 255);
116 }
117 
118 void Paddle::stopReplay(EmuTime::param /*time*/) noexcept
119 {
120 }
121 
122 template<typename Archive>
123 void Paddle::serialize(Archive& ar, unsigned /*version*/)
124 {
125  ar.serialize("lastPulse", lastPulse,
126  "analogValue", analogValue,
127  "lastInput", lastInput);
128 
129  if constexpr (Archive::IS_LOADER) {
130  if (isPluggedIn()) {
131  plugHelper(*getConnector(), EmuTime::dummy());
132  }
133  }
134 }
137 
138 } // namespace openmsx
static constexpr EmuDuration hz(unsigned x)
Definition: EmuDuration.hh:45
void registerEventListener(MSXEventListener &listener)
Registers a given object to receive certain events.
void unregisterEventListener(MSXEventListener &listener)
Unregisters a previously registered event listener.
void serialize(Archive &ar, unsigned)
Definition: Paddle.cc:20
PaddleState(EmuTime::param time_, int delta_)
Definition: Paddle.cc:16
int getDelta() const
Definition: Paddle.cc:18
Paddle(MSXEventDistributor &eventDistributor, StateChangeDistributor &stateChangeDistributor)
Definition: Paddle.cc:31
~Paddle() override
Definition: Paddle.cc:41
void serialize(Archive &ar, unsigned version)
Definition: Paddle.cc:123
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
Definition: Pluggable.hh:49
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
Definition: Pluggable.hh:43
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
Definition: StateChange.hh:20
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:296
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
auto visit(Visitor &&visitor, const Event &event)
Definition: Event.hh:653
constexpr int SCALE
Definition: ArkanoidPad.cc:24
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998