Merge branch 'refactoring/frame_manager'

This commit is contained in:
Christian Speckner 2017-10-15 21:12:14 +02:00
commit 2c96258890
30 changed files with 1637 additions and 1242 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ project.xcworkspace/
xcuserdata/
build/
src/macosx/M6502.ins
*.dSYM

View File

@ -14,6 +14,7 @@
"${workspaceRoot}/src/gui",
"${workspaceRoot}/src/emucore",
"${workspaceRoot}/src/emucore/tia",
"${workspaceRoot}/src/emucore/tia/frame-manager",
"${workspaceRoot}/src/unix",
"${workspaceRoot}/src/debugger",
"${workspaceRoot}/src/debugger/gui",

View File

@ -12,5 +12,9 @@
"editor.trimAutoWhitespace": true,
"editor.useTabStops": false,
"C_Cpp.intelliSenseEngine": "Default",
"files.insertFinalNewline": true
"files.insertFinalNewline": true,
"files.associations": {
"locale": "cpp",
"string": "cpp"
}
}

View File

@ -98,6 +98,7 @@ MODULES := $(MODULES)
MODULES += \
src/emucore \
src/emucore/tia \
src/emucore/tia/frame-manager \
src/gui \
src/common \
src/common/tv_filters

3
configure vendored
View File

@ -691,6 +691,7 @@ SRC="src"
CORE="$SRC/emucore"
COMMON="$SRC/common"
TIA="$SRC/emucore/tia"
TIA_FRAME_MANAGER="$SRC/emucore/tia/frame-manager"
TV="$SRC/common/tv_filters"
GUI="$SRC/gui"
DBG="$SRC/debugger"
@ -700,7 +701,7 @@ CHEAT="$SRC/cheat"
LIBPNG="$SRC/libpng"
ZLIB="$SRC/zlib"
INCLUDES="-I$CORE -I$COMMON -I$TV -I$GUI -I$TIA"
INCLUDES="-I$CORE -I$COMMON -I$TV -I$GUI -I$TIA -I$TIA_FRAME_MANAGER"
INCLUDES="$INCLUDES `$_sdlconfig --cflags`"
if test "$_build_static" = yes ; then

View File

@ -48,7 +48,7 @@ const DebuggerState& TIADebug::getState()
myState.coluRegs.push_back(coluBK());
// Debug Colors
int mode = myTIA.myFrameManager.layout() == FrameLayout::ntsc ? 0 : 1;
int mode = myTIA.frameLayout() == FrameLayout::ntsc ? 0 : 1;
myState.fixedCols.clear();
myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P0]);
myState.fixedCols.push_back(myTIA.myFixedColorPalette[mode][TIA::P1]);
@ -721,7 +721,7 @@ int TIADebug::scanlines() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int TIADebug::scanlinesLastFrame() const
{
return myTIA.myFrameManager.scanlinesLastFrame();
return myTIA.scanlinesLastFrame();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -807,7 +807,7 @@ string TIADebug::debugColors() const
{
ostringstream buf;
int mode = myTIA.myFrameManager.layout() == FrameLayout::ntsc ? 0 : 1;
int mode = myTIA.frameLayout() == FrameLayout::ntsc ? 0 : 1;
buf << " Red " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::P0])
<< " Player 0\n"
<< " Orange " << colorSwatch(myTIA.myFixedColorPalette[mode][TIA::M0])

View File

@ -55,8 +55,11 @@
#include "CommandMenu.hxx"
#include "Serializable.hxx"
#include "Version.hxx"
#include "FrameManager.hxx"
#include "TIAConstants.hxx"
#include "FrameLayout.hxx"
#include "frame-manager/FrameManager.hxx"
#include "frame-manager/FrameLayoutDetector.hxx"
#include "frame-manager/YStartDetector.hxx"
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
@ -77,7 +80,8 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
myCart(std::move(cart)),
myDisplayFormat(""), // Unknown TV format @ start
myFramerate(0.0), // Unknown framerate @ start
myCurrentFormat(0), // Unknown format @ start
myCurrentFormat(0), // Unknown format @ start,
myAutodetectedYstart(0),
myUserPaletteDefined(false),
myConsoleTiming(ConsoleTiming::ntsc)
{
@ -88,8 +92,11 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
my6502 = make_unique<M6502>(myOSystem.settings());
myRiot = make_unique<M6532>(*this, myOSystem.settings());
myTIA = make_unique<TIA>(*this, myOSystem.sound(), myOSystem.settings());
myFrameManager = make_unique<FrameManager>();
mySwitches = make_unique<Switches>(myEvent, myProperties);
myTIA->setFrameManager(myFrameManager.get());
// Construct the system and components
mySystem = make_unique<System>(osystem, *my6502, *myRiot, *myTIA, *myCart);
@ -114,36 +121,19 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
if(myDisplayFormat == "AUTO" || myOSystem.settings().getBool("rominfo"))
{
// Run the TIA, looking for PAL scanline patterns
// We turn off the SuperCharger progress bars, otherwise the SC BIOS
// will take over 250 frames!
// The 'fastscbios' option must be changed before the system is reset
bool fastscbios = myOSystem.settings().getBool("fastscbios");
myOSystem.settings().setValue("fastscbios", true);
autodetectFrameLayout();
uInt8 initialGarbageFrames = FrameManager::initialGarbageFrames();
uInt8 linesPAL = 0;
uInt8 linesNTSC = 0;
mySystem->reset(true); // autodetect in reset enabled
myTIA->autodetectLayout(true);
for(int i = 0; i < 60; ++i) {
if (i > initialGarbageFrames)
myTIA->frameLayout() == FrameLayout::pal ? linesPAL++ : linesNTSC++;
myTIA->update();
}
myDisplayFormat = linesPAL > linesNTSC ? "PAL" : "NTSC";
if(myProperties.get(Display_Format) == "AUTO")
{
autodetected = "*";
myCurrentFormat = 0;
}
// Don't forget to reset the SC progress bars again
myOSystem.settings().setValue("fastscbios", fastscbios);
}
if (atoi(myProperties.get(Display_YStart).c_str()) == 0) {
autodetectYStart();
}
myConsoleInfo.DisplayFormat = myDisplayFormat + autodetected;
// Set up the correct properties used when toggling format
@ -218,6 +208,54 @@ Console::~Console()
myRightControl->close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::autodetectFrameLayout()
{
// Run the TIA, looking for PAL scanline patterns
// We turn off the SuperCharger progress bars, otherwise the SC BIOS
// will take over 250 frames!
// The 'fastscbios' option must be changed before the system is reset
bool fastscbios = myOSystem.settings().getBool("fastscbios");
myOSystem.settings().setValue("fastscbios", true);
FrameLayoutDetector frameLayoutDetector;
myTIA->setFrameManager(&frameLayoutDetector);
mySystem->reset(true);
for(int i = 0; i < 60; ++i) myTIA->update();
myTIA->setFrameManager(myFrameManager.get());
myDisplayFormat = frameLayoutDetector.detectedLayout() == FrameLayout::pal ? "PAL" : "NTSC";
// Don't forget to reset the SC progress bars again
myOSystem.settings().setValue("fastscbios", fastscbios);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::autodetectYStart()
{
// We turn off the SuperCharger progress bars, otherwise the SC BIOS
// will take over 250 frames!
// The 'fastscbios' option must be changed before the system is reset
bool fastscbios = myOSystem.settings().getBool("fastscbios");
myOSystem.settings().setValue("fastscbios", true);
YStartDetector ystartDetector;
ystartDetector.setLayout(myDisplayFormat == "PAL" ? FrameLayout::pal : FrameLayout::ntsc);
myTIA->setFrameManager(&ystartDetector);
mySystem->reset();
for (int i = 0; i < 80; i++) myTIA->update();
myTIA->setFrameManager(myFrameManager.get());
myAutodetectedYstart = ystartDetector.detectedYStart();
// Don't forget to reset the SC progress bars again
myOSystem.settings().setValue("fastscbios", fastscbios);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Console::save(Serializer& out) const
{
@ -570,7 +608,7 @@ void Console::changeYStart(int direction)
if(direction == +1) // increase YStart
{
if(ystart >= FrameManager::maxYStart)
if(ystart >= TIAConstants::maxYStart)
{
myOSystem.frameBuffer().showMessage("YStart at maximum");
return;
@ -579,11 +617,17 @@ void Console::changeYStart(int direction)
}
else if(direction == -1) // decrease YStart
{
if(ystart == FrameManager::minYStart-1)
if (ystart == TIAConstants::minYStart && myAutodetectedYstart == 0) {
myOSystem.frameBuffer().showMessage("Autodetected YStart not available");
return;
}
if(ystart == TIAConstants::minYStart-1 && myAutodetectedYstart > 0)
{
myOSystem.frameBuffer().showMessage("YStart at minimum");
return;
}
ystart--;
}
else
@ -591,16 +635,16 @@ void Console::changeYStart(int direction)
ostringstream val;
val << ystart;
if(ystart == FrameManager::minYStart-1)
if(ystart == TIAConstants::minYStart-1)
myOSystem.frameBuffer().showMessage("YStart autodetected");
else
{
if(myTIA->ystartIsAuto(ystart))
if(myAutodetectedYstart > 0 && myAutodetectedYstart == ystart)
{
// We've reached the auto-detect value, so reset
myOSystem.frameBuffer().showMessage("YStart " + val.str() + " (Auto)");
val.str("");
val << FrameManager::minYStart-1;
val << TIAConstants::minYStart-1;
}
else
myOSystem.frameBuffer().showMessage("YStart " + val.str());
@ -620,7 +664,7 @@ void Console::changeHeight(int direction)
if(direction == +1) // increase Height
{
height++;
if(height > FrameManager::maxViewableHeight || height > dheight)
if(height > TIAConstants::maxViewableHeight || height > dheight)
{
myOSystem.frameBuffer().showMessage("Height at maximum");
return;
@ -629,7 +673,7 @@ void Console::changeHeight(int direction)
else if(direction == -1) // decrease Height
{
height--;
if(height < FrameManager::minViewableHeight) height = 0;
if(height < TIAConstants::minViewableHeight) height = 0;
}
else
return;
@ -649,12 +693,10 @@ void Console::setTIAProperties()
{
uInt32 ystart = atoi(myProperties.get(Display_YStart).c_str());
if(ystart != 0)
ystart = BSPF::clamp(ystart, FrameManager::minYStart, FrameManager::maxYStart);
ystart = BSPF::clamp(ystart, TIAConstants::minYStart, TIAConstants::maxYStart);
uInt32 height = atoi(myProperties.get(Display_Height).c_str());
if(height != 0)
height = BSPF::clamp(height, FrameManager::minViewableHeight, FrameManager::maxViewableHeight);
myTIA->autodetectLayout(false);
height = BSPF::clamp(height, TIAConstants::minViewableHeight, TIAConstants::maxViewableHeight);
if(myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" ||
myDisplayFormat == "SECAM60")
@ -676,7 +718,7 @@ void Console::setTIAProperties()
myTIA->setLayout(FrameLayout::pal);
}
myTIA->setYStart(ystart);
myTIA->setYStart(ystart != 0 ? ystart : myAutodetectedYstart);
myTIA->setHeight(height);
}

View File

@ -35,6 +35,7 @@ class Debugger;
#include "FrameBuffer.hxx"
#include "Serializable.hxx"
#include "NTSCFilter.hxx"
#include "frame-manager/AbstractFrameManager.hxx"
/**
Contains detailed info about a console.
@ -304,6 +305,16 @@ class Console : public Serializable
void toggleJitter() const;
private:
/**
* Dry-run the emulation and detect the frame layout (PAL / NTSC).
*/
void autodetectFrameLayout();
/**
* Dryrun the emulation and detect ystart (the first visible scanline).
*/
void autodetectYStart();
/**
Sets various properties of the TIA (YStart, Height, etc) based on
the current display format.
@ -366,6 +377,9 @@ class Console : public Serializable
// Pointer to the TIA object
unique_ptr<TIA> myTIA;
// The frame manager instance that is used during emulation.
unique_ptr<AbstractFrameManager> myFrameManager;
// Pointer to the Cartridge (the debugger needs it)
unique_ptr<Cartridge> myCart;
@ -387,6 +401,9 @@ class Console : public Serializable
// Display format currently in use
uInt32 myCurrentFormat;
// Autodetected ystart.
uInt32 myAutodetectedYstart;
// Indicates whether an external palette was found and
// successfully loaded
bool myUserPaletteDefined;

View File

@ -33,7 +33,7 @@ namespace GUI {
#include "Variant.hxx"
#include "FBSurface.hxx"
#include "TIASurface.hxx"
#include "FrameManager.hxx"
#include "TIAConstants.hxx"
#include "bspf.hxx"
// Return values for initialization of framebuffer window
@ -132,7 +132,7 @@ class FrameBuffer
{
public:
enum {
kTIAMinW = 320u, kTIAMinH = FrameManager::minViewableHeight,
kTIAMinW = 320u, kTIAMinH = TIAConstants::minViewableHeight,
kFBMinW = 640u, kFBMinH = 480u
};

View File

@ -31,6 +31,7 @@ class VideoMode;
#include "Rect.hxx"
#include "NTSCFilter.hxx"
#include "bspf.hxx"
#include "TIAConstants.hxx"
/**
This class is basically a wrapper around all things related to rendering
@ -173,9 +174,9 @@ class TIASurface
};
Filter myFilter;
enum TIAConstants {
enum {
kTIAW = 160,
kTIAH = FrameManager::frameBufferHeight,
kTIAH = TIAConstants::frameBufferHeight,
kScanH = kTIAH*2
};

View File

@ -1,488 +0,0 @@
//============================================================================
//
// 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.
//============================================================================
// #define TIA_FRAMEMANAGER_DEBUG_LOG
#include <algorithm>
#include "FrameManager.hxx"
enum Metrics: uInt32 {
vblankNTSC = 37,
vblankPAL = 45,
kernelNTSC = 192,
kernelPAL = 228,
overscanNTSC = 30,
overscanPAL = 36,
vsync = 3,
maxLinesVsync = 32,
maxLinesVsyncDuringAutodetect = 100,
visibleOverscan = 20,
tvModeDetectionTolerance = 20,
initialGarbageFrames = 10,
framesForModeConfirmation = 5,
minStableFrames = 10,
maxStabilizationFrames = 20,
minDeltaForJitter = 3,
framesForStableHeight = 2
};
static constexpr uInt32
frameLinesNTSC = Metrics::vsync + Metrics::vblankNTSC + Metrics::kernelNTSC + Metrics::overscanNTSC,
frameLinesPAL = Metrics::vsync + Metrics::vblankPAL + Metrics::kernelPAL + Metrics::overscanPAL;
inline static uInt32 vsyncLimit(bool autodetect) {
return autodetect ? maxLinesVsyncDuringAutodetect : maxLinesVsync;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 FrameManager::initialGarbageFrames()
{
return Metrics::initialGarbageFrames;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameManager::FrameManager()
: myLayout(FrameLayout::pal),
myAutodetectLayout(true),
myHeight(0),
myFixedHeight(0),
myJitterEnabled(false)
{
updateLayout(FrameLayout::ntsc);
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setHandlers(
FrameManager::callback frameStartCallback,
FrameManager::callback frameCompleteCallback,
FrameManager::callback renderingStartCallback
)
{
myOnFrameStart = frameStartCallback;
myOnFrameComplete = frameCompleteCallback;
myOnRenderingStart = renderingStartCallback;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::reset()
{
myVblankManager.reset();
myState = State::waitForVsyncStart;
myCurrentFrameTotalLines = myCurrentFrameFinalLines = 0;
myFrameRate = 60.0;
myLineInState = 0;
myVsync = false;
myTotalFrames = 0;
myFramesInMode = 0;
myModeConfirmed = false;
myVsyncLines = 0;
myY = 0;
myFramePending = false;
myStabilizationFrames = 0;
myStableFrames = 0;
myHasStabilized = false;
myStableFrameLines = -1;
myStableFrameHeightCountdown = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::nextLine()
{
State previousState = myState;
myCurrentFrameTotalLines++;
myLineInState++;
switch (myState)
{
case State::waitForVsyncStart:
if ((myCurrentFrameTotalLines > myFrameLines - 3) || myTotalFrames == 0)
myVsyncLines++;
if (myVsyncLines > vsyncLimit(myAutodetectLayout)) setState(State::waitForFrameStart);
break;
case State::waitForVsyncEnd:
if (++myVsyncLines > vsyncLimit(myAutodetectLayout))
setState(State::waitForFrameStart);
break;
case State::waitForFrameStart:
if (myVblankManager.nextLine(myTotalFrames <= Metrics::initialGarbageFrames))
setState(State::frame);
break;
case State::frame:
if (myLineInState >= myHeight)
{
myLastY = ystart() + myY; // Last line drawn in this frame
setState(State::waitForVsyncStart);
}
break;
// default:
// throw runtime_error("frame manager: invalid state");
}
if (myState == State::frame && previousState == State::frame) myY++;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameManager::missingScanlines() const
{
if (myLastY == ystart() + myY)
return 0;
else
return myHeight - myY;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setVsync(bool vsync)
{
if (vsync == myVsync) return;
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "vsync " << myVsync << " -> " << vsync << ": state " << int(myState) << " @ " << myLineInState << "\n").flush();
#endif
myVsync = vsync;
switch (myState)
{
case State::waitForVsyncStart:
case State::waitForFrameStart:
if (myVsync) setState(State::waitForVsyncEnd);
break;
case State::waitForVsyncEnd:
if (!myVsync) setState(State::waitForFrameStart);
break;
case State::frame:
if (myVsync) setState(State::waitForVsyncEnd);
break;
// default:
// throw runtime_error("frame manager: invalid state");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setState(FrameManager::State state)
{
if (myState == state) return;
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "state change " << myState << " -> " << state << " @ " << myLineInState << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG
myState = state;
myLineInState = 0;
switch (myState) {
case State::waitForFrameStart:
if (!myHasStabilized) {
myHasStabilized =
myStableFrames >= Metrics::minStableFrames ||
myStabilizationFrames >= Metrics::maxStabilizationFrames;
myStabilizationFrames++;
if (myVblankManager.isStable())
myStableFrames++;
else
myStableFrames = 0;
}
if (myFramePending) finalizeFrame();
if (myOnFrameStart) myOnFrameStart();
myVblankManager.start();
myFramePending = true;
myVsyncLines = 0;
break;
case State::frame:
if (myOnRenderingStart) myOnRenderingStart();
myVsyncLines = 0;
myY = 0;
break;
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::finalizeFrame()
{
if (myCurrentFrameTotalLines != uInt32(myStableFrameLines)) {
if (myCurrentFrameTotalLines == myCurrentFrameFinalLines) {
if (++myStableFrameHeightCountdown >= Metrics::framesForStableHeight) {
if (myStableFrameLines >= 0) {
handleJitter(myCurrentFrameTotalLines - myStableFrameLines);
}
myStableFrameLines = myCurrentFrameTotalLines;
}
}
else myStableFrameHeightCountdown = 0;
}
myPreviousFrameFinalLines = myCurrentFrameFinalLines;
myCurrentFrameFinalLines = myCurrentFrameTotalLines;
myCurrentFrameTotalLines = 0;
myTotalFrames++;
if (myOnFrameComplete) myOnFrameComplete();
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "frame complete @ " << myLineInState << " (" << myCurrentFrameFinalLines << " total)" << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG
if (myAutodetectLayout) updateAutodetectedLayout();
myFrameRate = (myLayout == FrameLayout::pal ? 15600.0 : 15720.0) /
myCurrentFrameFinalLines;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::handleJitter(Int32 scanlineDifference)
{
if (
uInt32(abs(scanlineDifference)) < Metrics::minDeltaForJitter ||
!myJitterEnabled ||
myTotalFrames < Metrics::initialGarbageFrames
) return;
myVblankManager.setJitter(scanlineDifference);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::updateAutodetectedLayout()
{
if (myTotalFrames <= Metrics::initialGarbageFrames) {
return;
}
const FrameLayout oldLayout = myLayout;
const uInt32
deltaNTSC = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesNTSC)),
deltaPAL = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesPAL));
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance)
updateLayout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
else if (!myModeConfirmed) {
if (
(myCurrentFrameFinalLines < frameLinesPAL) &&
(myCurrentFrameFinalLines > frameLinesNTSC) &&
(myCurrentFrameFinalLines % 2)
)
updateLayout(FrameLayout::ntsc);
else
updateLayout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
}
if (oldLayout == myLayout)
myFramesInMode++;
else
myFramesInMode = 0;
if (myFramesInMode > Metrics::framesForModeConfirmation)
myModeConfirmed = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::updateLayout(FrameLayout layout)
{
if (layout == myLayout) return;
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "TV mode switched to " << int(layout) << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG
myLayout = layout;
switch (myLayout)
{
case FrameLayout::ntsc:
myVblankLines = Metrics::vblankNTSC;
myKernelLines = Metrics::kernelNTSC;
myOverscanLines = Metrics::overscanNTSC;
break;
case FrameLayout::pal:
myVblankLines = Metrics::vblankPAL;
myKernelLines = Metrics::kernelPAL;
myOverscanLines = Metrics::overscanPAL;
break;
// default:
// throw runtime_error("frame manager: invalid TV mode");
}
myFrameLines = Metrics::vsync + myVblankLines + myKernelLines + myOverscanLines;
if (myFixedHeight == 0)
myHeight = myKernelLines + Metrics::visibleOverscan;
myVblankManager.setVblankLines(myVblankLines);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setVblank(bool vblank)
{
#ifdef TIA_FRAMEMANAGER_DEBUG_LOG
(cout << "vblank change " << myVblankManager.vblank() << " -> " << vblank << "@" << myLineInState << "\n").flush();
#endif // TIA_FRAMEMANAGER_DEBUG_LOG
if (myState == State::waitForFrameStart) {
if (myVblankManager.setVblankDuringVblank(vblank, myTotalFrames <= Metrics::initialGarbageFrames)) {
setState(State::frame);
}
} else
myVblankManager.setVblank(vblank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setFixedHeight(uInt32 height)
{
myFixedHeight = height;
myHeight = myFixedHeight > 0 ? myFixedHeight : (myKernelLines + Metrics::visibleOverscan);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::enableJitter(bool enabled)
{
myJitterEnabled = enabled;
if (!enabled) myVblankManager.setJitter(0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::save(Serializer& out) const
{
try
{
out.putString(name());
if (!myVblankManager.save(out)) return false;
out.putInt(uInt32(myLayout));
out.putBool(myAutodetectLayout);
out.putInt(uInt32(myState));
out.putInt(myLineInState);
out.putInt(myCurrentFrameTotalLines);
out.putInt(myCurrentFrameFinalLines);
out.putInt(myPreviousFrameFinalLines);
out.putInt(myVsyncLines);
out.putDouble(myFrameRate);
out.putInt(myY); out.putInt(myLastY);
out.putBool(myFramePending);
out.putInt(myTotalFrames);
out.putInt(myFramesInMode);
out.putBool(myModeConfirmed);
out.putInt(myStableFrames);
out.putInt(myStabilizationFrames);
out.putBool(myHasStabilized);
out.putBool(myVsync);
out.putInt(myVblankLines);
out.putInt(myKernelLines);
out.putInt(myOverscanLines);
out.putInt(myFrameLines);
out.putInt(myHeight);
out.putInt(myFixedHeight);
out.putBool(myJitterEnabled);
out.putInt(myStableFrameLines);
out.putInt(myStableFrameHeightCountdown);
}
catch(...)
{
cerr << "ERROR: TIA_FrameManager::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
if (!myVblankManager.load(in)) return false;
myLayout = FrameLayout(in.getInt());
myAutodetectLayout = in.getBool();
myState = State(in.getInt());
myLineInState = in.getInt();
myCurrentFrameTotalLines = in.getInt();
myCurrentFrameFinalLines = in.getInt();
myPreviousFrameFinalLines = in.getInt();
myVsyncLines = in.getInt();
myFrameRate = float(in.getDouble());
myY = in.getInt(); myLastY = in.getInt();
myFramePending = in.getBool();
myTotalFrames = in.getInt();
myFramesInMode = in.getInt();
myModeConfirmed = in.getBool();
myStableFrames = in.getInt();
myStabilizationFrames = in.getInt();
myHasStabilized = in.getBool();
myVsync = in.getBool();
myVblankLines = in.getInt();
myKernelLines = in.getInt();
myOverscanLines = in.getInt();
myFrameLines = in.getInt();
myHeight = in.getInt();
myFixedHeight = in.getInt();
myJitterEnabled = in.getBool();
myStableFrameLines = in.getInt();
myStableFrameHeightCountdown = in.getInt();
}
catch(...)
{
cerr << "ERROR: TIA_FrameManager::load" << endl;
return false;
}
return true;
}

View File

@ -1,188 +0,0 @@
//============================================================================
//
// 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_FRAME_MANAGER
#define TIA_FRAME_MANAGER
#include <functional>
#include "VblankManager.hxx"
#include "Serializable.hxx"
#include "FrameLayout.hxx"
#include "bspf.hxx"
class FrameManager : public Serializable
{
public:
using callback = std::function<void()>;
public:
FrameManager();
public:
static uInt8 initialGarbageFrames();
void setHandlers(
callback frameStartCallback,
callback frameCompletionCallback,
callback renderingStartCallback
);
void reset();
void nextLine();
void setVblank(bool vblank);
void setVsync(bool vsync);
bool isRendering() const { return myState == State::frame && myHasStabilized; }
FrameLayout layout() const { return myLayout; }
bool vblank() const { return myVblankManager.vblank(); }
bool vsync() const { return myVsync; }
uInt32 height() const { return myHeight; }
void setFixedHeight(uInt32 height);
uInt32 getY() const { return myY; }
uInt32 scanlines() const { return myCurrentFrameTotalLines; }
uInt32 scanlinesLastFrame() const { return myCurrentFrameFinalLines; }
uInt32 missingScanlines() const;
bool scanlineCountTransitioned() const {
return (myPreviousFrameFinalLines & 0x1) != (myCurrentFrameFinalLines & 0x1);
}
uInt32 frameCount() const { return myTotalFrames; }
float frameRate() const { return myFrameRate; }
void setYstart(uInt32 ystart) { myVblankManager.setYstart(ystart); }
uInt32 ystart() const { return myVblankManager.ystart(); }
bool ystartIsAuto(uInt32 line) const { return myVblankManager.ystartIsAuto(line); }
void autodetectLayout(bool toggle) { myAutodetectLayout = toggle; }
void setLayout(FrameLayout mode) { if (!myAutodetectLayout) updateLayout(mode); }
/**
Serializable methods (see that class for more information).
*/
bool save(Serializer& out) const override;
bool load(Serializer& in) override;
string name() const override { return "TIA_FrameManager"; }
void setJitterFactor(uInt8 factor) { myVblankManager.setJitterFactor(factor); }
bool jitterEnabled() const { return myJitterEnabled; }
void enableJitter(bool enabled);
public:
static constexpr uInt32 frameBufferHeight = 320;
static constexpr uInt32 minYStart = 1, maxYStart = 64;
static constexpr uInt32 minViewableHeight = 210, maxViewableHeight = 256;
private:
enum State {
waitForVsyncStart,
waitForVsyncEnd,
waitForFrameStart,
frame
};
enum VblankMode {
locked,
floating,
final,
fixed
};
private:
void updateLayout(FrameLayout mode);
void updateAutodetectedLayout();
void setState(State state);
void finalizeFrame();
void nextLineInVsync();
void handleJitter(Int32 scanlineDifference);
private:
callback myOnFrameStart;
callback myOnFrameComplete;
callback myOnRenderingStart;
VblankManager myVblankManager;
FrameLayout myLayout;
bool myAutodetectLayout;
State myState;
uInt32 myLineInState;
uInt32 myCurrentFrameTotalLines;
uInt32 myCurrentFrameFinalLines;
uInt32 myPreviousFrameFinalLines;
uInt32 myVsyncLines;
float myFrameRate;
uInt32 myY, myLastY;
bool myFramePending;
uInt32 myTotalFrames;
uInt32 myFramesInMode;
bool myModeConfirmed;
uInt32 myStableFrames;
uInt32 myStabilizationFrames;
bool myHasStabilized;
bool myVsync;
uInt32 myVblankLines;
uInt32 myKernelLines;
uInt32 myOverscanLines;
uInt32 myFrameLines;
uInt32 myHeight;
uInt32 myFixedHeight;
bool myJitterEnabled;
Int32 myStableFrameLines;
uInt8 myStableFrameHeightCountdown;
private:
FrameManager(const FrameManager&) = delete;
FrameManager(FrameManager&&) = delete;
FrameManager& operator=(const FrameManager&) = delete;
FrameManager& operator=(FrameManager&&) = delete;
};
#endif // TIA_FRAME_MANAGER

View File

@ -21,6 +21,8 @@
#include "Control.hxx"
#include "Paddles.hxx"
#include "DelayQueueIteratorImpl.hxx"
#include "TIAConstants.hxx"
#include "frame-manager/FrameManager.hxx"
#ifdef DEBUGGER_SUPPORT
#include "CartDebug.hxx"
@ -67,6 +69,7 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
: myConsole(console),
mySound(sound),
mySettings(settings),
myFrameManager(nullptr),
myPlayfield(~CollisionMask::playfield & 0x7FFF),
myMissile0(~CollisionMask::missile0 & 0x7FFF),
myMissile1(~CollisionMask::missile1 & 0x7FFF),
@ -76,18 +79,6 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
mySpriteEnabledBits(0xFF),
myCollisionsEnabledBits(0xFF)
{
myFrameManager.setHandlers(
[this] () {
onFrameStart();
},
[this] () {
onFrameComplete();
},
[this] () {
onRenderingStart();
}
);
myTIAPinsDriven = mySettings.getBool("tiadriven");
myBackground.setTIA(this);
@ -98,12 +89,42 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
myMissile1.setTIA(this);
myBall.setTIA(this);
myFrameManager.enableJitter(mySettings.getBool("tv.jitter"));
myFrameManager.setJitterFactor(mySettings.getInt("tv.jitter_recovery"));
myEnableJitter = mySettings.getBool("tv.jitter");
myJitterFactor = mySettings.getInt("tv.jitter_recovery");
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::setFrameManager(AbstractFrameManager *frameManager)
{
clearFrameManager();
myFrameManager = frameManager;
myFrameManager->setHandlers(
[this] () {
onFrameStart();
},
[this] () {
onFrameComplete();
}
);
myFrameManager->enableJitter(myEnableJitter);
myFrameManager->setJitterFactor(myJitterFactor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::clearFrameManager()
{
if (!myFrameManager) return;
myFrameManager->clearHandlers();
myFrameManager = nullptr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::reset()
{
@ -143,7 +164,8 @@ void TIA::reset()
mySound.reset();
myDelayQueue.reset();
myFrameManager.reset();
if (myFrameManager) myFrameManager->reset();
myCyclesAtFrameStart = 0;
@ -161,7 +183,7 @@ void TIA::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::frameReset()
{
memset(myFramebuffer, 0, 160 * FrameManager::frameBufferHeight);
memset(myFramebuffer, 0, 160 * TIAConstants::frameBufferHeight);
myAutoFrameEnabled = mySettings.getInt("framerate") <= 0;
enableColorLoss(mySettings.getBool("colorloss"));
}
@ -205,7 +227,7 @@ bool TIA::save(Serializer& out) const
if(!mySound.save(out)) return false;
if(!myDelayQueue.save(out)) return false;
if(!myFrameManager.save(out)) return false;
if(!myFrameManager->save(out)) return false;
if(!myBackground.save(out)) return false;
if(!myPlayfield.save(out)) return false;
@ -276,7 +298,7 @@ bool TIA::load(Serializer& in)
if(!mySound.load(in)) return false;
if(!myDelayQueue.load(in)) return false;
if(!myFrameManager.load(in)) return false;
if(!myFrameManager->load(in)) return false;
if(!myBackground.load(in)) return false;
if(!myPlayfield.load(in)) return false;
@ -481,7 +503,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break;
case VSYNC:
myFrameManager.setVsync(value & 0x02);
myFrameManager->setVsync(value & 0x02);
myShadowRegisters[address] = value;
break;
@ -752,7 +774,7 @@ bool TIA::saveDisplay(Serializer& out) const
{
try
{
out.putByteArray(myFramebuffer, 160*FrameManager::frameBufferHeight);
out.putByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight);
}
catch(...)
{
@ -769,7 +791,7 @@ bool TIA::loadDisplay(Serializer& in)
try
{
// Reset frame buffer pointer and data
in.getByteArray(myFramebuffer, 160*FrameManager::frameBufferHeight);
in.getByteArray(myFramebuffer, 160*TIAConstants::frameBufferHeight);
}
catch(...)
{
@ -795,7 +817,7 @@ bool TIA::enableColorLoss(bool enabled)
if(enabled)
{
myColorLossEnabled = true;
myColorLossActive = myFrameManager.scanlinesLastFrame() & 0x1;
myColorLossActive = myFrameManager->scanlinesLastFrame() & 0x1;
}
else
{
@ -819,7 +841,7 @@ bool TIA::electronBeamPos(uInt32& x, uInt32& y) const
uInt8 clocks = clocksThisLine();
x = (clocks < 68) ? 0 : clocks - 68;
y = myFrameManager.getY();
y = myFrameManager->getY();
return isRendering();
}
@ -905,7 +927,11 @@ bool TIA::toggleCollisions()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool TIA::enableFixedColors(bool enable)
{
int layout = myFrameManager.layout() == FrameLayout::pal ? 1 : 0;
// This will be called during reset at a point where no frame manager
// instance is available, so we guard aginst this here.
int layout = 0;
if (myFrameManager) layout = myFrameManager->layout() == FrameLayout::pal ? 1 : 0;
myMissile0.setDebugColor(myFixedColorPalette[layout][FixedObject::M0]);
myMissile1.setDebugColor(myFixedColorPalette[layout][FixedObject::M1]);
myPlayer0.setDebugColor(myFixedColorPalette[layout][FixedObject::P0]);
@ -990,22 +1016,32 @@ bool TIA::toggleJitter(uInt8 mode)
{
switch (mode) {
case 0:
myFrameManager.enableJitter(false);
myEnableJitter = false;
break;
case 1:
myFrameManager.enableJitter(true);
myEnableJitter = true;
break;
case 2:
myFrameManager.enableJitter(!myFrameManager.jitterEnabled());
myEnableJitter = !myEnableJitter;
break;
default:
throw runtime_error("invalid argument for toggleJitter");
}
return myFrameManager.jitterEnabled();
if (myFrameManager) myFrameManager->enableJitter(myEnableJitter);
return myEnableJitter;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::setJitterRecoveryFactor(Int32 factor)
{
myJitterFactor = factor;
if (myFrameManager) myFrameManager->setJitterFactor(myJitterFactor);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1080,9 +1116,9 @@ void TIA::onFrameStart()
{
// Only activate it when necessary, since changing colours in
// the graphical object forces the TIA cached line to be flushed
if (myFrameManager.scanlineCountTransitioned())
if (myFrameManager->scanlineCountTransitioned())
{
myColorLossActive = myFrameManager.scanlinesLastFrame() & 0x1;
myColorLossActive = myFrameManager->scanlinesLastFrame() & 0x1;
myMissile0.applyColorLoss();
myMissile1.applyColorLoss();
@ -1095,12 +1131,6 @@ void TIA::onFrameStart()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::onRenderingStart()
{
myXAtRenderingStart = myHctr > 68 ? myHctr - 68 : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::onFrameComplete()
{
@ -1111,13 +1141,13 @@ void TIA::onFrameComplete()
memset(myFramebuffer, 0, myXAtRenderingStart);
// Blank out any extra lines not drawn this frame
const uInt32 missingScanlines = myFrameManager.missingScanlines();
const Int32 missingScanlines = myFrameManager->missingScanlines();
if (missingScanlines > 0)
memset(myFramebuffer + 160 * myFrameManager.getY(), 0, missingScanlines * 160);
memset(myFramebuffer + 160 * myFrameManager->getY(), 0, missingScanlines * 160);
// Recalculate framerate, attempting to auto-correct for scanline 'jumps'
if(myAutoFrameEnabled)
myConsole.setFramerate(myFrameManager.frameRate());
myConsole.setFramerate(myFrameManager->frameRate());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1147,7 +1177,7 @@ void TIA::cycle(uInt32 colorClocks)
else
tickHframe();
if (myCollisionUpdateRequired && !myFrameManager.vblank()) updateCollision();
if (myCollisionUpdateRequired && !myFrameManager->vblank()) updateCollision();
}
if (++myHctr >= 228)
@ -1201,7 +1231,7 @@ void TIA::tickHblank()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::tickHframe()
{
const uInt32 y = myFrameManager.getY();
const uInt32 y = myFrameManager->getY();
const uInt32 x = myHctr - 68 - myHctrDelta;
myCollisionUpdateRequired = true;
@ -1213,7 +1243,7 @@ void TIA::tickHframe()
myPlayer1.tick();
myBall.tick();
if (myFrameManager.isRendering())
if (myFrameManager->isRendering())
renderPixel(x, y);
}
@ -1223,8 +1253,8 @@ void TIA::applyRsync()
const uInt32 x = myHctr > 68 ? myHctr - 68 : 0;
myHctrDelta = 225 - myHctr;
if (myFrameManager.isRendering())
memset(myFramebuffer + myFrameManager.getY() * 160 + x, 0, 160 - x);
if (myFrameManager->isRendering())
memset(myFramebuffer + myFrameManager->getY() * 160 + x, 0, 160 - x);
myHctr = 225;
}
@ -1243,9 +1273,9 @@ void TIA::nextLine()
myHstate = HState::blank;
myHctrDelta = 0;
myFrameManager.nextLine();
myFrameManager->nextLine();
if (myFrameManager.isRendering() && myFrameManager.getY() == 0) flushLineCache();
if (myFrameManager->isRendering() && myFrameManager->getY() == 0) flushLineCache();
mySystem->m6502().clearHaltRequest();
}
@ -1253,9 +1283,9 @@ void TIA::nextLine()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::cloneLastLine()
{
const auto y = myFrameManager.getY();
const auto y = myFrameManager->getY();
if (!myFrameManager.isRendering() || y == 0) return;
if (!myFrameManager->isRendering() || y == 0) return;
uInt8* buffer = myFramebuffer;
@ -1282,7 +1312,7 @@ void TIA::renderPixel(uInt32 x, uInt32 y)
uInt8 color = 0;
if (!myFrameManager.vblank())
if (!myFrameManager->vblank())
{
switch (myPriority)
{
@ -1355,8 +1385,8 @@ void TIA::flushLineCache()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::clearHmoveComb()
{
if (myFrameManager.isRendering() && myHstate == HState::blank)
memset(myFramebuffer + myFrameManager.getY() * 160, myColorHBlank, 8);
if (myFrameManager->isRendering() && myHstate == HState::blank)
memset(myFramebuffer + myFrameManager->getY() * 160, myColorHBlank, 8);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1369,7 +1399,7 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
{
case VBLANK:
flushLineCache();
myFrameManager.setVblank(value & 0x02);
myFrameManager->setVblank(value & 0x02);
break;
case HMOVE:

View File

@ -25,9 +25,10 @@
#include "Device.hxx"
#include "Serializer.hxx"
#include "TIATypes.hxx"
#include "TIAConstants.hxx"
#include "DelayQueue.hxx"
#include "DelayQueueIterator.hxx"
#include "FrameManager.hxx"
#include "frame-manager/AbstractFrameManager.hxx"
#include "FrameLayout.hxx"
#include "Background.hxx"
#include "Playfield.hxx"
@ -100,9 +101,20 @@ class TIA : public Device
@param settings The settings object for this TIA device
*/
TIA(Console& console, Sound& sound, Settings& settings);
virtual ~TIA() = default;
public:
/**
* Configure the frame manager.
*/
void setFrameManager(AbstractFrameManager *frameManager);
/**
* Clear the configured frame manager and deteach the lifecycle callbacks.
*/
void clearFrameManager();
/**
Reset device to its power-on state.
*/
@ -192,21 +204,19 @@ class TIA : public Device
Answers dimensional info about the framebuffer.
*/
uInt32 width() const { return 160; }
uInt32 height() const { return myFrameManager.height(); }
uInt32 ystart() const { return myFrameManager.ystart(); }
bool ystartIsAuto(uInt32 line) const { return myFrameManager.ystartIsAuto(line); }
uInt32 height() const { return myFrameManager->height(); }
uInt32 ystart() const { return myFrameManager->ystart(); }
/**
Changes the current Height/YStart properties.
Note that calls to these method(s) must be eventually followed by
::frameReset() for the changes to take effect.
*/
void setHeight(uInt32 height) { myFrameManager.setFixedHeight(height); }
void setYStart(uInt32 ystart) { myFrameManager.setYstart(ystart); }
void setHeight(uInt32 height) { myFrameManager->setFixedHeight(height); }
void setYStart(uInt32 ystart) { myFrameManager->setYstart(ystart); }
void autodetectLayout(bool toggle) { myFrameManager.autodetectLayout(toggle); }
void setLayout(FrameLayout layout) { myFrameManager.setLayout(layout); }
FrameLayout frameLayout() const { return myFrameManager.layout(); }
void setLayout(FrameLayout layout) { myFrameManager->setLayout(layout); }
FrameLayout frameLayout() const { return myFrameManager->layout(); }
/**
Answers the timing of the console currently in use.
@ -249,7 +259,7 @@ class TIA : public Device
@return The total number of scanlines generated
*/
uInt32 scanlines() const { return myFrameManager.scanlines(); }
uInt32 scanlines() const { return myFrameManager->scanlines(); }
/**
Answers the total number of scanlines the TIA generated in the
@ -257,7 +267,7 @@ class TIA : public Device
@return The total number of scanlines generated in the last frame.
*/
uInt32 scanlinesLastFrame() const { return myFrameManager.scanlinesLastFrame(); }
uInt32 scanlinesLastFrame() const { return myFrameManager->scanlinesLastFrame(); }
/**
Answers the total system cycles from the start of the emulation.
@ -267,7 +277,7 @@ class TIA : public Device
/**
Answers the frame count from the start of the emulation.
*/
uInt32 frameCount() const { return myFrameManager.frameCount(); }
uInt32 frameCount() const { return myFrameManager->frameCount(); }
/**
Answers the system cycles from the start of the current frame.
@ -282,7 +292,7 @@ class TIA : public Device
@return If the frame is in rendering mode
*/
bool isRendering() const { return myFrameManager.isRendering(); }
bool isRendering() const { return myFrameManager->isRendering(); }
/**
Answers the current position of the virtual 'electron beam' used
@ -359,7 +369,7 @@ class TIA : public Device
@return Whether the mode was enabled or disabled
*/
bool toggleJitter(uInt8 mode = 2);
void setJitterRecoveryFactor(Int32 factor) { myFrameManager.setJitterFactor(factor); }
void setJitterRecoveryFactor(Int32 factor);
/**
This method should be called to update the TIA with a new scanline.
@ -451,12 +461,6 @@ class TIA : public Device
*/
void onFrameStart();
/**
* This callback is invoked by FrameManager when the visible range of the
* current frame starts.
*/
void onRenderingStart();
/**
* This callback is invoked by FrameManager when the current frame completes.
*/
@ -598,7 +602,7 @@ class TIA : public Device
* The frame manager is responsible for detecting frame boundaries and the visible
* region of each frame.
*/
FrameManager myFrameManager;
AbstractFrameManager *myFrameManager;
/**
* The various TIA objects.
@ -622,10 +626,8 @@ class TIA : public Device
LatchedInput myInput0;
LatchedInput myInput1;
/**
* Pointer to the internal color-index-based frame buffer
*/
uInt8 myFramebuffer[160 * FrameManager::frameBufferHeight];
// Pointer to the internal color-index-based frame buffer
uInt8 myFramebuffer[160 * TIAConstants::frameBufferHeight];
/**
* Setting this to true injects random values into undefined reads.
@ -738,10 +740,17 @@ class TIA : public Device
bool myColorLossActive;
/**
* System cycles at the end of the previous frame / beginning of next frame
* System cycles at the end of the previous frame / beginning of next frame.
*/
uInt64 myCyclesAtFrameStart;
/**
* The frame manager can change during our lifetime, so we buffer those two.
*/
bool myEnableJitter;
uInt8 myJitterFactor;
#ifdef DEBUGGER_SUPPORT
// The arrays containing information about every byte of TIA
// indicating whether and how (RW) it is used.

View File

@ -0,0 +1,15 @@
#ifndef TIA_CONSTANTS_HXX
#define TIA_CONSTANTS_HXX
#include "bspf.hxx"
namespace TIAConstants {
constexpr uInt32 frameBufferHeight = 320;
constexpr uInt32 minYStart = 1, maxYStart = 64;
constexpr uInt32 minViewableHeight = 210, maxViewableHeight = 256;
constexpr uInt32 initialGarbageFrames = 10;
}
#endif // TIA_CONSTANTS_HXX

View File

@ -1,306 +0,0 @@
//============================================================================
//
// 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.
//============================================================================
// #define TIA_VBLANK_MANAGER_DEBUG_LOG
#include <algorithm>
#include "VblankManager.hxx"
enum Metrics: uInt32 {
maxUnderscan = 10,
maxVblankViolations = 2,
minStableVblankFrames = 1,
framesUntilFinal = 30,
maxJitter = 50
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VblankManager::VblankManager()
: myVblankLines(0),
myYstart(0),
myMode(VblankMode::floating),
myJitter(0),
myJitterFactor(2)
{
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VblankManager::reset()
{
myVblank = false;
myCurrentLine = 0;
myVblankViolations = 0;
myStableVblankFrames = 0;
myVblankViolated = false;
myLastVblankLines = 0;
myIsRunning = false;
myJitter = 0;
if (myMode != VblankMode::fixed) setVblankMode(VblankMode::floating);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VblankManager::setJitter(Int32 jitter) {
jitter = std::min<Int32>(jitter, Metrics::maxJitter);
if (myMode == VblankMode::final) jitter = std::max<Int32>(jitter, -myLastVblankLines);
if (myMode == VblankMode::fixed) jitter = std::max<Int32>(jitter, -myYstart);
if (jitter > 0) jitter += myJitterFactor;
if (jitter < 0) jitter -= myJitterFactor;
if (abs(jitter) > abs(myJitter)) myJitter = jitter;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VblankManager::start()
{
myCurrentLine = 0;
myIsRunning = true;
myVblankViolated = false;
if (myJitter > 0) myJitter = std::max(myJitter - myJitterFactor, 0);
if (myJitter < 0) myJitter = std::min(myJitter + myJitterFactor, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VblankManager::nextLine(bool isGarbageFrame)
{
if (!myIsRunning) return false;
// Make sure that we do the transition check **before** incrementing the line
// counter. This ensures that, if the transition is caused by VBLANK off during
// the line, this will continue to trigger the transition in 'locked' mode. Otherwise,
// the transition would be triggered by the line change **before** the VBLANK
// and thus detected as a suprious violation. Sigh, this stuff is complicated,
// isn't it?
const bool transition = shouldTransition(isGarbageFrame);
if (transition) myIsRunning = false;
myCurrentLine++;
return transition;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VblankManager::setYstart(uInt32 ystart)
{
if (ystart == myYstart) return;
myYstart = ystart;
setVblankMode(ystart ? VblankMode::fixed : VblankMode::floating);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VblankManager::setVblankDuringVblank(bool vblank, bool isGarbageFrame)
{
const bool oldVblank = myVblank;
myVblank = vblank;
if (!myIsRunning || vblank || oldVblank == myVblank) return false;
const bool transition = shouldTransition(isGarbageFrame);
if (transition) myIsRunning = false;
return transition;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VblankManager::shouldTransition(bool isGarbageFrame)
{
// Are we free to transition as per vblank cycle?
bool shouldTransition = myCurrentLine + 1 >= (myVblank ? myVblankLines : myVblankLines - Metrics::maxUnderscan);
// Do we **actually** transition? This depends on what mode we are in.
bool transition = false;
switch (myMode) {
// 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 (!isGarbageFrame && myCurrentLine == 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 = myCurrentLine;
#ifdef TIA_VBLANK_MANAGER_DEBUG_LOG
(cout << "leaving vblank in floating mode, should transition: " << shouldTransition << "\n").flush();
#endif
// 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) {
setVblankMode(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 (myCurrentLine == myLastVblankLines) {
// Are we free to transition per the algorithm and didn't we 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;
}
#ifdef TIA_VBLANK_MANAGER_DEBUG_LOG
(cout << "leaving vblank in locked mode, should transition: " << shouldTransition << "\n").flush();
#endif
// 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;
}
// Freeze frame start if the detected value seems to be sufficiently stable
if (transition && ++myFramesInLockedMode > Metrics::framesUntilFinal) {
setVblankMode(VblankMode::final);
// Revert to floating mode if there were too many irregular frames in a row
} else if (myVblankViolations > Metrics::maxVblankViolations) {
setVblankMode(VblankMode::floating);
myStableVblankFrames = 0;
}
break;
// Fixed mode: use external ystart value
case VblankMode::fixed:
transition = Int32(myCurrentLine) >=
std::max<Int32>(myYstart + std::min<Int32>(myJitter, Metrics::maxJitter), 0);
break;
// Final mode: use detected ystart value
case VblankMode::final:
transition = Int32(myCurrentLine) >=
std::max<Int32>(myLastVblankLines + std::min<Int32>(myJitter, Metrics::maxJitter), 0);
break;
}
return transition;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VblankManager::setVblankMode(VblankMode mode)
{
if (myMode == mode) return;
myMode = mode;
switch (myMode) {
case VblankMode::locked:
myFramesInLockedMode = 0;
break;
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VblankManager::save(Serializer& out) const
{
try
{
out.putString(name());
out.putInt(myVblankLines);
out.putInt(myYstart);
out.putBool(myVblank);
out.putInt(myCurrentLine);
out.putInt(int(myMode));
out.putInt(myLastVblankLines);
out.putByte(myVblankViolations);
out.putByte(myStableVblankFrames);
out.putBool(myVblankViolated);
out.putByte(myFramesInLockedMode);
out.putInt(myJitter);
out.putByte(myJitterFactor);
out.putBool(myIsRunning);
}
catch(...)
{
cerr << "ERROR: TIA_VblankManager::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VblankManager::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
myVblankLines = in.getInt();
myYstart = in.getInt();
myVblank = in.getBool();
myCurrentLine = in.getInt();
myMode = VblankMode(in.getInt());
myLastVblankLines = in.getInt();
myVblankViolations = in.getByte();
myStableVblankFrames = in.getByte();
myVblankViolated = in.getBool();
myFramesInLockedMode = in.getByte();
myJitter = in.getInt();
myJitterFactor = in.getByte();
myIsRunning = in.getBool();
}
catch(...)
{
cerr << "ERROR: TIA_VblankManager::load" << endl;
return false;
}
return true;
}

View File

@ -1,107 +0,0 @@
//============================================================================
//
// 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_VBLANK_MANAGER
#define TIA_VBLANK_MANAGER
#include "Serializable.hxx"
class VblankManager : public Serializable
{
public:
VblankManager();
public:
void reset();
void start();
bool nextLine(bool isGarbageFrame);
void setVblankLines(uInt32 lines) { myVblankLines = lines; }
void setYstart(uInt32 ystart);
uInt32 ystart() const { return myYstart == 0 ? myLastVblankLines : myYstart; }
bool ystartIsAuto(uInt32 line) const { return myLastVblankLines == line; }
void setVblank(bool vblank) { myVblank = vblank; }
bool setVblankDuringVblank(bool vblank, bool isGarbageFrame);
bool vblank() const { return myVblank; }
uInt32 currentLine() const { return myCurrentLine; }
void setJitter(Int32 jitter);
void setJitterFactor(uInt8 jitterFactor) { myJitterFactor = jitterFactor; }
bool isStable() const { return myMode != VblankMode::floating; }
/**
Serializable methods (see that class for more information).
*/
bool save(Serializer& out) const override;
bool load(Serializer& in) override;
string name() const override { return "TIA_VblankManager"; }
private:
enum VblankMode {
locked,
floating,
final,
fixed
};
private:
bool shouldTransition(bool isGarbageFrame);
void setVblankMode(VblankMode mode);
private:
uInt32 myVblankLines;
uInt32 myYstart;
bool myVblank;
uInt32 myCurrentLine;
VblankMode myMode;
uInt32 myLastVblankLines;
uInt8 myVblankViolations;
uInt8 myStableVblankFrames;
bool myVblankViolated;
uInt8 myFramesInLockedMode;
Int32 myJitter;
uInt8 myJitterFactor;
bool myIsRunning;
private:
VblankManager(const VblankManager&) = delete;
VblankManager(VblankManager&&) = delete;
VblankManager& operator=(const VblankManager&) = delete;
VblankManager& operator=(VblankManager&&) = delete;
};
#endif // TIA_VBLANK_MANAGER

View File

@ -0,0 +1,165 @@
//============================================================================
//
// 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 "AbstractFrameManager.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AbstractFrameManager::AbstractFrameManager() :
myLayout(FrameLayout::pal),
myOnFrameStart(nullptr),
myOnFrameComplete(nullptr)
{
layout(FrameLayout::ntsc);
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::reset()
{
myIsRendering = false;
myVsync = false;
myVblank = false;
myCurrentFrameTotalLines = 0;
myCurrentFrameFinalLines = 0;
myPreviousFrameFinalLines = 0;
myTotalFrames = 0;
myFrameRate = 0;
myFrameRate = 60.0;
onReset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::nextLine()
{
myCurrentFrameTotalLines++;
onNextLine();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::setHandlers(
callback frameStartCallback,
callback frameCompletionCallback
) {
myOnFrameStart = frameStartCallback;
myOnFrameComplete = frameCompletionCallback;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::clearHandlers()
{
myOnFrameStart = myOnFrameComplete = nullptr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::setVblank(bool vblank)
{
if (vblank == myVblank) return;
myVblank = vblank;
onSetVblank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::setVsync(bool vsync)
{
if (vsync == myVsync) return;
myVsync = vsync;
onSetVsync();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::notifyFrameStart()
{
if (myOnFrameStart) myOnFrameStart();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::notifyFrameComplete()
{
myPreviousFrameFinalLines = myCurrentFrameFinalLines;
myCurrentFrameFinalLines = myCurrentFrameTotalLines;
myCurrentFrameTotalLines = 0;
myTotalFrames++;
if (myOnFrameComplete) myOnFrameComplete();
myFrameRate = (layout() == FrameLayout::pal ? 15600.0 : 15720.0) /
myCurrentFrameFinalLines;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AbstractFrameManager::layout(FrameLayout layout)
{
if (layout == myLayout) return;
myLayout = layout;
onLayoutChange();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AbstractFrameManager::save(Serializer& out) const
{
try {
out.putString(name());
out.putBool(myIsRendering);
out.putBool(myVsync);
out.putBool(myVblank);
out.putInt(myCurrentFrameFinalLines);
out.putInt(myPreviousFrameFinalLines);
out.putInt(myTotalFrames);
out.putInt(uInt32(myLayout));
out.putDouble(myFrameRate);
return onSave(out);
}
catch(...)
{
cerr << "ERROR: AbstractFrameManager::save" << endl;
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool AbstractFrameManager::load(Serializer& in)
{
try {
if (in.getString() != name()) return false;
myIsRendering = in.getBool();
myVsync = in.getBool();
myVblank = in.getBool();
myCurrentFrameFinalLines = in.getInt();
myPreviousFrameFinalLines = in.getInt();
myTotalFrames = in.getInt();
myLayout = FrameLayout(in.getInt());
myFrameRate = float(in.getDouble());
return onLoad(in);
}
catch(...)
{
cerr << "ERROR: AbstractFrameManager::load" << endl;
return false;
}
}

View File

@ -0,0 +1,321 @@
//============================================================================
//
// 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_ABSTRACT_FRAME_MANAGER
#define TIA_ABSTRACT_FRAME_MANAGER
#include <functional>
#include "Serializable.hxx"
#include "FrameLayout.hxx"
class AbstractFrameManager : public Serializable
{
public:
using callback = std::function<void()>;
public:
AbstractFrameManager();
public:
/**
* Configure the various handler callbacks.
*/
void setHandlers(
callback frameStartCallback,
callback frameCompletionCallback
);
/**
* Clear the configured handler callbacks.
*/
void clearHandlers();
/**
* Reset.
*/
void reset();
/**
* Called by TIA to notify the start of the next scanline.
*/
void nextLine();
/**
* Called by TIA on VBLANK writes.
*/
void setVblank(bool vblank);
/**
* Called by TIA on VSYNC writes.
*/
void setVsync(bool vsync);
/**
* Should the TIA render its frame? This is buffered in a flag for
* performance reasons; descendants must update the flag.
*/
bool isRendering() const { return myIsRendering; }
/**
* Is vsync on?
*/
bool vsync() const { return myVsync; }
/**
* Is vblank on?
*/
bool vblank() const { return myVblank; }
/**
* The number of scanlines in the last finished frame.
*/
uInt32 scanlinesLastFrame() const { return myCurrentFrameFinalLines; }
/**
* Did the number of scanlines switch between even / odd (used for color loss
* emulation).
*
* TODO: Crappy name, find something better.
*/
bool scanlineCountTransitioned() const {
return (myPreviousFrameFinalLines & 0x1) != (myCurrentFrameFinalLines & 0x1);
}
/**
* The total number of frames. 32 bit should be good for > 2 years :)
*/
uInt32 frameCount() const { return myTotalFrames; }
/**
* The configured (our autodetected) frame layout (PAL / NTSC).
*/
FrameLayout layout() const { return myLayout; }
/**
* The current frame rate. This is calculated dynamically from the number of
* scanlines in the last frames and used to control sleep time in the
* dispatch loop.
*/
float frameRate() const { return myFrameRate; }
/**
* Save state.
*/
bool save(Serializer& out) const override;
/**
* Restore state.
*/
bool load(Serializer& in) override;
public:
// The following methods are implement as noops and should be overriden as
// required. All of these are irrelevant if nothing is displayed (during
// autodetect).
/**
* The jitter factor determines the time jitter simulation takes to recover.
*/
virtual void setJitterFactor(uInt8 factor) {}
/**
* Is jitter simulation enabled?
*/
virtual bool jitterEnabled() const { return false; }
/**
* Enable jitter simulation
*/
virtual void enableJitter(bool enabled) {}
/**
* The scanline difference between the last two frames. Used in the TIA to
* clear any scanlines that were not repainted.
*/
virtual Int32 missingScanlines() const { return 0; }
/**
* Frame height.
*/
virtual uInt32 height() const { return 0; }
/**
* Configure a fixed frame height (the default is determined by the frame
* layout).
*/
virtual void setFixedHeight(uInt32 height) {}
/**
* The current y coordinate (valid only during rendering).
*/
virtual uInt32 getY() const { return 0; }
/**
* The current number of scanlines in the current frame (including invisible
* lines).
*/
virtual uInt32 scanlines() const { return 0; }
/**
* Configure the ystart value.
*/
virtual void setYstart(uInt32 ystart) {}
/**
* The configured ystart value.
*/
virtual uInt32 ystart() const { return 0; }
/**
* Set the frame layout. This may be a noop (on the autodetection manager).
*/
virtual void setLayout(FrameLayout mode) {}
protected:
// The following are template methods that can be implemented to hook into
// the frame logic.
/**
* Called if vblank changes.
*/
virtual void onSetVblank() {}
/**
* Called if vsync changes.
*/
virtual void onSetVsync() {}
/**
* Called if the next line is signalled, after the internal bookkeeping has
* been updated.
*/
virtual void onNextLine() {}
/**
* Called on reset (after the base class has reset).
*/
virtual void onReset() {}
/**
* Called after a frame layout change.
*/
virtual void onLayoutChange() {}
/**
* Called during state save (after the base class has serialized its state).
*/
virtual bool onSave(Serializer& out) const { throw runtime_error("cannot be serialized"); }
/**
* Called during state restore (after the base class has restored its state).
*/
virtual bool onLoad(Serializer& in) { throw runtime_error("cannot be serialized"); }
/**
* This needs to be overriden if state serialization is implemented
* (unnecesary in autodetect managers).
*/
string name() const override { throw runtime_error("state serialization is not implemented!"); }
protected:
// These need to be called in order to drive the frame lifecycle of the
// emulation.
/**
* Signal frame start.
*/
void notifyFrameStart();
/**
* Signal frame stop.
*/
void notifyFrameComplete();
/**
* The internal setter to update the frame layout.
*/
void layout(FrameLayout layout);
protected:
/**
* Rendering flag.
*/
bool myIsRendering;
/**
* Vsync flag.
*/
bool myVsync;
/**
* Vblank flag.
*/
bool myVblank;
/**
* Current scanline count in the current frame.
*/
uInt32 myCurrentFrameTotalLines;
/**
* Total number of scanlines in the last complete frame.
*/
uInt32 myCurrentFrameFinalLines;
/**
* Total number of scanlines in the second last complete frame.
*/
uInt32 myPreviousFrameFinalLines;
/**
* Total frame count.
*/
uInt32 myTotalFrames;
/**
* Frame rate (see above.)
*/
float myFrameRate;
private:
/**
* Current frame layout.
*/
FrameLayout myLayout;
/**
* The various lifecycle callbacks.
*/
callback myOnFrameStart;
callback myOnFrameComplete;
private:
AbstractFrameManager(const AbstractFrameManager&) = delete;
AbstractFrameManager(AbstractFrameManager&&) = delete;
AbstractFrameManager& operator=(const AbstractFrameManager&);
AbstractFrameManager& operator=(AbstractFrameManager&&);
};
#endif // TIA_ABSTRACT_FRAME_MANAGER

View File

@ -0,0 +1,143 @@
//============================================================================
//
// 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 "FrameLayoutDetector.hxx"
#include "TIAConstants.hxx"
/**
* Misc. numeric constants used in the algorithm.
*/
enum Metrics: uInt32 {
frameLinesNTSC = 262,
frameLinesPAL = 312,
waitForVsync = 100,
tvModeDetectionTolerance = 20,
initialGarbageFrames = TIAConstants::initialGarbageFrames
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameLayout FrameLayoutDetector::detectedLayout() const{
// We choose the mode that was detected for the majority of frames.
return myPalFrames > myNtscFrames ? FrameLayout::pal : FrameLayout::ntsc;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::onReset()
{
myState = State::waitForVsyncStart;
myNtscFrames = myPalFrames = 0;
myLinesWaitingForVsyncToStart = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::onSetVsync()
{
if (myVsync)
setState(State::waitForVsyncEnd);
else
setState(State::waitForVsyncStart);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::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::waitForVsyncStart);
break;
default:
throw runtime_error("cannot happen");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::setState(State state)
{
if (state == myState) return;
myState = state;
myLinesWaitingForVsyncToStart = 0;
switch (myState) {
case State::waitForVsyncEnd:
break;
case State::waitForVsyncStart:
finalizeFrame();
notifyFrameStart();
break;
default:
throw new runtime_error("cannot happen");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameLayoutDetector::finalizeFrame()
{
notifyFrameComplete();
if (myTotalFrames <= Metrics::initialGarbageFrames) return;
// Calculate the delta between scanline count and the sweet spot for the respective
// frame layouts
const uInt32
deltaNTSC = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesNTSC)),
deltaPAL = abs(Int32(myCurrentFrameFinalLines) - Int32(frameLinesPAL));
// Does the scanline count fall into one of our tolerance windows? -> use it
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance)
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
else if (
// If scanline count is odd and lies between the PAL and NTSC windows we assume
// it is NTSC (it would cause color loss on PAL CRTs)
(myCurrentFrameFinalLines < frameLinesPAL) &&
(myCurrentFrameFinalLines > frameLinesNTSC) &&
(myCurrentFrameFinalLines % 2)
)
layout(FrameLayout::ntsc);
else
// Take the nearest layout if all else fails
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
switch (layout()) {
case FrameLayout::ntsc:
myNtscFrames++;
break;
case FrameLayout::pal:
myPalFrames++;
break;
default:
throw runtime_error("cannot happen");
}
}

View File

@ -0,0 +1,110 @@
//============================================================================
//
// 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_FRAME_LAYOUT_DETECTOR
#define TIA_FRAME_LAYOUT_DETECTOR
#include "AbstractFrameManager.hxx"
#include "FrameLayout.hxx"
/**
* This frame manager performs frame layout autodetection. It counts the scanlines
* in each frame and assigns guesses the frame layout from this.
*/
class FrameLayoutDetector: public AbstractFrameManager {
public:
FrameLayoutDetector() = default;
public:
/**
* Return the detected frame layout.
*/
FrameLayout detectedLayout() const;
protected:
/**
* Hook into vsync changes.
*/
void onSetVsync() override;
/**
* Hook into reset.
*/
void onReset() override;
/**
* Hook into line changes.
*/
void onNextLine() override;
private:
/**
* This frame manager only tracks frame boundaries, so we have only two states.
*/
enum State {
// Wait for VSYNC to be enabled.
waitForVsyncStart,
// Wait for VSYNC to be disabled.
waitForVsyncEnd
};
private:
/**
* Change state and change internal state accordingly.
*/
void setState(State state);
/**
* Finalize the current frame and guess frame layout from the scanline count.
*/
void finalizeFrame();
private:
/**
* The current state.
*/
State myState;
/**
* The total number of frames detected as the respective frame layout.
*/
uInt32 myNtscFrames, myPalFrames;
/**
* We count the number of scanlines we spend waiting for vsync to be
* toggled. If a threshold is exceeded, we force the transition.
*/
uInt32 myLinesWaitingForVsyncToStart;
private:
FrameLayoutDetector(const FrameLayoutDetector&) = delete;
FrameLayoutDetector(FrameLayoutDetector&&) = delete;
FrameLayoutDetector& operator=(const FrameLayoutDetector&) = delete;
FrameLayoutDetector& operator=(FrameLayoutDetector&&) = delete;
};
#endif // TIA_FRAME_LAYOUT_DETECTOR

View File

@ -0,0 +1,253 @@
//============================================================================
//
// 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.
//============================================================================
// #define TIA_FRAMEMANAGER_DEBUG_LOG
#include <algorithm>
#include "FrameManager.hxx"
enum Metrics: uInt32 {
vblankNTSC = 37,
vblankPAL = 45,
kernelNTSC = 192,
kernelPAL = 228,
overscanNTSC = 30,
overscanPAL = 36,
vsync = 3,
maxLinesVsync = 32,
visibleOverscan = 20,
tvModeDetectionTolerance = 20,
initialGarbageFrames = TIAConstants::initialGarbageFrames,
minStableFrames = 10,
maxStabilizationFrames = 20,
minDeltaForJitter = 3,
framesForStableHeight = 2
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameManager::FrameManager() :
myHeight(0),
myYStart(0)
{
onLayoutChange();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::onReset()
{
myState = State::waitForVsyncStart;
myLineInState = 0;
myTotalFrames = 0;
myVsyncLines = 0;
myY = 0;
myStableFrameLines = -1;
myStableFrameHeightCountdown = 0;
myJitterEmulation.reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::onNextLine()
{
Int32 jitter;
State previousState = myState;
myLineInState++;
switch (myState)
{
case State::waitForVsyncStart:
if ((myCurrentFrameTotalLines > myFrameLines - 3) || myTotalFrames == 0)
myVsyncLines++;
if (myVsyncLines > Metrics::maxLinesVsync) setState(State::waitForFrameStart);
break;
case State::waitForVsyncEnd:
if (++myVsyncLines > Metrics::maxLinesVsync)
setState(State::waitForFrameStart);
break;
case State::waitForFrameStart:
jitter =
(myJitterEnabled && myTotalFrames > Metrics::initialGarbageFrames) ? myJitterEmulation.jitter() : 0;
if (myLineInState >= (myYStart + jitter)) setState(State::frame);
break;
case State::frame:
if (myLineInState >= myHeight)
{
myLastY = ystart() + myY; // Last line drawn in this frame
setState(State::waitForVsyncStart);
}
break;
default:
throw runtime_error("frame manager: invalid state");
}
if (myState == State::frame && previousState == State::frame) myY++;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int32 FrameManager::missingScanlines() const
{
if (myLastY == myYStart + myY)
return 0;
else {
return myHeight - myY;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setYstart(uInt32 ystart)
{
myYStart = ystart;
myJitterEmulation.setYStart(ystart);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::onSetVsync()
{
if (myState == State::waitForVsyncEnd) setState(State::waitForFrameStart);
else setState(State::waitForVsyncEnd);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setState(FrameManager::State state)
{
if (myState == state) return;
myState = state;
myLineInState = 0;
switch (myState) {
case State::waitForFrameStart:
notifyFrameComplete();
myJitterEmulation.frameComplete(myCurrentFrameFinalLines);
notifyFrameStart();
myVsyncLines = 0;
break;
case State::frame:
myVsyncLines = 0;
myY = 0;
break;
default:
break;
}
updateIsRendering();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TODO: kill this with fire once frame manager refactoring is complete
void FrameManager::onLayoutChange()
{
switch (layout())
{
case FrameLayout::ntsc:
myVblankLines = Metrics::vblankNTSC;
myKernelLines = Metrics::kernelNTSC;
myOverscanLines = Metrics::overscanNTSC;
break;
case FrameLayout::pal:
myVblankLines = Metrics::vblankPAL;
myKernelLines = Metrics::kernelPAL;
myOverscanLines = Metrics::overscanPAL;
break;
default:
throw runtime_error("frame manager: invalid TV mode");
}
myFrameLines = Metrics::vsync + myVblankLines + myKernelLines + myOverscanLines;
if (myFixedHeight == 0)
myHeight = myKernelLines + Metrics::visibleOverscan;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setFixedHeight(uInt32 height)
{
myFixedHeight = height;
myHeight = myFixedHeight > 0 ? myFixedHeight : (myKernelLines + Metrics::visibleOverscan);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::updateIsRendering() {
myIsRendering = myState == State::frame;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::onSave(Serializer& out) const
{
if (!myJitterEmulation.save(out)) return false;
out.putInt(uInt32(myState));
out.putInt(myLineInState);
out.putInt(myVsyncLines);
out.putInt(myY);
out.putInt(myLastY);
out.putInt(myVblankLines);
out.putInt(myKernelLines);
out.putInt(myOverscanLines);
out.putInt(myFrameLines);
out.putInt(myHeight);
out.putInt(myFixedHeight);
out.putBool(myJitterEnabled);
out.putInt(myStableFrameLines);
out.putInt(myStableFrameHeightCountdown);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameManager::onLoad(Serializer& in)
{
if (!myJitterEmulation.load(in)) return false;
myState = State(in.getInt());
myLineInState = in.getInt();
myVsyncLines = in.getInt();
myY = in.getInt();
myLastY = in.getInt();
myVblankLines = in.getInt();
myKernelLines = in.getInt();
myOverscanLines = in.getInt();
myFrameLines = in.getInt();
myHeight = in.getInt();
myFixedHeight = in.getInt();
myJitterEnabled = in.getBool();
myStableFrameLines = in.getInt();
myStableFrameHeightCountdown = in.getInt();
return true;
}

View File

@ -0,0 +1,116 @@
//============================================================================
//
// 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_FRAME_MANAGER
#define TIA_FRAME_MANAGER
#include "AbstractFrameManager.hxx"
#include "TIAConstants.hxx"
#include "bspf.hxx"
#include "JitterEmulation.hxx"
class FrameManager: public AbstractFrameManager {
public:
FrameManager();
public:
void setJitterFactor(uInt8 factor) override { myJitterEmulation.setJitterFactor(factor); }
bool jitterEnabled() const override { return myJitterEnabled; }
void enableJitter(bool enabled) override { myJitterEnabled = enabled; }
uInt32 height() const override { return myHeight; }
void setFixedHeight(uInt32 height) override;
uInt32 getY() const override { return myY; }
uInt32 scanlines() const override { return myCurrentFrameTotalLines; }
Int32 missingScanlines() const override;
void setYstart(uInt32 ystart) override;
uInt32 ystart() const override { return myYStart; }
void setLayout(FrameLayout mode) override { layout(mode); }
void onSetVsync() override;
void onNextLine() override;
void onReset() override;
void onLayoutChange() override;
bool onSave(Serializer& out) const override;
bool onLoad(Serializer& in) override;
string name() const override { return "TIA_FrameManager"; }
private:
enum State {
waitForVsyncStart,
waitForVsyncEnd,
waitForFrameStart,
frame
};
private:
void updateAutodetectedLayout();
void setState(State state);
void updateIsRendering();
private:
State myState;
uInt32 myLineInState;
uInt32 myVsyncLines;
uInt32 myY, myLastY;
uInt32 myVblankLines;
uInt32 myKernelLines;
uInt32 myOverscanLines;
uInt32 myFrameLines;
uInt32 myHeight;
uInt32 myFixedHeight;
uInt32 myYStart;
bool myJitterEnabled;
Int32 myStableFrameLines;
uInt8 myStableFrameHeightCountdown;
JitterEmulation myJitterEmulation;
private:
FrameManager(const FrameManager&) = delete;
FrameManager(FrameManager&&) = delete;
FrameManager& operator=(const FrameManager&) = delete;
FrameManager& operator=(FrameManager&&) = delete;
};
#endif // TIA_FRAME_MANAGER

View File

@ -0,0 +1,123 @@
//============================================================================
//
// 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 "JitterEmulation.hxx"
enum Metrics: uInt32 {
framesForStableHeight = 2,
minDeltaForJitter = 3,
maxJitter = 50
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JitterEmulation::JitterEmulation() :
myYStart(0)
{}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::reset()
{
myLastFrameScanlines = 0;
myStableFrameFinalLines = 0;
myStableFrames = 0;
myStabilizationCounter = 0;
myJitter = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::frameComplete(uInt32 scanlineCount)
{
if (scanlineCount != myStableFrameFinalLines) {
if (scanlineCount == myLastFrameScanlines) {
if (++myStabilizationCounter >= Metrics::framesForStableHeight) {
if (myStableFrameFinalLines > 0) updateJitter(scanlineCount - myStableFrameFinalLines);
myStableFrameFinalLines = scanlineCount;
}
}
else myStabilizationCounter = 0;
}
myLastFrameScanlines = scanlineCount;
if (myJitter > 0) myJitter = std::max(myJitter - myJitterFactor, 0);
if (myJitter < 0) myJitter = std::min(myJitter + myJitterFactor, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::updateJitter(Int32 scanlineDifference)
{
if (uInt32(abs(scanlineDifference)) < Metrics::minDeltaForJitter) return;
Int32 jitter = std::min<Int32>(jitter, Metrics::maxJitter);
jitter = std::max<Int32>(jitter, -myYStart);
if (jitter > 0) jitter += myJitterFactor;
if (jitter < 0) jitter -= myJitterFactor;
if (abs(jitter) > abs(myJitter)) myJitter = jitter;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JitterEmulation::save(Serializer& out) const
{
try {
out.putString(name());
out.putInt(myLastFrameScanlines);
out.putInt(myStableFrameFinalLines);
out.putInt(myStableFrames);
out.putInt(myStabilizationCounter);
out.putInt(myJitter);
out.putInt(myJitterFactor);
out.putInt(myYStart);
}
catch(...)
{
cerr << "ERROR: JitterEmulation::save" << std::endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JitterEmulation::load(Serializer& in)
{
try {
if (in.getString() != name()) return false;
myLastFrameScanlines = in.getInt();
myStableFrameFinalLines = in.getInt();
myStableFrames = in.getInt();
myStabilizationCounter = in.getInt();
myJitter = in.getInt();
myJitterFactor = in.getInt();
myYStart = in.getInt();
}
catch (...)
{
cerr << "ERROR: JitterEmulation::load" << std::endl;
return false;
}
return true;
}

View File

@ -0,0 +1,81 @@
//============================================================================
//
// 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_JITTER_EMULATION
#define TIA_JITTER_EMULATION
#include "bspf.hxx"
#include "Serializable.hxx"
class JitterEmulation: public Serializable {
public:
JitterEmulation();
public:
void reset();
void frameComplete(uInt32 scanlineCount);
void setJitterFactor(Int32 factor) { myJitterFactor = factor; }
Int32 jitter() const { return myJitter; }
void setYStart(uInt32 ystart) { myYStart = ystart; }
/**
* Save state.
*/
bool save(Serializer& out) const override;
/**
* Restore state.
*/
bool load(Serializer& in) override;
string name() const override { return "JitterEmulation"; }
private:
void updateJitter(Int32 scanlineDifference);
private:
uInt32 myLastFrameScanlines;
uInt32 myStableFrameFinalLines;
uInt32 myStableFrames;
uInt32 myStabilizationCounter;
Int32 myJitter;
Int32 myJitterFactor;
uInt32 myYStart;
private:
JitterEmulation(const JitterEmulation&) = delete;
JitterEmulation(JitterEmulation&&) = delete;
JitterEmulation& operator=(const JitterEmulation&) = delete;
JitterEmulation& operator=(JitterEmulation&&) = delete;
};
#endif // TIA_JITTER_EMULATION

View File

@ -0,0 +1,14 @@
MODULE := src/emucore/tia/frame-manager
MODULE_OBJS := \
src/emucore/tia/frame-manager/FrameManager.o \
src/emucore/tia/frame-manager/AbstractFrameManager.o \
src/emucore/tia/frame-manager/FrameLayoutDetector.o \
src/emucore/tia/frame-manager/YStartDetector.o \
src/emucore/tia/frame-manager/JitterEmulation.o
MODULE_DIRS += \
src/emucore/tia/frame-manager
# Include common rules
include $(srcdir)/common.rules

View File

@ -2,7 +2,6 @@ MODULE := src/emucore/tia
MODULE_OBJS := \
src/emucore/tia/TIA.o \
src/emucore/tia/FrameManager.o \
src/emucore/tia/Playfield.o \
src/emucore/tia/DrawCounterDecodes.o \
src/emucore/tia/Missile.o \
@ -10,8 +9,7 @@ MODULE_OBJS := \
src/emucore/tia/Ball.o \
src/emucore/tia/Background.o \
src/emucore/tia/LatchedInput.o \
src/emucore/tia/PaddleReader.o \
src/emucore/tia/VblankManager.o
src/emucore/tia/PaddleReader.o
MODULE_DIRS += \
src/emucore/tia

View File

@ -27,7 +27,7 @@
#include "Props.hxx"
#include "PropsSet.hxx"
#include "TabWidget.hxx"
#include "FrameManager.hxx"
#include "TIAConstants.hxx"
#include "Widget.hxx"
#include "GameInfoDialog.hxx"
@ -310,8 +310,8 @@ GameInfoDialog::GameInfoDialog(
t = new StaticTextWidget(myTab, font, hSpace, ypos+1, "YStart ", kTextAlignLeft);
myYStart = new SliderWidget(myTab, font, t->getRight(), ypos, 8*fontWidth, lineHeight,
"", 0, kYStartChanged);
myYStart->setMinValue(FrameManager::minYStart-1);
myYStart->setMaxValue(FrameManager::maxYStart);
myYStart->setMinValue(TIAConstants::minYStart-1);
myYStart->setMaxValue(TIAConstants::maxYStart);
wid.push_back(myYStart);
myYStartLabel = new StaticTextWidget(myTab, font, myYStart->getRight() + 4,
ypos+1, 5*fontWidth, fontHeight, "", kTextAlignLeft);
@ -321,8 +321,8 @@ GameInfoDialog::GameInfoDialog(
t = new StaticTextWidget(myTab, font, hSpace, ypos+1, "Height ", kTextAlignLeft);
myHeight = new SliderWidget(myTab, font, t->getRight(), ypos, 8*fontWidth, lineHeight,
"", 0, kHeightChanged);
myHeight->setMinValue(FrameManager::minViewableHeight-1);
myHeight->setMaxValue(FrameManager::maxViewableHeight);
myHeight->setMinValue(TIAConstants::minViewableHeight-1);
myHeight->setMaxValue(TIAConstants::maxViewableHeight);
wid.push_back(myHeight);
myHeightLabel = new StaticTextWidget(myTab, font, myHeight->getRight() + 4,
ypos+1, 5*fontWidth, fontHeight, "", kTextAlignLeft);
@ -647,14 +647,14 @@ void GameInfoDialog::handleCommand(CommandSender* sender, int cmd,
}
case kYStartChanged:
if(myYStart->getValue() == FrameManager::minYStart-1)
if(myYStart->getValue() == TIAConstants::minYStart-1)
myYStartLabel->setLabel("Auto");
else
myYStartLabel->setValue(myYStart->getValue());
break;
case kHeightChanged:
if(myHeight->getValue() == FrameManager::minViewableHeight-1)
if(myHeight->getValue() == TIAConstants::minViewableHeight-1)
myHeightLabel->setLabel("Auto");
else
myHeightLabel->setValue(myHeight->getValue());

View File

@ -19,6 +19,7 @@
#include "OSystem.hxx"
#include "Settings.hxx"
#include "Widget.hxx"
#include "TIAConstants.hxx"
#include "RomInfoWidget.hxx"
@ -28,8 +29,8 @@ RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font,
: Widget(boss, font, x, y, w, h),
mySurfaceIsValid(false),
myHaveProperties(false),
myAvail(w > 400 ? GUI::Size(640, FrameManager::maxViewableHeight*2) :
GUI::Size(320, FrameManager::maxViewableHeight))
myAvail(w > 400 ? GUI::Size(640, TIAConstants::maxViewableHeight*2) :
GUI::Size(320, TIAConstants::maxViewableHeight))
{
_flags = WIDGET_ENABLED;
_bgcolor = _bgcolorhi = kWidColor;
@ -76,7 +77,7 @@ void RomInfoWidget::parseProperties()
// only draw certain parts of it
if(mySurface == nullptr)
{
mySurface = instance().frameBuffer().allocateSurface(320*2, FrameManager::maxViewableHeight*2);
mySurface = instance().frameBuffer().allocateSurface(320*2, TIAConstants::maxViewableHeight*2);
mySurface->attributes().smoothing = true;
mySurface->applyAttributes();

View File

@ -366,8 +366,6 @@
DC6C726313CDEA0A008A5975 /* LoggerDialog.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6C726113CDEA0A008A5975 /* LoggerDialog.hxx */; };
DC6D39871A3CE65000171E71 /* CartWDWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC6D39851A3CE65000171E71 /* CartWDWidget.cxx */; };
DC6D39881A3CE65000171E71 /* CartWDWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6D39861A3CE65000171E71 /* CartWDWidget.hxx */; };
DC72B2221E356F4F009056D0 /* VblankManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC72B2201E356F4F009056D0 /* VblankManager.cxx */; };
DC72B2231E356F4F009056D0 /* VblankManager.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC72B2211E356F4F009056D0 /* VblankManager.hxx */; };
DC73BD851915E5B1003FAFAD /* FBSurfaceSDL2.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC73BD831915E5B1003FAFAD /* FBSurfaceSDL2.cxx */; };
DC73BD861915E5B1003FAFAD /* FBSurfaceSDL2.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC73BD841915E5B1003FAFAD /* FBSurfaceSDL2.hxx */; };
DC73BD891915E5E3003FAFAD /* FBSurface.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC73BD871915E5E3003FAFAD /* FBSurface.cxx */; };
@ -563,8 +561,6 @@
DCF3A6EE1DFC75E3008A8AF3 /* DelayQueueMember.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCF3A6D41DFC75E3008A8AF3 /* DelayQueueMember.hxx */; };
DCF3A6EF1DFC75E3008A8AF3 /* DrawCounterDecodes.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCF3A6D51DFC75E3008A8AF3 /* DrawCounterDecodes.cxx */; };
DCF3A6F01DFC75E3008A8AF3 /* DrawCounterDecodes.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCF3A6D61DFC75E3008A8AF3 /* DrawCounterDecodes.hxx */; };
DCF3A6F11DFC75E3008A8AF3 /* FrameManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCF3A6D71DFC75E3008A8AF3 /* FrameManager.cxx */; };
DCF3A6F21DFC75E3008A8AF3 /* FrameManager.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCF3A6D81DFC75E3008A8AF3 /* FrameManager.hxx */; };
DCF3A6F31DFC75E3008A8AF3 /* LatchedInput.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCF3A6D91DFC75E3008A8AF3 /* LatchedInput.cxx */; };
DCF3A6F41DFC75E3008A8AF3 /* LatchedInput.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCF3A6DA1DFC75E3008A8AF3 /* LatchedInput.hxx */; };
DCF3A6F51DFC75E3008A8AF3 /* Missile.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCF3A6DB1DFC75E3008A8AF3 /* Missile.cxx */; };
@ -594,6 +590,16 @@
DCFF14CE18B0260300A20364 /* EventHandlerSDL2.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCFF14CC18B0260300A20364 /* EventHandlerSDL2.hxx */; };
DCFFE59D12100E1400DFA000 /* ComboDialog.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCFFE59B12100E1400DFA000 /* ComboDialog.cxx */; };
DCFFE59E12100E1400DFA000 /* ComboDialog.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCFFE59C12100E1400DFA000 /* ComboDialog.hxx */; };
E0306E0C1F93E916003DDD52 /* YStartDetector.cxx in Sources */ = {isa = PBXBuildFile; fileRef = E0306E061F93E915003DDD52 /* YStartDetector.cxx */; };
E0306E0D1F93E916003DDD52 /* FrameLayoutDetector.hxx in Headers */ = {isa = PBXBuildFile; fileRef = E0306E071F93E915003DDD52 /* FrameLayoutDetector.hxx */; };
E0306E0E1F93E916003DDD52 /* YStartDetector.hxx in Headers */ = {isa = PBXBuildFile; fileRef = E0306E081F93E915003DDD52 /* YStartDetector.hxx */; };
E0306E0F1F93E916003DDD52 /* JitterEmulation.cxx in Sources */ = {isa = PBXBuildFile; fileRef = E0306E091F93E915003DDD52 /* JitterEmulation.cxx */; };
E0306E101F93E916003DDD52 /* FrameLayoutDetector.cxx in Sources */ = {isa = PBXBuildFile; fileRef = E0306E0A1F93E916003DDD52 /* FrameLayoutDetector.cxx */; };
E0306E111F93E916003DDD52 /* JitterEmulation.hxx in Headers */ = {isa = PBXBuildFile; fileRef = E0306E0B1F93E916003DDD52 /* JitterEmulation.hxx */; };
E0406FB61F81A85400A82AE0 /* AbstractFrameManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = E0DFDD781F81A358000F3505 /* AbstractFrameManager.cxx */; };
E0406FB71F81A85400A82AE0 /* AbstractFrameManager.hxx in Sources */ = {isa = PBXBuildFile; fileRef = E0DFDD791F81A358000F3505 /* AbstractFrameManager.hxx */; };
E0406FB81F81A85400A82AE0 /* FrameManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = E0DFDD7B1F81A358000F3505 /* FrameManager.cxx */; };
E0406FB91F81A85400A82AE0 /* FrameManager.hxx in Sources */ = {isa = PBXBuildFile; fileRef = E0DFDD7C1F81A358000F3505 /* FrameManager.hxx */; };
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
@ -604,6 +610,7 @@
isEditable = 1;
outputFiles = (
);
script = "";
};
DC5EE7DF14F7C32D001C628C /* PBXBuildRule */ = {
isa = PBXBuildRule;
@ -612,6 +619,7 @@
isEditable = 1;
outputFiles = (
);
script = "";
};
DC5EE7E014F7C32D001C628C /* PBXBuildRule */ = {
isa = PBXBuildRule;
@ -620,6 +628,7 @@
isEditable = 1;
outputFiles = (
);
script = "";
};
/* End PBXBuildRule section */
@ -1004,8 +1013,6 @@
DC6C726113CDEA0A008A5975 /* LoggerDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoggerDialog.hxx; sourceTree = "<group>"; };
DC6D39851A3CE65000171E71 /* CartWDWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartWDWidget.cxx; sourceTree = "<group>"; };
DC6D39861A3CE65000171E71 /* CartWDWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartWDWidget.hxx; sourceTree = "<group>"; };
DC72B2201E356F4F009056D0 /* VblankManager.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VblankManager.cxx; sourceTree = "<group>"; };
DC72B2211E356F4F009056D0 /* VblankManager.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VblankManager.hxx; sourceTree = "<group>"; };
DC73BD831915E5B1003FAFAD /* FBSurfaceSDL2.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FBSurfaceSDL2.cxx; sourceTree = "<group>"; };
DC73BD841915E5B1003FAFAD /* FBSurfaceSDL2.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FBSurfaceSDL2.hxx; sourceTree = "<group>"; };
DC73BD871915E5E3003FAFAD /* FBSurface.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FBSurface.cxx; sourceTree = "<group>"; };
@ -1203,8 +1210,6 @@
DCF3A6D41DFC75E3008A8AF3 /* DelayQueueMember.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DelayQueueMember.hxx; sourceTree = "<group>"; };
DCF3A6D51DFC75E3008A8AF3 /* DrawCounterDecodes.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DrawCounterDecodes.cxx; sourceTree = "<group>"; };
DCF3A6D61DFC75E3008A8AF3 /* DrawCounterDecodes.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DrawCounterDecodes.hxx; sourceTree = "<group>"; };
DCF3A6D71DFC75E3008A8AF3 /* FrameManager.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FrameManager.cxx; sourceTree = "<group>"; };
DCF3A6D81DFC75E3008A8AF3 /* FrameManager.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FrameManager.hxx; sourceTree = "<group>"; };
DCF3A6D91DFC75E3008A8AF3 /* LatchedInput.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LatchedInput.cxx; sourceTree = "<group>"; };
DCF3A6DA1DFC75E3008A8AF3 /* LatchedInput.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LatchedInput.hxx; sourceTree = "<group>"; };
DCF3A6DB1DFC75E3008A8AF3 /* Missile.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Missile.cxx; sourceTree = "<group>"; };
@ -1234,6 +1239,16 @@
DCFF14CC18B0260300A20364 /* EventHandlerSDL2.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EventHandlerSDL2.hxx; sourceTree = "<group>"; };
DCFFE59B12100E1400DFA000 /* ComboDialog.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComboDialog.cxx; sourceTree = "<group>"; };
DCFFE59C12100E1400DFA000 /* ComboDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ComboDialog.hxx; sourceTree = "<group>"; };
E0306E061F93E915003DDD52 /* YStartDetector.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YStartDetector.cxx; sourceTree = "<group>"; };
E0306E071F93E915003DDD52 /* FrameLayoutDetector.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FrameLayoutDetector.hxx; sourceTree = "<group>"; };
E0306E081F93E915003DDD52 /* YStartDetector.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = YStartDetector.hxx; sourceTree = "<group>"; };
E0306E091F93E915003DDD52 /* JitterEmulation.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JitterEmulation.cxx; sourceTree = "<group>"; };
E0306E0A1F93E916003DDD52 /* FrameLayoutDetector.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FrameLayoutDetector.cxx; sourceTree = "<group>"; };
E0306E0B1F93E916003DDD52 /* JitterEmulation.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = JitterEmulation.hxx; sourceTree = "<group>"; };
E0DFDD781F81A358000F3505 /* AbstractFrameManager.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractFrameManager.cxx; sourceTree = "<group>"; };
E0DFDD791F81A358000F3505 /* AbstractFrameManager.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AbstractFrameManager.hxx; sourceTree = "<group>"; };
E0DFDD7B1F81A358000F3505 /* FrameManager.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FrameManager.cxx; sourceTree = "<group>"; };
E0DFDD7C1F81A358000F3505 /* FrameManager.hxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FrameManager.hxx; sourceTree = "<group>"; };
F5A47A9D01A0482F01D3D55B /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SDLMain.h; sourceTree = SOURCE_ROOT; };
F5A47A9E01A0483001D3D55B /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SDLMain.m; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
@ -1975,6 +1990,7 @@
DCE903E31DF5DCD10080A7F3 /* tia */ = {
isa = PBXGroup;
children = (
E0DFDD731F81A358000F3505 /* frame-manager */,
DCF3A6CD1DFC75E3008A8AF3 /* Background.cxx */,
DCF3A6CE1DFC75E3008A8AF3 /* Background.hxx */,
DCF3A6CF1DFC75E3008A8AF3 /* Ball.cxx */,
@ -1986,8 +2002,6 @@
DCF3A6D51DFC75E3008A8AF3 /* DrawCounterDecodes.cxx */,
DCF3A6D61DFC75E3008A8AF3 /* DrawCounterDecodes.hxx */,
DCE8B1861E7E03B300189864 /* FrameLayout.hxx */,
DCF3A6D71DFC75E3008A8AF3 /* FrameManager.cxx */,
DCF3A6D81DFC75E3008A8AF3 /* FrameManager.hxx */,
DCF3A6D91DFC75E3008A8AF3 /* LatchedInput.cxx */,
DCF3A6DA1DFC75E3008A8AF3 /* LatchedInput.hxx */,
DCF3A6DB1DFC75E3008A8AF3 /* Missile.cxx */,
@ -2000,12 +2014,27 @@
DCF3A6E31DFC75E3008A8AF3 /* Playfield.hxx */,
DCF3A6E41DFC75E3008A8AF3 /* TIA.cxx */,
DCF3A6E51DFC75E3008A8AF3 /* TIA.hxx */,
DC72B2201E356F4F009056D0 /* VblankManager.cxx */,
DC72B2211E356F4F009056D0 /* VblankManager.hxx */,
);
path = tia;
sourceTree = "<group>";
};
E0DFDD731F81A358000F3505 /* frame-manager */ = {
isa = PBXGroup;
children = (
E0306E0A1F93E916003DDD52 /* FrameLayoutDetector.cxx */,
E0306E071F93E915003DDD52 /* FrameLayoutDetector.hxx */,
E0306E091F93E915003DDD52 /* JitterEmulation.cxx */,
E0306E0B1F93E916003DDD52 /* JitterEmulation.hxx */,
E0306E061F93E915003DDD52 /* YStartDetector.cxx */,
E0306E081F93E915003DDD52 /* YStartDetector.hxx */,
E0DFDD781F81A358000F3505 /* AbstractFrameManager.cxx */,
E0DFDD791F81A358000F3505 /* AbstractFrameManager.hxx */,
E0DFDD7B1F81A358000F3505 /* FrameManager.cxx */,
E0DFDD7C1F81A358000F3505 /* FrameManager.hxx */,
);
path = "frame-manager";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -2104,7 +2133,6 @@
2D91742309BA90380026E9FF /* DebuggerParser.hxx in Headers */,
2D91742409BA90380026E9FF /* EditableWidget.hxx in Headers */,
DC3EE86F1E2C0E6D00905161 /* zutil.h in Headers */,
DCF3A6F21DFC75E3008A8AF3 /* FrameManager.hxx in Headers */,
2D91742509BA90380026E9FF /* EditTextWidget.hxx in Headers */,
DCB87E581A104C1E00BF2A3B /* MediaFactory.hxx in Headers */,
2D91742809BA90380026E9FF /* PackedBitArray.hxx in Headers */,
@ -2126,6 +2154,7 @@
2D91745209BA90380026E9FF /* CommandDialog.hxx in Headers */,
2D91745309BA90380026E9FF /* CommandMenu.hxx in Headers */,
DCB2ECAE1F0AECA3009738A6 /* BSType.hxx in Headers */,
E0306E111F93E916003DDD52 /* JitterEmulation.hxx in Headers */,
2D91745509BA90380026E9FF /* CpuWidget.hxx in Headers */,
2D91745609BA90380026E9FF /* DataGridOpsWidget.hxx in Headers */,
2D91745709BA90380026E9FF /* DataGridWidget.hxx in Headers */,
@ -2161,6 +2190,7 @@
DC47455D09C34BFA00EDDA3A /* CheetahCheat.hxx in Headers */,
DC47455F09C34BFA00EDDA3A /* RamCheat.hxx in Headers */,
DCD56D390B247D920092F9F8 /* Cart4A50.hxx in Headers */,
E0306E0D1F93E916003DDD52 /* FrameLayoutDetector.hxx in Headers */,
DC8078DB0B4BD5F3005E9305 /* DebuggerExpressions.hxx in Headers */,
DC8078EB0B4BD697005E9305 /* UIDialog.hxx in Headers */,
DCEECE570B5E5E540021D754 /* Cart0840.hxx in Headers */,
@ -2199,7 +2229,6 @@
DCF7B0DE10A762FC007A2870 /* CartF0.hxx in Headers */,
DCF7B0E010A762FC007A2870 /* CartFA.hxx in Headers */,
DCC527D110B9DA19005E1287 /* Device.hxx in Headers */,
DC72B2231E356F4F009056D0 /* VblankManager.hxx in Headers */,
DCC527D310B9DA19005E1287 /* M6502.hxx in Headers */,
DC3EE8661E2C0E6D00905161 /* inflate.h in Headers */,
DCC527D510B9DA19005E1287 /* NullDev.hxx in Headers */,
@ -2309,6 +2338,7 @@
DCAACB13188D636F00A4D282 /* CartBFWidget.hxx in Headers */,
DCAACB15188D636F00A4D282 /* CartDFSCWidget.hxx in Headers */,
DC44019F1F1A5D01008C08F6 /* ColorWidget.hxx in Headers */,
E0306E0E1F93E916003DDD52 /* YStartDetector.hxx in Headers */,
DC96162D1F817830008A2206 /* AmigaMouseWidget.hxx in Headers */,
DCAACB17188D636F00A4D282 /* CartDFWidget.hxx in Headers */,
DCF3A6FF1DFC75E3008A8AF3 /* TIA.hxx in Headers */,
@ -2416,6 +2446,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E0406FB61F81A85400A82AE0 /* AbstractFrameManager.cxx in Sources */,
E0406FB71F81A85400A82AE0 /* AbstractFrameManager.hxx in Sources */,
E0406FB81F81A85400A82AE0 /* FrameManager.cxx in Sources */,
E0406FB91F81A85400A82AE0 /* FrameManager.hxx in Sources */,
2D91747409BA90380026E9FF /* SDLMain.m in Sources */,
2D91747509BA90380026E9FF /* Booster.cxx in Sources */,
DC3EE8671E2C0E6D00905161 /* inftrees.c in Sources */,
@ -2441,6 +2475,7 @@
2D91748909BA90380026E9FF /* Console.cxx in Sources */,
2D91748A09BA90380026E9FF /* Control.cxx in Sources */,
2D91748C09BA90380026E9FF /* Driving.cxx in Sources */,
E0306E101F93E916003DDD52 /* FrameLayoutDetector.cxx in Sources */,
2D91748E09BA90380026E9FF /* Joystick.cxx in Sources */,
2D91748F09BA90380026E9FF /* Keyboard.cxx in Sources */,
2D91749009BA90380026E9FF /* M6532.cxx in Sources */,
@ -2584,7 +2619,6 @@
DCAD60A81152F8BD00BC4184 /* CartDPCPlus.cxx in Sources */,
DCD6FC7011C281ED005DA767 /* png.c in Sources */,
DCD6FC7311C281ED005DA767 /* pngerror.c in Sources */,
DCF3A6F11DFC75E3008A8AF3 /* FrameManager.cxx in Sources */,
DCF3A6E71DFC75E3008A8AF3 /* Background.cxx in Sources */,
DCD6FC7411C281ED005DA767 /* pngget.c in Sources */,
DCD6FC7511C281ED005DA767 /* pngmem.c in Sources */,
@ -2595,6 +2629,7 @@
DCD6FC7A11C281ED005DA767 /* pngrtran.c in Sources */,
DC3EE85C1E2C0E6D00905161 /* gzclose.c in Sources */,
DCD6FC7B11C281ED005DA767 /* pngrutil.c in Sources */,
E0306E0F1F93E916003DDD52 /* JitterEmulation.cxx in Sources */,
DCD6FC7C11C281ED005DA767 /* pngset.c in Sources */,
DCD6FC7E11C281ED005DA767 /* pngtrans.c in Sources */,
DCD6FC7F11C281ED005DA767 /* pngwio.c in Sources */,
@ -2625,6 +2660,7 @@
DCF3A6F31DFC75E3008A8AF3 /* LatchedInput.cxx in Sources */,
DC67270B1556F4860023653B /* CartCTY.cxx in Sources */,
DCE395F016CB0B5F008DB1E5 /* FSNodeZIP.cxx in Sources */,
E0306E0C1F93E916003DDD52 /* YStartDetector.cxx in Sources */,
DCE395F216CB0B5F008DB1E5 /* ZipHandler.cxx in Sources */,
DCAAE5D31715887B0080BB82 /* Cart2KWidget.cxx in Sources */,
DCAAE5D51715887B0080BB82 /* Cart3FWidget.cxx in Sources */,
@ -2644,7 +2680,6 @@
DCAAE5E81715887B0080BB82 /* CartF6SCWidget.cxx in Sources */,
DCAAE5EA1715887B0080BB82 /* CartF6Widget.cxx in Sources */,
DCAAE5EC1715887B0080BB82 /* CartF8SCWidget.cxx in Sources */,
DC72B2221E356F4F009056D0 /* VblankManager.cxx in Sources */,
DCAAE5EE1715887B0080BB82 /* CartF8Widget.cxx in Sources */,
DCAAE5F01715887B0080BB82 /* CartFAWidget.cxx in Sources */,
DCAAE5F21715887B0080BB82 /* CartUAWidget.cxx in Sources */,
@ -2736,6 +2771,7 @@
../emucore,
../gui,
../yacc,
../emucore/tia,
.,
);
INFOPLIST_FILE = "Info-Stella.plist";
@ -2788,6 +2824,7 @@
../emucore,
../gui,
../yacc,
../emucore/tia,
.,
);
INFOPLIST_FILE = "Info-Stella.plist";