diff --git a/src/emucore/tia/core_6502ts/FrameManager.cxx b/src/emucore/tia/core_6502ts/FrameManager.cxx index 1b6787607..669619d27 100644 --- a/src/emucore/tia/core_6502ts/FrameManager.cxx +++ b/src/emucore/tia/core_6502ts/FrameManager.cxx @@ -159,7 +159,7 @@ bool FrameManager::isRendering() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -FrameManager::TvMode FrameManager::tvMode() const +TvMode FrameManager::tvMode() const { return myMode; } @@ -183,7 +183,7 @@ uInt32 FrameManager::currentLine() const } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void FrameManager::setTvMode(FrameManager::TvMode mode) +void FrameManager::setTvMode(TvMode mode) { if (mode == myMode) return; diff --git a/src/emucore/tia/core_6502ts/FrameManager.hxx b/src/emucore/tia/core_6502ts/FrameManager.hxx index def0277d8..0aa96d2f0 100644 --- a/src/emucore/tia/core_6502ts/FrameManager.hxx +++ b/src/emucore/tia/core_6502ts/FrameManager.hxx @@ -23,6 +23,7 @@ #include #include "Serializable.hxx" +#include "Types.hxx" #include "bspf.hxx" namespace TIA6502tsCore { @@ -31,10 +32,6 @@ class FrameManager : public Serializable { public: - enum TvMode { - pal, ntsc - }; - using callback = std::function; public: diff --git a/src/emucore/tia/core_6502ts/PaddleReader.cxx b/src/emucore/tia/core_6502ts/PaddleReader.cxx new file mode 100644 index 000000000..f9453bcf1 --- /dev/null +++ b/src/emucore/tia/core_6502ts/PaddleReader.cxx @@ -0,0 +1,120 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2016 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id$ +//============================================================================ + +#include + +#include "PaddleReader.hxx" + + +static double expApprox(double x) +{ + double x2 = x * x / 2, + x3 = x2 * x / 3, + x4 = x3 * x / 4; + + return 1 + x + x2 + x3 + x4; +} + +static constexpr double + C = 68e-9, + RPOT = 1e6, + R0 = 1.8e3, + USUPP = 5; + +static constexpr double TRIPPOINT_LINES = 380; + +namespace TIA6502tsCore { + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PaddleReader::PaddleReader() +{ + reset(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PaddleReader::reset(double timestamp) +{ + myU = 0; + myIsDumped = false; + + myValue = 0; + myTimestamp = timestamp; + + setTvMode(TvMode::ntsc); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PaddleReader::vblank(uInt8 value, double timestamp) +{ + bool oldIsDumped = myIsDumped; + + if (value & 0x80) { + myIsDumped = true; + myU = 0; + myTimestamp = timestamp; + } else if (oldIsDumped) { + myIsDumped = false; + myTimestamp = timestamp; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 PaddleReader::inpt(double timestamp) +{ + updateCharge(timestamp); + + bool state = myIsDumped ? false : myU > myUThresh; + + return state ? 0x80 : 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PaddleReader::update(double value, double timestamp, TvMode tvMode) +{ + if (tvMode != myTvMode) { + setTvMode(tvMode); + } + + if (value != myValue) { + myValue = value; + updateCharge(timestamp); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PaddleReader::setTvMode(TvMode mode) +{ + myTvMode = mode; + + myClockFreq = myTvMode == TvMode::ntsc ? 60 * 228 * 262 : 50 * 228 * 312; + myUThresh = USUPP * (1. - exp(-TRIPPOINT_LINES * 228 / myClockFreq / (RPOT + R0) / C)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PaddleReader::updateCharge(double timestamp) +{ + if (myIsDumped) return; + + myU = USUPP * (1 - (1 - myU / USUPP) * + expApprox(-(timestamp - myTimestamp) / (myValue * RPOT + R0) / C / myClockFreq)); + + myTimestamp = timestamp; +} + +} // namespace TIA6502tsCore \ No newline at end of file diff --git a/src/emucore/tia/core_6502ts/PaddleReader.hxx b/src/emucore/tia/core_6502ts/PaddleReader.hxx new file mode 100644 index 000000000..6c8c4fe2e --- /dev/null +++ b/src/emucore/tia/core_6502ts/PaddleReader.hxx @@ -0,0 +1,73 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2016 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id$ +//============================================================================ + +#ifndef TIA_6502TS_CORE_PADDLE_READER +#define TIA_6502TS_CORE_PADDLE_READER + +#include "bspf.hxx" +#include "Types.hxx" + +namespace TIA6502tsCore { + +class PaddleReader +{ + public: + + PaddleReader(); + + public: + + void reset(double timestamp); + + void vblank(uInt8 value, double timestamp); + + uInt8 inpt(double timestamp); + + void update(double value, double timestamp, TvMode tvMode); + + private: + + void setTvMode(TvMode mode); + + void updateCharge(double timestamp); + + private: + + double myUThresh; + double myU; + + double myValue; + double myTimestamp; + + TvMode myTvMode; + double myClockFreq; + + bool myIsDumped; + + private: + + PaddleReader(const PaddleReader&) = delete; + PaddleReader(PaddleReader&&) = delete; + PaddleReader& operator=(const PaddleReader&) = delete; + PaddleReader& operator=(PaddleReader&&) = delete; +}; + +} // namespace TIA6502tsCore + +#endif // TIA_6502TS_CORE_PADDLE_READER \ No newline at end of file diff --git a/src/emucore/tia/core_6502ts/TIA.cxx b/src/emucore/tia/core_6502ts/TIA.cxx index 9fb901156..4b50b849b 100644 --- a/src/emucore/tia/core_6502ts/TIA.cxx +++ b/src/emucore/tia/core_6502ts/TIA.cxx @@ -21,6 +21,7 @@ #include "TIATypes.hxx" #include "M6502.hxx" #include "Console.hxx" +#include "Types.hxx" enum CollisionMask: uInt32 { player0 = 0b0111110000000000, @@ -65,6 +66,7 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings) myFrameManager.setHandlers( [this] () { myCurrentFrameBuffer.swap(myPreviousFrameBuffer); + updatePaddles(); }, [this] () { mySystem->m6502().stop(); @@ -108,6 +110,10 @@ void TIA::reset() myInput0.reset(); myInput1.reset(); + myTimestamp = 0; + for (PaddleReader& paddleReader : myPaddleReaders) + paddleReader.reset(myTimestamp); + mySound.reset(); myDelayQueue.reset(); myFrameManager.reset(); @@ -267,6 +273,22 @@ uInt8 TIA::peek(uInt16 address) result = (myCollisionMask & CollisionMask::ball & CollisionMask::playfield) ? 0x80 : 0; break; + case INPT0: + result = myPaddleReaders[0].inpt(myTimestamp); + break; + + case INPT1: + result = myPaddleReaders[1].inpt(myTimestamp); + break; + + case INPT2: + result = myPaddleReaders[2].inpt(myTimestamp); + break; + + case INPT3: + result = myPaddleReaders[3].inpt(myTimestamp); + break; + case INPT4: result = myInput0.inpt(!myConsole.leftController().read(Controller::Six)); break; @@ -308,6 +330,10 @@ bool TIA::poke(uInt16 address, uInt8 value) myInput1.vblank(value); myFrameManager.setVblank(value & 0x02); + + for (PaddleReader& paddleReader : myPaddleReaders) + paddleReader.vblank(value, myTimestamp); + break; case AUDV0: @@ -582,7 +608,7 @@ void TIA::enableColorLoss(bool enabled) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool TIA::isPAL() const { - return myFrameManager.tvMode() == FrameManager::TvMode::pal; + return myFrameManager.tvMode() == TvMode::pal; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -721,6 +747,8 @@ void TIA::cycle(uInt32 colorClocks) tickHframe(); if (myCollisionUpdateRequired) updateCollision(); + + myTimestamp++; } } @@ -985,4 +1013,21 @@ void TIA::delayedWrite(uInt8 address, uInt8 value) } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::updatePaddles() +{ + static constexpr double MAX_RESISTANCE = 1400000; + + TvMode tvMode = myFrameManager.tvMode(); + Int32 resistances[] = { + myConsole.leftController().read(Controller::Nine), + myConsole.leftController().read(Controller::Five), + myConsole.rightController().read(Controller::Nine), + myConsole.rightController().read(Controller::Five), + }; + + for (uInt8 i = 0; i < 4; i++) + myPaddleReaders[i].update(double(resistances[i]) / MAX_RESISTANCE, myTimestamp, tvMode); +} + } // namespace TIA6502tsCore diff --git a/src/emucore/tia/core_6502ts/TIA.hxx b/src/emucore/tia/core_6502ts/TIA.hxx index 8998756e0..5311f360b 100644 --- a/src/emucore/tia/core_6502ts/TIA.hxx +++ b/src/emucore/tia/core_6502ts/TIA.hxx @@ -32,6 +32,7 @@ #include "Player.hxx" #include "Ball.hxx" #include "LatchedInput.hxx" +#include "PaddleReader.hxx" class Console; @@ -176,6 +177,8 @@ class TIA : public AbstractTIA void delayedWrite(uInt8 address, uInt8 value); + void updatePaddles(); + private: Console& myConsole; @@ -209,6 +212,8 @@ class TIA : public AbstractTIA uInt8 myColorBk; + double myTimestamp; + BytePtr myCurrentFrameBuffer; BytePtr myPreviousFrameBuffer; @@ -218,6 +223,7 @@ class TIA : public AbstractTIA Player myPlayer0; Player myPlayer1; Ball myBall; + PaddleReader myPaddleReaders[4]; LatchedInput myInput0; LatchedInput myInput1; diff --git a/src/emucore/tia/core_6502ts/module.mk b/src/emucore/tia/core_6502ts/module.mk index 4e37e06a7..2e061d241 100644 --- a/src/emucore/tia/core_6502ts/module.mk +++ b/src/emucore/tia/core_6502ts/module.mk @@ -10,7 +10,8 @@ MODULE_OBJS := \ src/emucore/tia/core_6502ts/Missile.o \ src/emucore/tia/core_6502ts/Player.o \ src/emucore/tia/core_6502ts/Ball.o \ - src/emucore/tia/core_6502ts/LatchedInput.o + src/emucore/tia/core_6502ts/LatchedInput.o \ + src/emucore/tia/core_6502ts/PaddleReader.o MODULE_DIRS += \