Adjust paddle parameters, improve keyboard controller emulation, emulate cap discharge,

This commit is contained in:
Christian Speckner 2021-04-02 00:15:13 +02:00
parent cfcbb5a3ff
commit a5cb517280
5 changed files with 60 additions and 51 deletions

View File

@ -54,44 +54,50 @@ Keyboard::Keyboard(Jack jack, const Event& event, const System& system)
}
}
Keyboard::ColumnState Keyboard::processColumn(const Event::Type buttons[]) {
constexpr DigitalPin signals[] =
{DigitalPin::One, DigitalPin::Two, DigitalPin::Three, DigitalPin::Four};
for (uInt8 i = 0; i < 4; i++)
if (myEvent.get(buttons[i]) && !getPin(signals[i])) return ColumnState::gnd;
for (uInt8 i = 0; i < 4; i++)
if (myEvent.get(buttons[i]) && getPin(signals[i])) return ColumnState::vcc;
return ColumnState::notConneccted;
}
Int32 Keyboard::columnStateToAnalogSignal(ColumnState state) const {
switch (state) {
case ColumnState::gnd:
return MAX_RESISTANCE;
case ColumnState::vcc:
return 0;
case ColumnState::notConneccted:
return INTERNAL_RESISTANCE;
default:
throw runtime_error("unreachable");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Keyboard::write(DigitalPin pin, bool value)
{
setPin(pin, value);
// Set defaults
setPin(DigitalPin::Six, true);
Int32 resistanceFive = MIN_RESISTANCE;
Int32 resistanceNine = MIN_RESISTANCE;
const Event::Type col0[] = {myOneEvent, myFourEvent, mySevenEvent, myStarEvent};
const Event::Type col1[] = {myTwoEvent, myFiveEvent, myEightEvent, myZeroEvent};
const Event::Type col2[] = {myThreeEvent, mySixEvent, myNineEvent, myPoundEvent};
// Now scan the rows and columns
if(!getPin(DigitalPin::Four))
{
setPin(DigitalPin::Six, myEvent.get(myPoundEvent) == 0);
if(myEvent.get(myZeroEvent) != 0) resistanceFive = MAX_RESISTANCE;
if(myEvent.get(myStarEvent) != 0) resistanceNine = MAX_RESISTANCE;
}
if(!getPin(DigitalPin::Three))
{
setPin(DigitalPin::Six, myEvent.get(myNineEvent) == 0);
if(myEvent.get(myEightEvent) != 0) resistanceFive = MAX_RESISTANCE;
if(myEvent.get(mySevenEvent) != 0) resistanceNine = MAX_RESISTANCE;
}
if(!getPin(DigitalPin::Two))
{
setPin(DigitalPin::Six, myEvent.get(mySixEvent) == 0);
if(myEvent.get(myFiveEvent) != 0) resistanceFive = MAX_RESISTANCE;
if(myEvent.get(myFourEvent) != 0) resistanceNine = MAX_RESISTANCE;
}
if(!getPin(DigitalPin::One))
{
setPin(DigitalPin::Six, myEvent.get(myThreeEvent) == 0);
if(myEvent.get(myTwoEvent) != 0) resistanceFive = MAX_RESISTANCE;
if(myEvent.get(myOneEvent) != 0) resistanceNine = MAX_RESISTANCE;
}
ColumnState stateCol0 = processColumn(col0);
ColumnState stateCol1 = processColumn(col1);
ColumnState stateCol2 = processColumn(col2);
if(resistanceFive != read(AnalogPin::Five))
setPin(AnalogPin::Five, resistanceFive);
if(resistanceNine != read(AnalogPin::Nine))
setPin(AnalogPin::Nine, resistanceNine);
setPin(DigitalPin::Six, stateCol2 == ColumnState::gnd ? 0 : 1);
setPin(AnalogPin::Five, columnStateToAnalogSignal(stateCol1));
setPin(AnalogPin::Nine, columnStateToAnalogSignal(stateCol0));
}

View File

@ -62,6 +62,16 @@ class Keyboard : public Controller
*/
string name() const override { return "Keyboard"; }
private:
enum class ColumnState {
vcc, gnd, notConneccted
};
private:
ColumnState processColumn(const Event::Type buttons[]);
Int32 columnStateToAnalogSignal(ColumnState state) const;
private:
// Pre-compute the events we care about based on given port
// This will eliminate test for left or right port in update()
@ -70,7 +80,7 @@ class Keyboard : public Controller
mySevenEvent, myEightEvent, myNineEvent,
myStarEvent, myZeroEvent, myPoundEvent;
static constexpr Int32 MIN_RESISTANCE = 5600;
static constexpr Int32 INTERNAL_RESISTANCE = 4700;
private:
// Following constructors and assignment operators not supported

View File

@ -162,7 +162,7 @@ class Paddles : public Controller
*/
static void setDigitalPaddleRange(int range);
static constexpr double MAX_RESISTANCE = 1400000.0;
static constexpr double MAX_RESISTANCE = 1000000.0;
private:
// Range of values over which digital and mouse movement is scaled

View File

@ -40,16 +40,17 @@ void PaddleReader::reset(uInt64 timestamp)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PaddleReader::vblank(uInt8 value, uInt64 timestamp)
{
updateCharge(timestamp);
bool oldIsDumped = myIsDumped;
if (value & 0x80) {
myIsDumped = true;
myU = 0;
myTimestamp = timestamp;
} else if (oldIsDumped) {
myIsDumped = false;
myTimestamp = timestamp;
}
myTimestamp = timestamp;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -72,15 +73,7 @@ void PaddleReader::update(double value, uInt64 timestamp, ConsoleTiming consoleT
if (value != myValue) {
myValue = value;
if (myValue < 0) {
// value < 0 signifies either maximum resistance OR analog input connected to
// ground (keyboard controllers). As we have no way to tell these apart we just
// assume ground and discharge.
myU = 0;
myTimestamp = timestamp;
} else {
updateCharge(timestamp);
}
updateCharge(timestamp);
}
}
@ -96,11 +89,11 @@ void PaddleReader::setConsoleTiming(ConsoleTiming consoleTiming)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PaddleReader::updateCharge(uInt64 timestamp)
{
if (myIsDumped) return;
if (myValue >= 0)
if (myValue >= 0 && !myIsDumped)
myU = USUPP * (1 - (1 - myU / USUPP) *
exp(-static_cast<double>(timestamp - myTimestamp) / (myValue * RPOT + R0) / C / myClockFreq));
else
myU *= exp(-static_cast<double>(timestamp - myTimestamp) / R0 / C / myClockFreq);
myTimestamp = timestamp;
}

View File

@ -65,7 +65,7 @@ class PaddleReader : public Serializable
bool myIsDumped{false};
static constexpr double
R0 = 1.5e3,
R0 = 1.8e3,
C = 68e-9,
RPOT = 1e6,
USUPP = 5;