Properly model analog input.

This commit is contained in:
Christian Speckner 2021-04-05 15:46:30 +02:00
parent c7d3a43f58
commit 8aa14d8c75
19 changed files with 205 additions and 106 deletions

View File

@ -18,7 +18,7 @@
#ifndef STATE_MANAGER_HXX #ifndef STATE_MANAGER_HXX
#define STATE_MANAGER_HXX #define STATE_MANAGER_HXX
#define STATE_HEADER "06020100state" #define STATE_HEADER "06050300state"
class OSystem; class OSystem;
class RewindManager; class RewindManager;

View File

@ -93,9 +93,9 @@ void BoosterWidget::loadConfig()
myPins[kJFire]->setState(!getPin(ourPinNo[kJFire])); myPins[kJFire]->setState(!getPin(ourPinNo[kJFire]));
myPins[kJBooster]->setState( myPins[kJBooster]->setState(
getPin(Controller::AnalogPin::Five) == Controller::MIN_RESISTANCE); getPin(Controller::AnalogPin::Five) == AnalogReadout::connectToVcc());
myPins[kJTrigger]->setState( myPins[kJTrigger]->setState(
getPin(Controller::AnalogPin::Nine) == Controller::MIN_RESISTANCE); getPin(Controller::AnalogPin::Nine) == AnalogReadout::connectToVcc());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -115,13 +115,13 @@ void BoosterWidget::handleCommand(
break; break;
case kJBooster: case kJBooster:
setPin(Controller::AnalogPin::Five, setPin(Controller::AnalogPin::Five,
myPins[id]->getState() ? Controller::MIN_RESISTANCE : myPins[id]->getState() ? AnalogReadout::connectToVcc() :
Controller::MAX_RESISTANCE); AnalogReadout::disconnect());
break; break;
case kJTrigger: case kJTrigger:
setPin(Controller::AnalogPin::Nine, setPin(Controller::AnalogPin::Nine,
myPins[id]->getState() ? Controller::MIN_RESISTANCE : myPins[id]->getState() ? AnalogReadout::connectToVcc() :
Controller::MAX_RESISTANCE); AnalogReadout::disconnect());
break; break;
default: default:
break; break;

View File

@ -86,7 +86,7 @@ void GenesisWidget::loadConfig()
myPins[kJBbtn]->setState(!getPin(ourPinNo[kJBbtn])); myPins[kJBbtn]->setState(!getPin(ourPinNo[kJBbtn]));
myPins[kJCbtn]->setState( myPins[kJCbtn]->setState(
getPin(Controller::AnalogPin::Five) == Controller::MAX_RESISTANCE); getPin(Controller::AnalogPin::Five) == AnalogReadout::disconnect());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -106,8 +106,8 @@ void GenesisWidget::handleCommand(
break; break;
case kJCbtn: case kJCbtn:
setPin(Controller::AnalogPin::Five, setPin(Controller::AnalogPin::Five,
myPins[id]->getState() ? Controller::MAX_RESISTANCE : myPins[id]->getState() ? AnalogReadout::disconnect() :
Controller::MIN_RESISTANCE); AnalogReadout::connectToVcc());
break; break;
default: default:
break; break;

View File

@ -109,9 +109,9 @@ PaddleWidget::PaddleWidget(GuiObject* boss, const GUI::Font& font, int x, int y,
void PaddleWidget::loadConfig() void PaddleWidget::loadConfig()
{ {
myP0Resistance->setValue(Int32(Paddles::MAX_RESISTANCE - myP0Resistance->setValue(Int32(Paddles::MAX_RESISTANCE -
getPin(Controller::AnalogPin::Nine))); getPin(Controller::AnalogPin::Nine).resistance));
myP1Resistance->setValue(Int32(Paddles::MAX_RESISTANCE - myP1Resistance->setValue(Int32(Paddles::MAX_RESISTANCE -
getPin(Controller::AnalogPin::Five))); getPin(Controller::AnalogPin::Five).resistance));
myP0Fire->setState(!getPin(Controller::DigitalPin::Four)); myP0Fire->setState(!getPin(Controller::DigitalPin::Four));
myP1Fire->setState(!getPin(Controller::DigitalPin::Three)); myP1Fire->setState(!getPin(Controller::DigitalPin::Three));
} }
@ -124,11 +124,11 @@ void PaddleWidget::handleCommand(
{ {
case kP0Changed: case kP0Changed:
setPin(Controller::AnalogPin::Nine, setPin(Controller::AnalogPin::Nine,
static_cast<Int32>(Paddles::MAX_RESISTANCE - myP0Resistance->getValue())); AnalogReadout::connectToVcc(Paddles::MAX_RESISTANCE - myP0Resistance->getValue()));
break; break;
case kP1Changed: case kP1Changed:
setPin(Controller::AnalogPin::Five, setPin(Controller::AnalogPin::Five,
static_cast<Int32>(Paddles::MAX_RESISTANCE - myP1Resistance->getValue())); AnalogReadout::connectToVcc(Paddles::MAX_RESISTANCE - myP1Resistance->getValue()));
break; break;
case kP0Fire: case kP0Fire:
setPin(Controller::DigitalPin::Four, !myP0Fire->getState()); setPin(Controller::DigitalPin::Four, !myP0Fire->getState());

View File

@ -32,8 +32,8 @@ BoosterGrip::BoosterGrip(Jack jack, const Event& event, const System& system)
myBoosterEvent = Event::JoystickOneFire9; myBoosterEvent = Event::JoystickOneFire9;
} }
setPin(AnalogPin::Five, MAX_RESISTANCE); setPin(AnalogPin::Five, AnalogReadout::disconnect());
setPin(AnalogPin::Nine, MAX_RESISTANCE); setPin(AnalogPin::Nine, AnalogReadout::disconnect());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -48,6 +48,7 @@ void BoosterGrip::updateButtons()
updateMouseButtons(firePressed, boosterPressed); updateMouseButtons(firePressed, boosterPressed);
setPin(DigitalPin::Six, !getAutoFireState(firePressed)); setPin(DigitalPin::Six, !getAutoFireState(firePressed));
setPin(AnalogPin::Five, triggerPressed ? MIN_RESISTANCE : MAX_RESISTANCE); setPin(AnalogPin::Five, triggerPressed ? AnalogReadout::connectToVcc() : AnalogReadout::disconnect());
setPin(AnalogPin::Nine, boosterPressed ? MIN_RESISTANCE : MAX_RESISTANCE); setPin(AnalogPin::Nine, boosterPressed ? AnalogReadout::connectToVcc() : AnalogReadout::disconnect());
} }

View File

@ -31,10 +31,10 @@ CompuMate::CompuMate(const Console& console, const Event& event,
myLeftController = make_unique<CMControl>(*this, Controller::Jack::Left, event, system); myLeftController = make_unique<CMControl>(*this, Controller::Jack::Left, event, system);
myRightController = make_unique<CMControl>(*this, Controller::Jack::Right, event, system); myRightController = make_unique<CMControl>(*this, Controller::Jack::Right, event, system);
myLeftController->setPin(Controller::AnalogPin::Nine, Controller::MAX_RESISTANCE); myLeftController->setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToGround());
myLeftController->setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); myLeftController->setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
myRightController->setPin(Controller::AnalogPin::Nine, Controller::MIN_RESISTANCE); myRightController->setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToVcc());
myRightController->setPin(Controller::AnalogPin::Five, Controller::MAX_RESISTANCE); myRightController->setPin(Controller::AnalogPin::Five, AnalogReadout::connectToGround());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -44,17 +44,17 @@ void CompuMate::update()
Controller& lp = myConsole.leftController(); Controller& lp = myConsole.leftController();
Controller& rp = myConsole.rightController(); Controller& rp = myConsole.rightController();
lp.setPin(Controller::AnalogPin::Nine, Controller::MAX_RESISTANCE); lp.setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToGround());
lp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); lp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, true); lp.setPin(Controller::DigitalPin::Six, true);
rp.setPin(Controller::AnalogPin::Nine, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToVcc());
rp.setPin(Controller::AnalogPin::Five, Controller::MAX_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToGround());
rp.setPin(Controller::DigitalPin::Six, true); rp.setPin(Controller::DigitalPin::Six, true);
if (myEvent.get(Event::CompuMateShift)) if (myEvent.get(Event::CompuMateShift))
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
if (myEvent.get(Event::CompuMateFunc)) if (myEvent.get(Event::CompuMateFunc))
lp.setPin(Controller::AnalogPin::Nine, Controller::MIN_RESISTANCE); lp.setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToVcc());
rp.setPin(Controller::DigitalPin::Three, true); rp.setPin(Controller::DigitalPin::Three, true);
rp.setPin(Controller::DigitalPin::Four, true); rp.setPin(Controller::DigitalPin::Four, true);
@ -72,7 +72,7 @@ void CompuMate::update()
// Emulate the '?' character (Shift-6) with the actual question key // Emulate the '?' character (Shift-6) with the actual question key
if (myEvent.get(Event::CompuMateQuestion)) if (myEvent.get(Event::CompuMateQuestion))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateY)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateY)) rp.setPin(Controller::DigitalPin::Three, false);
@ -84,7 +84,7 @@ void CompuMate::update()
// Emulate the '[' character (Shift-8) with the actual key // Emulate the '[' character (Shift-8) with the actual key
if (myEvent.get(Event::CompuMateLeftBracket)) if (myEvent.get(Event::CompuMateLeftBracket))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateI)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateI)) rp.setPin(Controller::DigitalPin::Three, false);
@ -96,7 +96,7 @@ void CompuMate::update()
// Emulate the '-' character (Shift-2) with the actual minus key // Emulate the '-' character (Shift-2) with the actual minus key
if (myEvent.get(Event::CompuMateMinus)) if (myEvent.get(Event::CompuMateMinus))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateW)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateW)) rp.setPin(Controller::DigitalPin::Three, false);
@ -114,7 +114,7 @@ void CompuMate::update()
// Emulate the quote character (Shift-0) with the actual quote key // Emulate the quote character (Shift-0) with the actual quote key
if (myEvent.get(Event::CompuMateQuote)) if (myEvent.get(Event::CompuMateQuote))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateP)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateP)) rp.setPin(Controller::DigitalPin::Three, false);
@ -123,7 +123,7 @@ void CompuMate::update()
// Emulate Ctrl-space (aka backspace) with the actual Backspace key // Emulate Ctrl-space (aka backspace) with the actual Backspace key
if (myEvent.get(Event::CompuMateBackspace)) if (myEvent.get(Event::CompuMateBackspace))
{ {
lp.setPin(Controller::AnalogPin::Nine, Controller::MIN_RESISTANCE); lp.setPin(Controller::AnalogPin::Nine, AnalogReadout::connectToVcc());
rp.setPin(Controller::DigitalPin::Four, false); rp.setPin(Controller::DigitalPin::Four, false);
} }
break; break;
@ -132,7 +132,7 @@ void CompuMate::update()
// Emulate the ']' character (Shift-9) with the actual key // Emulate the ']' character (Shift-9) with the actual key
if (myEvent.get(Event::CompuMateRightBracket)) if (myEvent.get(Event::CompuMateRightBracket))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateO)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateO)) rp.setPin(Controller::DigitalPin::Three, false);
@ -144,7 +144,7 @@ void CompuMate::update()
// Emulate the '=' character (Shift-5) with the actual equals key // Emulate the '=' character (Shift-5) with the actual equals key
if (myEvent.get(Event::CompuMateEquals)) if (myEvent.get(Event::CompuMateEquals))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateT)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateT)) rp.setPin(Controller::DigitalPin::Three, false);
@ -156,7 +156,7 @@ void CompuMate::update()
// Emulate the '+' character (Shift-1) with the actual plus key (Shift-=) // Emulate the '+' character (Shift-1) with the actual plus key (Shift-=)
if (myEvent.get(Event::CompuMatePlus)) if (myEvent.get(Event::CompuMatePlus))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateQ)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateQ)) rp.setPin(Controller::DigitalPin::Three, false);
@ -168,7 +168,7 @@ void CompuMate::update()
// Emulate the '/' character (Shift-4) with the actual slash key // Emulate the '/' character (Shift-4) with the actual slash key
if (myEvent.get(Event::CompuMateSlash)) if (myEvent.get(Event::CompuMateSlash))
{ {
rp.setPin(Controller::AnalogPin::Five, Controller::MIN_RESISTANCE); rp.setPin(Controller::AnalogPin::Five, AnalogReadout::connectToVcc());
lp.setPin(Controller::DigitalPin::Six, false); lp.setPin(Controller::DigitalPin::Six, false);
} }
if (myEvent.get(Event::CompuMateR)) rp.setPin(Controller::DigitalPin::Three, false); if (myEvent.get(Event::CompuMateR)) rp.setPin(Controller::DigitalPin::Three, false);

View File

@ -48,7 +48,7 @@ bool Controller::read(DigitalPin pin)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 Controller::read(AnalogPin pin) AnalogReadout::Connection Controller::read(AnalogPin pin)
{ {
return getPin(pin); return getPin(pin);
} }
@ -66,8 +66,8 @@ bool Controller::save(Serializer& out) const
out.putBool(getPin(DigitalPin::Six)); out.putBool(getPin(DigitalPin::Six));
// Output the analog pins // Output the analog pins
out.putInt(getPin(AnalogPin::Five)); getPin(AnalogPin::Five).save(out);
out.putInt(getPin(AnalogPin::Nine)); getPin(AnalogPin::Nine).save(out);
} }
catch(...) catch(...)
{ {
@ -90,8 +90,8 @@ bool Controller::load(Serializer& in)
setPin(DigitalPin::Six, in.getBool()); setPin(DigitalPin::Six, in.getBool());
// Input the analog pins // Input the analog pins
setPin(AnalogPin::Five, in.getInt()); getPin(AnalogPin::Five).load(in);
setPin(AnalogPin::Nine, in.getInt()); getPin(AnalogPin::Nine).load(in);
} }
catch(...) catch(...)
{ {

View File

@ -27,6 +27,7 @@ class System;
#include "bspf.hxx" #include "bspf.hxx"
#include "Serializable.hxx" #include "Serializable.hxx"
#include "AnalogReadout.hxx"
/** /**
A controller is a device that plugs into either the left or right A controller is a device that plugs into either the left or right
@ -154,7 +155,7 @@ class Controller : public Serializable
@param pin The pin of the controller jack to read @param pin The pin of the controller jack to read
@return The resistance at the specified pin @return The resistance at the specified pin
*/ */
virtual Int32 read(AnalogPin pin); virtual AnalogReadout::Connection read(AnalogPin pin);
/** /**
Write the given value to the specified digital pin for this Write the given value to the specified digital pin for this
@ -280,13 +281,6 @@ class Controller : public Serializable
*/ */
static void setAutoFireRate(int rate, bool isNTSC = true); static void setAutoFireRate(int rate, bool isNTSC = true);
public:
/// Constant which represents maximum resistance for analog pins
static constexpr Int32 MAX_RESISTANCE = 0x7FFFFFFF;
/// Constant which represents minimum resistance for analog pins
static constexpr Int32 MIN_RESISTANCE = 0x00000000;
protected: protected:
/** /**
Derived classes *must* use these accessor/mutator methods. Derived classes *must* use these accessor/mutator methods.
@ -298,12 +292,12 @@ class Controller : public Serializable
inline bool getPin(DigitalPin pin) const { inline bool getPin(DigitalPin pin) const {
return myDigitalPinState[static_cast<int>(pin)]; return myDigitalPinState[static_cast<int>(pin)];
} }
inline void setPin(AnalogPin pin, Int32 value) { inline void setPin(AnalogPin pin, AnalogReadout::Connection value) {
myAnalogPinValue[static_cast<int>(pin)] = value; myAnalogPinValue[static_cast<int>(pin)] = value;
if(myOnAnalogPinUpdateCallback) if(myOnAnalogPinUpdateCallback)
myOnAnalogPinUpdateCallback(pin); myOnAnalogPinUpdateCallback(pin);
} }
inline Int32 getPin(AnalogPin pin) const { inline AnalogReadout::Connection getPin(AnalogPin pin) const {
return myAnalogPinValue[static_cast<int>(pin)]; return myAnalogPinValue[static_cast<int>(pin)];
} }
inline void resetDigitalPins() { inline void resetDigitalPins() {
@ -314,8 +308,8 @@ class Controller : public Serializable
setPin(DigitalPin::Six, true); setPin(DigitalPin::Six, true);
} }
inline void resetAnalogPins() { inline void resetAnalogPins() {
setPin(AnalogPin::Five, MAX_RESISTANCE); setPin(AnalogPin::Five, AnalogReadout::disconnect());
setPin(AnalogPin::Nine, MAX_RESISTANCE); setPin(AnalogPin::Nine, AnalogReadout::disconnect());
} }
/** /**
@ -384,7 +378,8 @@ class Controller : public Serializable
std::array<bool, 5> myDigitalPinState{true, true, true, true, true}; std::array<bool, 5> myDigitalPinState{true, true, true, true, true};
/// The analog value on each analog pin /// The analog value on each analog pin
std::array<Int32, 2> myAnalogPinValue{MAX_RESISTANCE, MAX_RESISTANCE}; std::array<AnalogReadout::Connection, 2>
myAnalogPinValue{AnalogReadout::disconnect(), AnalogReadout::disconnect()};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -44,10 +44,10 @@ class ControllerLowLevel
inline bool getPin(Controller::DigitalPin pin) const { inline bool getPin(Controller::DigitalPin pin) const {
return myController.getPin(pin); return myController.getPin(pin);
} }
inline void setPin(Controller::AnalogPin pin, Int32 value) { inline void setPin(Controller::AnalogPin pin, AnalogReadout::Connection value) {
myController.setPin(pin, value); myController.setPin(pin, value);
} }
inline Int32 getPin(Controller::AnalogPin pin) const { inline AnalogReadout::Connection getPin(Controller::AnalogPin pin) const {
return myController.getPin(pin); return myController.getPin(pin);
} }
inline void resetDigitalPins() { inline void resetDigitalPins() {

View File

@ -26,7 +26,7 @@ Genesis::Genesis(Jack jack, const Event& event, const System& system)
else else
myButtonCEvent = Event::JoystickOneFire5; myButtonCEvent = Event::JoystickOneFire5;
setPin(AnalogPin::Five, MIN_RESISTANCE); setPin(AnalogPin::Five, AnalogReadout::connectToVcc());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -41,5 +41,5 @@ void Genesis::updateButtons()
updateMouseButtons(firePressed, buttonCPressed); updateMouseButtons(firePressed, buttonCPressed);
setPin(DigitalPin::Six, !getAutoFireState(firePressed)); setPin(DigitalPin::Six, !getAutoFireState(firePressed));
setPin(AnalogPin::Five, buttonCPressed ? MAX_RESISTANCE : MIN_RESISTANCE); setPin(AnalogPin::Five, buttonCPressed ? AnalogReadout::disconnect() : AnalogReadout::connectToVcc());
} }

View File

@ -67,16 +67,16 @@ Keyboard::ColumnState Keyboard::processColumn(const Event::Type buttons[]) {
return ColumnState::notConnected; return ColumnState::notConnected;
} }
Int32 Keyboard::columnStateToAnalogSignal(ColumnState state) const { AnalogReadout::Connection Keyboard::columnStateToAnalogSignal(ColumnState state) const {
switch (state) { switch (state) {
case ColumnState::gnd: case ColumnState::gnd:
return MAX_RESISTANCE; return AnalogReadout::connectToGround();
case ColumnState::vcc: case ColumnState::vcc:
return 0; return AnalogReadout::connectToVcc();
case ColumnState::notConnected: case ColumnState::notConnected:
return INTERNAL_RESISTANCE; return AnalogReadout::connectToVcc(INTERNAL_RESISTANCE);
default: default:
throw runtime_error("unreachable"); throw runtime_error("unreachable");

View File

@ -70,7 +70,7 @@ class Keyboard : public Controller
private: private:
ColumnState processColumn(const Event::Type buttons[]); ColumnState processColumn(const Event::Type buttons[]);
Int32 columnStateToAnalogSignal(ColumnState state) const; AnalogReadout::Connection columnStateToAnalogSignal(ColumnState state) const;
private: private:
// Pre-compute the events we care about based on given port // Pre-compute the events we care about based on given port

View File

@ -27,8 +27,8 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
{ {
// We must start with minimum resistance; see commit // We must start with minimum resistance; see commit
// 38b452e1a047a0dca38c5bcce7c271d40f76736e for more information // 38b452e1a047a0dca38c5bcce7c271d40f76736e for more information
setPin(AnalogPin::Five, MIN_RESISTANCE); setPin(AnalogPin::Five, AnalogReadout::connectToVcc());
setPin(AnalogPin::Nine, MIN_RESISTANCE); setPin(AnalogPin::Nine, AnalogReadout::connectToVcc());
// The following logic reflects that mapping paddles to different // The following logic reflects that mapping paddles to different
// devices can be extremely complex // devices can be extremely complex
@ -181,12 +181,12 @@ void Paddles::update()
// Only change state if the charge has actually changed // Only change state if the charge has actually changed
if(myCharge[1] != myLastCharge[1]) if(myCharge[1] != myLastCharge[1])
{ {
setPin(AnalogPin::Five, Int32(MAX_RESISTANCE * (myCharge[1] / double(TRIGMAX)))); setPin(AnalogPin::Five, AnalogReadout::connectToVcc(MAX_RESISTANCE * (myCharge[1] / double(TRIGMAX))));
myLastCharge[1] = myCharge[1]; myLastCharge[1] = myCharge[1];
} }
if(myCharge[0] != myLastCharge[0]) if(myCharge[0] != myLastCharge[0])
{ {
setPin(AnalogPin::Nine, Int32(MAX_RESISTANCE * (myCharge[0] / double(TRIGMAX)))); setPin(AnalogPin::Nine, AnalogReadout::connectToVcc(MAX_RESISTANCE * (myCharge[0] / double(TRIGMAX))));
myLastCharge[0] = myCharge[0]; myLastCharge[0] = myCharge[0];
} }
} }
@ -235,7 +235,7 @@ bool Paddles::updateAnalogAxes()
if(abs(new_val - sa_xaxis) > 10) if(abs(new_val - sa_xaxis) > 10)
sa_xaxis = new_val; sa_xaxis = new_val;
setPin(AnalogPin::Nine, Int32(MAX_RESISTANCE * setPin(AnalogPin::Nine, AnalogReadout::connectToVcc(MAX_RESISTANCE *
(BSPF::clamp(32768 - Int32(Int32(sa_xaxis) * SENSITIVITY + XCENTER), 0, 65536) / 65536.0))); (BSPF::clamp(32768 - Int32(Int32(sa_xaxis) * SENSITIVITY + XCENTER), 0, 65536) / 65536.0)));
sa_changed = true; sa_changed = true;
} }
@ -250,7 +250,7 @@ bool Paddles::updateAnalogAxes()
if(abs(new_val - sa_yaxis) > 10) if(abs(new_val - sa_yaxis) > 10)
sa_yaxis = new_val; sa_yaxis = new_val;
setPin(AnalogPin::Five, Int32(MAX_RESISTANCE * setPin(AnalogPin::Five, AnalogReadout::connectToVcc(MAX_RESISTANCE *
(BSPF::clamp(32768 - Int32(Int32(sa_yaxis) * SENSITIVITY + YCENTER), 0, 65536) / 65536.0))); (BSPF::clamp(32768 - Int32(Int32(sa_yaxis) * SENSITIVITY + YCENTER), 0, 65536) / 65536.0)));
sa_changed = true; sa_changed = true;
} }

View File

@ -162,7 +162,8 @@ class Paddles : public Controller
*/ */
static void setDigitalPaddleRange(int range); static void setDigitalPaddleRange(int range);
static constexpr double MAX_RESISTANCE = 1000000.0; // The maximum value of the paddle pot = 1MOhm
static constexpr uInt32 MAX_RESISTANCE = 1000000;
private: private:
// Range of values over which digital and mouse movement is scaled // Range of values over which digital and mouse movement is scaled

View File

@ -61,8 +61,8 @@ QuadTari::QuadTari(Jack jack, const OSystem& osystem, const System& system,
mySecondController = addController(secondType, true); mySecondController = addController(secondType, true);
// QuadTari auto detection setting // QuadTari auto detection setting
setPin(AnalogPin::Five, MIN_RESISTANCE); setPin(AnalogPin::Five, AnalogReadout::connectToVcc());
setPin(AnalogPin::Nine, MAX_RESISTANCE); setPin(AnalogPin::Nine, AnalogReadout::connectToGround());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -31,7 +31,7 @@ void AnalogReadout::reset(uInt64 timestamp)
myU = 0; myU = 0;
myIsDumped = false; myIsDumped = false;
myValue = 0; myConnection = disconnect();
myTimestamp = timestamp; myTimestamp = timestamp;
setConsoleTiming(ConsoleTiming::ntsc); setConsoleTiming(ConsoleTiming::ntsc);
@ -64,14 +64,14 @@ uInt8 AnalogReadout::inpt(uInt64 timestamp)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AnalogReadout::update(double value, uInt64 timestamp, ConsoleTiming consoleTiming) void AnalogReadout::update(Connection connection, uInt64 timestamp, ConsoleTiming consoleTiming)
{ {
if (consoleTiming != myConsoleTiming) { if (consoleTiming != myConsoleTiming) {
setConsoleTiming(consoleTiming); setConsoleTiming(consoleTiming);
} }
if (value != myValue) { if (connection != myConnection) {
myValue = value; myConnection = connection;
updateCharge(timestamp); updateCharge(timestamp);
} }
@ -89,11 +89,28 @@ void AnalogReadout::setConsoleTiming(ConsoleTiming consoleTiming)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AnalogReadout::updateCharge(uInt64 timestamp) void AnalogReadout::updateCharge(uInt64 timestamp)
{ {
if (myValue >= 0 && !myIsDumped) if (myIsDumped) {
myU *= exp(-static_cast<double>(timestamp - myTimestamp) / R_DUMP / C / myClockFreq);
} else {
switch (myConnection.type) {
case ConnectionType::vcc:
myU = U_SUPP * (1 - (1 - myU / U_SUPP) * myU = U_SUPP * (1 - (1 - myU / U_SUPP) *
exp(-static_cast<double>(timestamp - myTimestamp) / (myValue * R_POT + R0) / C / myClockFreq)); exp(-static_cast<double>(timestamp - myTimestamp) / (myConnection.resistance + R0) / C / myClockFreq));
else
myU *= exp(-static_cast<double>(timestamp - myTimestamp) / (myIsDumped ? R_DUMP : R0) / C / myClockFreq); break;
case ConnectionType::ground:
myU *= exp(-static_cast<double>(timestamp - myTimestamp) / (myConnection.resistance + R0) / C / myClockFreq);
break;
case ConnectionType::disconnected:
break;
default:
throw runtime_error("unreachable");
}
}
myTimestamp = timestamp; myTimestamp = timestamp;
} }
@ -106,7 +123,7 @@ bool AnalogReadout::save(Serializer& out) const
out.putDouble(myUThresh); out.putDouble(myUThresh);
out.putDouble(myU); out.putDouble(myU);
out.putDouble(myValue); myConnection.save(out);
out.putLong(myTimestamp); out.putLong(myTimestamp);
out.putInt(int(myConsoleTiming)); out.putInt(int(myConsoleTiming));
@ -131,7 +148,7 @@ bool AnalogReadout::load(Serializer& in)
myUThresh = in.getDouble(); myUThresh = in.getDouble();
myU = in.getDouble(); myU = in.getDouble();
myValue = in.getDouble(); myConnection.load(in);
myTimestamp = in.getLong(); myTimestamp = in.getLong();
myConsoleTiming = ConsoleTiming(in.getInt()); myConsoleTiming = ConsoleTiming(in.getInt());
@ -147,3 +164,63 @@ bool AnalogReadout::load(Serializer& in)
return true; return true;
} }
AnalogReadout::Connection AnalogReadout::connectToGround(uInt32 resistance)
{
return Connection{ConnectionType::ground, resistance};
}
AnalogReadout::Connection AnalogReadout::connectToVcc(uInt32 resistance)
{
return Connection{ConnectionType::vcc, resistance};
}
AnalogReadout::Connection AnalogReadout::disconnect()
{
return Connection{ConnectionType::disconnected, 0};
}
bool AnalogReadout::Connection::save(Serializer& out) const
{
try
{
out.putInt(static_cast<uInt8>(type));
out.putInt(resistance);
}
catch(...)
{
cerr << "ERROR: AnalogReadout::Connection::save" << endl;
return false;
}
return true;
}
bool AnalogReadout::Connection::load(Serializer& in)
{
try
{
type = ConnectionType(in.getInt());
resistance = in.getInt();
}
catch(...)
{
cerr << "ERROR: AnalogReadout::Connection::load" << endl;
return false;
}
return true;
}
bool operator==(const AnalogReadout::Connection& c1, const AnalogReadout::Connection& c2)
{
if (c1.type == AnalogReadout::ConnectionType::disconnected)
return c2.type == AnalogReadout::ConnectionType::disconnected;
return c1.type == c2.type && c1.resistance == c2.resistance;
}
bool operator!=(const AnalogReadout::Connection& c1, const AnalogReadout::Connection& c2)
{
return !(c1 == c2);
}

View File

@ -26,10 +26,24 @@ class AnalogReadout : public Serializable
{ {
public: public:
AnalogReadout(); enum class ConnectionType : uInt8 {
ground = 0, vcc = 1, disconnected = 2
};
struct Connection {
ConnectionType type;
uInt32 resistance;
bool save(Serializer& out) const;
bool load(Serializer& in);
friend bool operator==(const AnalogReadout::Connection& c1, const AnalogReadout::Connection& c2);
};
public: public:
AnalogReadout();
void reset(uInt64 timestamp); void reset(uInt64 timestamp);
void vblank(uInt8 value, uInt64 timestamp); void vblank(uInt8 value, uInt64 timestamp);
@ -37,7 +51,7 @@ class AnalogReadout : public Serializable
uInt8 inpt(uInt64 timestamp); uInt8 inpt(uInt64 timestamp);
void update(double value, uInt64 timestamp, ConsoleTiming consoleTiming); void update(Connection connection, uInt64 timestamp, ConsoleTiming consoleTiming);
/** /**
Serializable methods (see that class for more information). Serializable methods (see that class for more information).
@ -45,6 +59,14 @@ class AnalogReadout : public Serializable
bool save(Serializer& out) const override; bool save(Serializer& out) const override;
bool load(Serializer& in) override; bool load(Serializer& in) override;
public:
static Connection connectToGround(uInt32 resistance = 0);
static Connection connectToVcc(uInt32 resistance = 0);
static Connection disconnect();
private: private:
void setConsoleTiming(ConsoleTiming timing); void setConsoleTiming(ConsoleTiming timing);
@ -56,7 +78,7 @@ class AnalogReadout : public Serializable
double myUThresh{0.0}; double myUThresh{0.0};
double myU{0.0}; double myU{0.0};
double myValue{0.0}; Connection myConnection{ConnectionType::disconnected, 0};
uInt64 myTimestamp{0}; uInt64 myTimestamp{0};
ConsoleTiming myConsoleTiming; ConsoleTiming myConsoleTiming;
@ -80,4 +102,7 @@ class AnalogReadout : public Serializable
AnalogReadout& operator=(AnalogReadout&&) = delete; AnalogReadout& operator=(AnalogReadout&&) = delete;
}; };
bool operator==(const AnalogReadout::Connection& c1, const AnalogReadout::Connection& c2);
bool operator!=(const AnalogReadout::Connection& c1, const AnalogReadout::Connection& c2);
#endif // TIA_ANALOG_READOUT #endif // TIA_ANALOG_READOUT

View File

@ -391,11 +391,11 @@ void TIA::bindToControllers()
switch (pin) { switch (pin) {
case Controller::AnalogPin::Five: case Controller::AnalogPin::Five:
updatePaddle(1); updateAnalogReadout(1);
break; break;
case Controller::AnalogPin::Nine: case Controller::AnalogPin::Nine:
updatePaddle(0); updateAnalogReadout(0);
break; break;
} }
} }
@ -407,18 +407,18 @@ void TIA::bindToControllers()
switch (pin) { switch (pin) {
case Controller::AnalogPin::Five: case Controller::AnalogPin::Five:
updatePaddle(3); updateAnalogReadout(3);
break; break;
case Controller::AnalogPin::Nine: case Controller::AnalogPin::Nine:
updatePaddle(2); updateAnalogReadout(2);
break; break;
} }
} }
); );
for (uInt8 i = 0; i < 4; ++i) for (uInt8 i = 0; i < 4; ++i)
updatePaddle(i); updateAnalogReadout(i);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -464,22 +464,22 @@ uInt8 TIA::peek(uInt16 address)
break; break;
case INPT0: case INPT0:
updatePaddle(0); updateAnalogReadout(0);
result = myAnalogReadouts[0].inpt(myTimestamp) & 0b10000000; result = myAnalogReadouts[0].inpt(myTimestamp) & 0b10000000;
break; break;
case INPT1: case INPT1:
updatePaddle(1); updateAnalogReadout(1);
result = myAnalogReadouts[1].inpt(myTimestamp) & 0b10000000; result = myAnalogReadouts[1].inpt(myTimestamp) & 0b10000000;
break; break;
case INPT2: case INPT2:
updatePaddle(2); updateAnalogReadout(2);
result = myAnalogReadouts[2].inpt(myTimestamp) & 0b10000000; result = myAnalogReadouts[2].inpt(myTimestamp) & 0b10000000;
break; break;
case INPT3: case INPT3:
updatePaddle(3); updateAnalogReadout(3);
result = myAnalogReadouts[3].inpt(myTimestamp) & 0b10000000; result = myAnalogReadouts[3].inpt(myTimestamp) & 0b10000000;
break; break;
@ -1804,32 +1804,32 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::updatePaddle(uInt8 idx) void TIA::updateAnalogReadout(uInt8 idx)
{ {
Int32 resistance; AnalogReadout::Connection connection;
switch (idx) { switch (idx) {
case 0: case 0:
resistance = myConsole.leftController().read(Controller::AnalogPin::Nine); connection = myConsole.leftController().read(Controller::AnalogPin::Nine);
break; break;
case 1: case 1:
resistance = myConsole.leftController().read(Controller::AnalogPin::Five); connection = myConsole.leftController().read(Controller::AnalogPin::Five);
break; break;
case 2: case 2:
resistance = myConsole.rightController().read(Controller::AnalogPin::Nine); connection = myConsole.rightController().read(Controller::AnalogPin::Nine);
break; break;
case 3: case 3:
resistance = myConsole.rightController().read(Controller::AnalogPin::Five); connection = myConsole.rightController().read(Controller::AnalogPin::Five);
break; break;
default: default:
throw runtime_error("invalid paddle index"); throw runtime_error("invalid analog input");
} }
myAnalogReadouts[idx].update( myAnalogReadouts[idx].update(
(resistance == Controller::MAX_RESISTANCE) ? -1 : (double(resistance) / Paddles::MAX_RESISTANCE), connection,
myTimestamp, myTimestamp,
myTimingProvider() myTimingProvider()
); );

View File

@ -663,9 +663,9 @@ class TIA : public Device
void delayedWrite(uInt8 address, uInt8 value); void delayedWrite(uInt8 address, uInt8 value);
/** /**
* Update all paddle readout circuits to the current controller state. * Update analog readout circuit to the current controller state.
*/ */
void updatePaddle(uInt8 idx); void updateAnalogReadout(uInt8 idx);
/** /**
* Get the target counter value during a RESx. This essentially depends on * Get the target counter value during a RESx. This essentially depends on