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