mirror of https://github.com/stella-emu/stella.git
Oops.
This commit is contained in:
parent
2c96258890
commit
5384ca95d2
|
@ -0,0 +1,204 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2017 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.
|
||||
//============================================================================
|
||||
|
||||
#include "YStartDetector.hxx"
|
||||
#include "TIAConstants.hxx"
|
||||
|
||||
/**
|
||||
* Misc. numeric constants used in the algorithm.
|
||||
*/
|
||||
enum Metrics: uInt32 {
|
||||
frameLinesNTSC = 262,
|
||||
frameLinesPAL = 312,
|
||||
vblankNTSC = 37,
|
||||
vblankPAL = 45,
|
||||
waitForVsync = 32,
|
||||
tvModeDetectionTolerance = 20,
|
||||
maxUnderscan = 10,
|
||||
maxVblankViolations = 2,
|
||||
minStableVblankFrames = 1,
|
||||
initialGarbageFrames = TIAConstants::initialGarbageFrames
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 YStartDetector::detectedYStart() const
|
||||
{
|
||||
return myLastVblankLines;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void YStartDetector::onReset()
|
||||
{
|
||||
myState = State::waitForVsyncStart;
|
||||
myVblankMode = VblankMode::floating;
|
||||
myLinesWaitingForVsyncToStart = 0;
|
||||
myCurrentVblankLines = 0;
|
||||
myLastVblankLines = 0;
|
||||
myVblankViolations = 0;
|
||||
myStableVblankFrames = 0;
|
||||
myVblankViolated = false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void YStartDetector::onSetVsync()
|
||||
{
|
||||
if (myVsync)
|
||||
setState(State::waitForVsyncEnd);
|
||||
else
|
||||
setState(State::waitForFrameStart);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void YStartDetector::onNextLine()
|
||||
{
|
||||
const uInt32 frameLines = layout() == FrameLayout::ntsc ? Metrics::frameLinesNTSC : Metrics::frameLinesPAL;
|
||||
|
||||
switch (myState) {
|
||||
case State::waitForVsyncStart:
|
||||
// We start counting the number of "lines spent while waiting for vsync start" from
|
||||
// the "ideal" frame size (corrected by the three scanlines spent in vsync).
|
||||
if (myCurrentFrameTotalLines > frameLines - 3 || myTotalFrames == 0)
|
||||
myLinesWaitingForVsyncToStart++;
|
||||
|
||||
if (myLinesWaitingForVsyncToStart > Metrics::waitForVsync) setState(State::waitForVsyncEnd);
|
||||
|
||||
break;
|
||||
|
||||
case State::waitForVsyncEnd:
|
||||
if (++myLinesWaitingForVsyncToStart > Metrics::waitForVsync) setState(State::waitForFrameStart);
|
||||
|
||||
break;
|
||||
|
||||
case State::waitForFrameStart:
|
||||
if (shouldTransitionToFrame()) setState(State::waitForVsyncStart);
|
||||
else myCurrentVblankLines++;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("cannot happen");
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool YStartDetector::shouldTransitionToFrame()
|
||||
{
|
||||
uInt32 vblankLines = layout() == FrameLayout::pal ? Metrics::vblankPAL : Metrics::vblankNTSC;
|
||||
|
||||
// Are we free to transition as per vblank cycle?
|
||||
bool shouldTransition = myCurrentVblankLines + 1 >= (myVblank ? vblankLines : vblankLines - Metrics::maxUnderscan);
|
||||
|
||||
// Do we **actually** transition? This depends on what mode we are in.
|
||||
bool transition = false;
|
||||
|
||||
switch (myVblankMode) {
|
||||
// Floating mode: we still are looking for a stable frame start
|
||||
case VblankMode::floating:
|
||||
|
||||
// Are we free to transition?
|
||||
if (shouldTransition) {
|
||||
// Is this same scanline in which the transition ocurred last frame?
|
||||
if (myTotalFrames > Metrics::initialGarbageFrames && myCurrentVblankLines == myLastVblankLines)
|
||||
// Yes? -> Increase the number of stable frames
|
||||
myStableVblankFrames++;
|
||||
else
|
||||
// No? -> Frame start shifted again, set the number of consecutive stable frames to zero
|
||||
myStableVblankFrames = 0;
|
||||
|
||||
// Save the transition point for checking on it next frame
|
||||
myLastVblankLines = myCurrentVblankLines;
|
||||
|
||||
// In floating mode, we transition whenever we can.
|
||||
transition = true;
|
||||
}
|
||||
|
||||
// Transition to locked mode if we saw enough stable frames in a row.
|
||||
if (myStableVblankFrames >= Metrics::minStableVblankFrames) {
|
||||
myVblankMode = VblankMode::locked;
|
||||
myVblankViolations = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Locked mode: always transition at the same point, but check whether this is actually the
|
||||
// detected transition point and revert state if applicable
|
||||
case VblankMode::locked:
|
||||
|
||||
// Have we reached the transition point?
|
||||
if (myCurrentVblankLines == myLastVblankLines) {
|
||||
|
||||
// Are we free to transition per the algorithm and didn't observe an violation before?
|
||||
// (aka did the algorithm tell us to transition before reaching the actual line)
|
||||
if (shouldTransition && !myVblankViolated)
|
||||
// Reset the number of irregular frames (if any)
|
||||
myVblankViolations = 0;
|
||||
else {
|
||||
// Record a violation if it wasn't recorded before
|
||||
if (!myVblankViolated) myVblankViolations++;
|
||||
myVblankViolated = true;
|
||||
}
|
||||
|
||||
// transition
|
||||
transition = true;
|
||||
// The algorithm tells us to transition although we haven't reached the trip line before
|
||||
} else if (shouldTransition) {
|
||||
// Record a violation if it wasn't recorded before
|
||||
if (!myVblankViolated) myVblankViolations++;
|
||||
myVblankViolated = true;
|
||||
}
|
||||
|
||||
// Revert to floating mode if there were too many irregular frames in a row
|
||||
if (myVblankViolations > Metrics::maxVblankViolations) {
|
||||
myVblankMode = VblankMode::floating;
|
||||
myStableVblankFrames = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("cannot happen");
|
||||
}
|
||||
|
||||
return transition;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void YStartDetector::setState(State state)
|
||||
{
|
||||
if (state == myState) return;
|
||||
|
||||
myState = state;
|
||||
myLinesWaitingForVsyncToStart = 0;
|
||||
|
||||
switch (state) {
|
||||
case State::waitForVsyncEnd:
|
||||
break;
|
||||
|
||||
case State::waitForVsyncStart:
|
||||
notifyFrameComplete();
|
||||
notifyFrameStart();
|
||||
break;
|
||||
|
||||
case State::waitForFrameStart:
|
||||
myVblankViolated = false;
|
||||
myCurrentVblankLines = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new runtime_error("cannot happen");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2017 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.
|
||||
//============================================================================
|
||||
|
||||
#ifndef TIA_YSTART_DETECTOR
|
||||
#define TIA_YSTART_DETECTOR
|
||||
|
||||
#include "AbstractFrameManager.hxx"
|
||||
|
||||
class YStartDetector: public AbstractFrameManager {
|
||||
|
||||
public:
|
||||
|
||||
YStartDetector() = default;
|
||||
|
||||
public:
|
||||
|
||||
uInt32 detectedYStart() const;
|
||||
|
||||
void setLayout(FrameLayout layout) override { this->layout(layout); }
|
||||
|
||||
protected:
|
||||
|
||||
void onSetVsync() override;
|
||||
|
||||
void onReset() override;
|
||||
|
||||
void onNextLine() override;
|
||||
|
||||
private:
|
||||
|
||||
enum State {
|
||||
waitForVsyncStart,
|
||||
waitForVsyncEnd,
|
||||
waitForFrameStart
|
||||
};
|
||||
|
||||
enum VblankMode {
|
||||
locked,
|
||||
floating
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
void setState(State state);
|
||||
|
||||
bool shouldTransitionToFrame();
|
||||
|
||||
private:
|
||||
|
||||
State myState;
|
||||
|
||||
VblankMode myVblankMode;
|
||||
|
||||
uInt32 myLinesWaitingForVsyncToStart;
|
||||
|
||||
uInt32 myCurrentVblankLines;
|
||||
uInt32 myLastVblankLines;
|
||||
uInt32 myVblankViolations;
|
||||
uInt32 myStableVblankFrames;
|
||||
|
||||
bool myVblankViolated;
|
||||
|
||||
private:
|
||||
|
||||
YStartDetector(const YStartDetector&) = delete;
|
||||
YStartDetector(YStartDetector&&) = delete;
|
||||
YStartDetector& operator=(const YStartDetector&) = delete;
|
||||
YStartDetector& operator=(YStartDetector&&) = delete;
|
||||
};
|
||||
|
||||
#endif // TIA_YSTART_DETECTOR
|
Loading…
Reference in New Issue