Ystart is sleeping with the fishes.

This commit is contained in:
Christian Speckner 2019-12-19 18:01:54 +01:00
parent 356f06598d
commit ec35026329
17 changed files with 78 additions and 556 deletions

3
.gitignore vendored
View File

@ -30,5 +30,4 @@ src/windows/Stella.vcxproj.user
src/**/*.psess src/**/*.psess
src/**/*.vspx src/**/*.vspx
src/**/**.pdb src/**/**.pdb
Stella.xcscheme

View File

@ -120,7 +120,7 @@ void TiaOutputWidget::handleMouseDown(int x, int y, MouseButton b, int clickCoun
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, int id) void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{ {
uInt32 ystart = instance().console().tia().ystart(); uInt32 startLine = instance().console().tia().startLine();
switch(cmd) switch(cmd)
{ {
@ -131,7 +131,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in
if(rmb == "scanline") if(rmb == "scanline")
{ {
ostringstream command; ostringstream command;
int lines = myClickY + ystart - instance().console().tia().scanlines(); int lines = myClickY + startLine - instance().console().tia().scanlines();
if(lines < 0) if(lines < 0)
lines += instance().console().tia().scanlinesLastFrame(); lines += instance().console().tia().scanlinesLastFrame();
@ -145,7 +145,7 @@ void TiaOutputWidget::handleCommand(CommandSender* sender, int cmd, int data, in
else if(rmb == "bp") else if(rmb == "bp")
{ {
ostringstream command; ostringstream command;
int scanline = myClickY + ystart; int scanline = myClickY + startLine;
command << "breakif _scan==#" << scanline; command << "breakif _scan==#" << scanline;
string message = instance().debugger().parser().run(command.str()); string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message); instance().frameBuffer().showMessage(message);

View File

@ -249,13 +249,13 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int
{ {
case ContextMenu::kItemSelectedCmd: case ContextMenu::kItemSelectedCmd:
{ {
uInt32 ystart = instance().console().tia().ystart(); uInt32 startLine = instance().console().tia().startLine();
const string& rmb = myMenu->getSelectedTag().toString(); const string& rmb = myMenu->getSelectedTag().toString();
if(rmb == "scanline") if(rmb == "scanline")
{ {
ostringstream command; ostringstream command;
int lines = myClickY / myZoomLevel + myOffY + ystart - instance().console().tia().scanlines(); int lines = myClickY / myZoomLevel + myOffY + startLine - instance().console().tia().scanlines();
if (lines < 0) if (lines < 0)
lines += instance().console().tia().scanlinesLastFrame(); lines += instance().console().tia().scanlinesLastFrame();
@ -269,7 +269,7 @@ void TiaZoomWidget::handleCommand(CommandSender* sender, int cmd, int data, int
else if(rmb == "bp") else if(rmb == "bp")
{ {
ostringstream command; ostringstream command;
int scanline = myClickY / myZoomLevel + myOffY + ystart; int scanline = myClickY / myZoomLevel + myOffY + startLine;
command << "breakif _scan==#" << scanline; command << "breakif _scan==#" << scanline;
string message = instance().debugger().parser().run(command.str()); string message = instance().debugger().parser().run(command.str());
instance().frameBuffer().showMessage(message); instance().frameBuffer().showMessage(message);

View File

@ -60,7 +60,6 @@
#include "AudioSettings.hxx" #include "AudioSettings.hxx"
#include "frame-manager/FrameManager.hxx" #include "frame-manager/FrameManager.hxx"
#include "frame-manager/FrameLayoutDetector.hxx" #include "frame-manager/FrameLayoutDetector.hxx"
#include "frame-manager/YStartDetector.hxx"
#ifdef CHEATCODE_SUPPORT #ifdef CHEATCODE_SUPPORT
#include "CheatManager.hxx" #include "CheatManager.hxx"
@ -71,10 +70,6 @@
#include "Console.hxx" #include "Console.hxx"
namespace {
constexpr uInt8 YSTART_EXTRA = 2;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart, Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
const Properties& props, AudioSettings& audioSettings) const Properties& props, AudioSettings& audioSettings)
@ -84,8 +79,6 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
myCart(std::move(cart)), myCart(std::move(cart)),
myDisplayFormat(""), // Unknown TV format @ start myDisplayFormat(""), // Unknown TV format @ start
myCurrentFormat(0), // Unknown format @ start, myCurrentFormat(0), // Unknown format @ start,
myAutodetectedYstart(0),
myYStartAutodetected(false),
myFormatAutodetected(false), myFormatAutodetected(false),
myUserPaletteDefined(false), myUserPaletteDefined(false),
myConsoleTiming(ConsoleTiming::ntsc), myConsoleTiming(ConsoleTiming::ntsc),
@ -151,10 +144,6 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
} }
} }
if (atoi(myProperties.get(PropType::Display_VCenter).c_str()) == 0) {
autodetectYStart();
}
myConsoleInfo.DisplayFormat = myDisplayFormat + autodetected; myConsoleInfo.DisplayFormat = myDisplayFormat + autodetected;
// Set up the correct properties used when toggling format // Set up the correct properties used when toggling format
@ -265,49 +254,6 @@ void Console::redetectFrameLayout()
save(s); save(s);
autodetectFrameLayout(false); autodetectFrameLayout(false);
if (myYStartAutodetected) autodetectYStart();
load(s);
initializeAudio();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::autodetectYStart(bool reset)
{
// 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);
if (reset) {
mySystem->reset(true);
myRiot->update();
}
for (int i = 0; i < 80; i++) myTIA->update();
myTIA->setFrameManager(myFrameManager.get());
myAutodetectedYstart = ystartDetector.detectedYStart() - YSTART_EXTRA;
// Don't forget to reset the SC progress bars again
myOSystem.settings().setValue("fastscbios", fastscbios);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::redetectYStart()
{
Serializer s;
myOSystem.sound().close();
save(s);
autodetectYStart(false);
load(s); load(s);
initializeAudio(); initializeAudio();
@ -696,75 +642,57 @@ void Console::fry() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::changeVerticalCenter(int direction) void Console::changeVerticalCenter(int direction)
{ {
uInt32 ystart = myTIA->ystart(); Int32 vcenter = myTIA->vcenter();
if(direction == +1) // increase YStart if(direction == +1) // increase vcenter
{ {
if(ystart >= TIAConstants::maxYStart) if(vcenter >= TIAConstants::maxVcenter)
{ {
myOSystem.frameBuffer().showMessage("V-Center at minimum"); myOSystem.frameBuffer().showMessage("V-Center at minimum");
return; return;
} }
++ystart; ++vcenter;
myYStartAutodetected = false;
} }
else if(direction == -1) // decrease YStart else if(direction == -1) // decrease vcenter
{ {
if (ystart <= TIAConstants::minYStart) if (vcenter <= TIAConstants::minVcenter)
{ {
myOSystem.frameBuffer().showMessage("V-Center at maximum"); myOSystem.frameBuffer().showMessage("V-Center at maximum");
return; return;
} }
--ystart; --vcenter;
myYStartAutodetected = false;
} }
else else
return; return;
ostringstream ss; ostringstream ss;
ss << ystart; ss << vcenter;
myProperties.set(PropType::Display_VCenter, ss.str()); myProperties.set(PropType::Display_VCenter, ss.str());
if (ystart != myTIA->ystart()) myTIA->setYStart(ystart); if (vcenter != myTIA->vcenter()) myTIA->setVcenter(vcenter);
// use vertical center instead of y-start for display
int vCenter = TIAConstants::defaultYStart - ystart;
ss.str(""); ss.str("");
ss << "V-Center " << vCenter; ss << "V-Center " << vcenter;
myOSystem.frameBuffer().showMessage(ss.str()); myOSystem.frameBuffer().showMessage(ss.str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::updateYStart(uInt32 ystart) void Console::updateVcenter(Int32 vcenter)
{ {
if (ystart > TIAConstants::maxYStart) return; if (vcenter > TIAConstants::maxVcenter | vcenter < TIAConstants::minVcenter) return;
if (ystart == 0) { if (vcenter != myTIA->vcenter()) myTIA->setVcenter(vcenter);
if (myYStartAutodetected) return;
redetectYStart();
myYStartAutodetected = true;
ystart = myAutodetectedYstart;
} else
myYStartAutodetected = false;
if (ystart != myTIA->ystart()) myTIA->setYStart(ystart);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::setTIAProperties() void Console::setTIAProperties()
{ {
// FIXME - ystart is probably disappearing soon, or at least autodetection is Int32 vcenter = BSPF::clamp(
uInt32 ystart = atoi(myProperties.get(PropType::Display_VCenter).c_str()); static_cast<Int32>(atoi(myProperties.get(PropType::Display_VCenter).c_str())), TIAConstants::minVcenter, TIAConstants::maxVcenter
if(ystart != 0) );
ystart = BSPF::clamp(ystart, 0u, TIAConstants::maxYStart);
else {
ystart = myAutodetectedYstart;
myYStartAutodetected = true;
}
if(myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" || if(myDisplayFormat == "NTSC" || myDisplayFormat == "PAL60" ||
myDisplayFormat == "SECAM60") myDisplayFormat == "SECAM60")
@ -778,7 +706,7 @@ void Console::setTIAProperties()
myTIA->setLayout(FrameLayout::pal); myTIA->setLayout(FrameLayout::pal);
} }
myTIA->setYStart(ystart); myTIA->setVcenter(vcenter);
myEmulationTiming.updateFrameLayout(myTIA->frameLayout()); myEmulationTiming.updateFrameLayout(myTIA->frameLayout());
myEmulationTiming.updateConsoleTiming(myConsoleTiming); myEmulationTiming.updateConsoleTiming(myConsoleTiming);

View File

@ -306,9 +306,9 @@ class Console : public Serializable, public ConsoleIO
void toggleJitter() const; void toggleJitter() const;
/** /**
* Update yatart and run autodetection if necessary. * Update vcenter
*/ */
void updateYStart(uInt32 ystart); void updateVcenter(Int32 vcenter);
private: private:
/** /**
@ -316,23 +316,13 @@ class Console : public Serializable, public ConsoleIO
*/ */
void autodetectFrameLayout(bool reset = true); void autodetectFrameLayout(bool reset = true);
/**
* Dryrun the emulation and detect ystart (the first visible scanline).
*/
void autodetectYStart(bool reset = true);
/** /**
* Rerun frame layout autodetection * Rerun frame layout autodetection
*/ */
void redetectFrameLayout(); void redetectFrameLayout();
/** /**
* Rerun ystart autodetection. Sets various properties of the TIA (vcenter, Height, etc) based on
*/
void redetectYStart();
/**
Sets various properties of the TIA (YStart, Height, etc) based on
the current display format. the current display format.
*/ */
void setTIAProperties(); void setTIAProperties();
@ -411,12 +401,6 @@ class Console : public Serializable, public ConsoleIO
// Display format currently in use // Display format currently in use
uInt32 myCurrentFormat; uInt32 myCurrentFormat;
// Autodetected ystart.
uInt32 myAutodetectedYstart;
// Is ystart currently autodetected?
bool myYStartAutodetected;
// Is the TV format autodetected? // Is the TV format autodetected?
bool myFormatAutodetected; bool myFormatAutodetected;

View File

@ -587,7 +587,7 @@ unique_ptr<Console> OSystem::openConsole(const FilesystemNode& romfile, string&
CMDLINE_PROPS_UPDATE("rd", PropType::Console_RightDiff); CMDLINE_PROPS_UPDATE("rd", PropType::Console_RightDiff);
CMDLINE_PROPS_UPDATE("tv", PropType::Console_TVType); CMDLINE_PROPS_UPDATE("tv", PropType::Console_TVType);
CMDLINE_PROPS_UPDATE("format", PropType::Display_Format); CMDLINE_PROPS_UPDATE("format", PropType::Display_Format);
CMDLINE_PROPS_UPDATE("ystart", PropType::Display_VCenter); // TODO (SA): change option CMDLINE_PROPS_UPDATE("vcenter", PropType::Display_VCenter); // TODO (SA): change option
CMDLINE_PROPS_UPDATE("pp", PropType::Display_Phosphor); CMDLINE_PROPS_UPDATE("pp", PropType::Display_Phosphor);
CMDLINE_PROPS_UPDATE("ppblend", PropType::Display_PPBlend); CMDLINE_PROPS_UPDATE("ppblend", PropType::Display_PPBlend);

View File

@ -29,7 +29,6 @@
#include "TIA.hxx" #include "TIA.hxx"
#include "ConsoleTiming.hxx" #include "ConsoleTiming.hxx"
#include "FrameManager.hxx" #include "FrameManager.hxx"
#include "YStartDetector.hxx"
#include "FrameLayoutDetector.hxx" #include "FrameLayoutDetector.hxx"
#include "EmulationTiming.hxx" #include "EmulationTiming.hxx"
#include "ConsoleTiming.hxx" #include "ConsoleTiming.hxx"
@ -159,20 +158,9 @@ bool ProfilingRunner::runOne(const ProfilingRun run)
(cout << endl).flush(); (cout << endl).flush();
YStartDetector ystartDetector;
tia.setFrameManager(&ystartDetector);
system.reset();
(cout << "detecting ystart... ").flush();
for (int i = 0; i < 80; i++) tia.update();
uInt32 yStart = ystartDetector.detectedYStart();
(cout << yStart << endl).flush();
FrameManager frameManager; FrameManager frameManager;
tia.setFrameManager(&frameManager); tia.setFrameManager(&frameManager);
tia.setLayout(frameLayout); tia.setLayout(frameLayout);
tia.setYStart(yStart);
system.reset(); system.reset();

View File

@ -553,7 +553,7 @@ void Settings::usage() const
<< " -bc <arg> Same as using both -lc and -rc\n" << " -bc <arg> Same as using both -lc and -rc\n"
<< " -cp <arg> Sets the 'Controller.SwapPaddles' property\n" << " -cp <arg> Sets the 'Controller.SwapPaddles' property\n"
<< " -format <arg> Sets the 'Display.Format' property\n" << " -format <arg> Sets the 'Display.Format' property\n"
<< " -ystart <arg> Sets the 'Display.YStart' property\n" << " -vcenter <arg> Sets the 'Display.vcenter' property\n"
<< " -pp <arg> Sets the 'Display.Phosphor' property\n" << " -pp <arg> Sets the 'Display.Phosphor' property\n"
<< " -ppblend <arg> Sets the 'Display.PPBlend' property\n" << " -ppblend <arg> Sets the 'Display.PPBlend' property\n"
<< endl << endl

View File

@ -249,12 +249,13 @@ class TIA : public Device
*/ */
uInt32 width() const { return TIAConstants::H_PIXEL; } uInt32 width() const { return TIAConstants::H_PIXEL; }
uInt32 height() const { return myFrameManager->height(); } uInt32 height() const { return myFrameManager->height(); }
uInt32 ystart() const { return myFrameManager->ystart(); } Int32 vcenter() const { return myFrameManager->vcenter(); }
uInt32 startLine() const { return myFrameManager->startLine(); }
/** /**
Changes the current YStart property. Changes the current vcenter property.
*/ */
void setYStart(uInt32 ystart) { myFrameManager->setYstart(ystart); } void setVcenter(Int32 vcenter) { myFrameManager->setVcenter(vcenter); }
void setLayout(FrameLayout layout) { myFrameManager->setLayout(layout); } void setLayout(FrameLayout layout) { myFrameManager->setLayout(layout); }
FrameLayout frameLayout() const { return myFrameManager->layout(); } FrameLayout frameLayout() const { return myFrameManager->layout(); }

View File

@ -24,9 +24,8 @@ namespace TIAConstants {
static constexpr uInt32 frameBufferWidth = 160; static constexpr uInt32 frameBufferWidth = 160;
static constexpr uInt32 frameBufferHeight = 320; static constexpr uInt32 frameBufferHeight = 320;
static constexpr uInt32 defaultYStart = 34; // TODO: PAL-50 might need a different value here static constexpr Int32 maxVcenter = 20; // limit to reasonable values
static constexpr uInt32 minYStart = defaultYStart - 15; // limit to reasonable values static constexpr Int32 minVcenter = -20; // limit to reasonable values
static constexpr uInt32 maxYStart = defaultYStart + 15; // limit to reasonable values
static constexpr uInt32 viewableWidth = 320; static constexpr uInt32 viewableWidth = 320;
static constexpr uInt32 viewableHeight = 240; static constexpr uInt32 viewableHeight = 240;
static constexpr uInt32 initialGarbageFrames = 10; static constexpr uInt32 initialGarbageFrames = 10;

View File

@ -22,6 +22,7 @@
#include "Serializable.hxx" #include "Serializable.hxx"
#include "FrameLayout.hxx" #include "FrameLayout.hxx"
#include "bspf.hxx"
class AbstractFrameManager : public Serializable class AbstractFrameManager : public Serializable
{ {
@ -160,14 +161,19 @@ class AbstractFrameManager : public Serializable
virtual uInt32 scanlines() const { return 0; } virtual uInt32 scanlines() const { return 0; }
/** /**
* Configure the ystart value. * Configure the vcenter value.
*/ */
virtual void setYstart(uInt32 ystart) {} virtual void setVcenter(Int32 vcenter) {}
/** /**
* The configured ystart value. * The configured vcenter value.
*/ */
virtual uInt32 ystart() const { return 0; } virtual Int32 vcenter() const { return 0; }
/**
* The corresponding start line.
*/
virtual uInt32 startLine() const { return 0; }
/** /**
* Set the frame layout. This may be a noop (on the autodetection manager). * Set the frame layout. This may be a noop (on the autodetection manager).

View File

@ -31,7 +31,9 @@ enum Metrics: uInt32 {
vsync = 3, vsync = 3,
maxLinesVsync = 50, maxLinesVsync = 50,
visibleOverscan = 20, visibleOverscan = 20,
initialGarbageFrames = TIAConstants::initialGarbageFrames initialGarbageFrames = TIAConstants::initialGarbageFrames,
ystartNTSC = 34,
ystartPAL = 39
}; };
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -46,9 +48,11 @@ FrameManager::FrameManager()
myFrameLines(0), myFrameLines(0),
myHeight(0), myHeight(0),
myYStart(0), myYStart(0),
myVcenter(0),
myJitterEnabled(false) myJitterEnabled(false)
{ {
reset(); reset();
updateYStart();
onLayoutChange(); onLayoutChange();
} }
@ -98,7 +102,7 @@ void FrameManager::onNextLine()
case State::frame: case State::frame:
if (myLineInState >= myHeight) if (myLineInState >= myHeight)
{ {
myLastY = ystart() + myY; // Last line drawn in this frame myLastY = myYStart + myY; // Last line drawn in this frame
setState(State::waitForVsyncStart); setState(State::waitForVsyncStart);
} }
break; break;
@ -121,10 +125,10 @@ Int32 FrameManager::missingScanlines() const
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::setYstart(uInt32 ystart) void FrameManager::setVcenter(Int32 vcenter)
{ {
myYStart = ystart; myVcenter = vcenter;
myJitterEmulation.setYStart(ystart); updateYStart();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -241,3 +245,9 @@ bool FrameManager::onLoad(Serializer& in)
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameManager::updateYStart() {
myYStart = (layout() == FrameLayout::ntsc ? Metrics::ystartNTSC : Metrics::ystartPAL) - myVcenter;
myJitterEmulation.setYStart(myYStart);
}

View File

@ -44,9 +44,11 @@ class FrameManager: public AbstractFrameManager {
Int32 missingScanlines() const override; Int32 missingScanlines() const override;
void setYstart(uInt32 ystart) override; void setVcenter(Int32 vcenter) override;
uInt32 ystart() const override { return myYStart; } Int32 vcenter() const override { return myVcenter; }
uInt32 startLine() const override { return myYStart; }
void setLayout(FrameLayout mode) override { layout(mode); } void setLayout(FrameLayout mode) override { layout(mode); }
@ -77,6 +79,8 @@ class FrameManager: public AbstractFrameManager {
void updateIsRendering(); void updateIsRendering();
void updateYStart();
private: private:
State myState; State myState;
@ -90,6 +94,7 @@ class FrameManager: public AbstractFrameManager {
uInt32 myFrameLines; uInt32 myFrameLines;
uInt32 myHeight; uInt32 myHeight;
uInt32 myYStart; uInt32 myYStart;
Int32 myVcenter;
bool myJitterEnabled; bool myJitterEnabled;

View File

@ -1,230 +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-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "YStartDetector.hxx"
#include "TIAConstants.hxx"
/**
* Misc. numeric constants used in the algorithm.
*/
enum Metrics: uInt32 {
// ideal world frame sizes
frameLinesNTSC = 262,
frameLinesPAL = 312,
// the ideal vblank zone
vblankNTSC = 37,
vblankPAL = 45,
// number of scanlines to wait for vsync to start (exceeding after the ideal frame size) and stop
waitForVsync = 50,
// max lines underscan
maxUnderscan = 10,
// max lines deviations from detected ystart before we switch back to floating
maxVblankViolations = 2,
// switch to fixed mode after this number of stable frames (+1)
minStableVblankFrames = 1,
// no transitions to fixed mode will happend during those
initialGarbageFrames = TIAConstants::initialGarbageFrames
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
YStartDetector::YStartDetector()
: myState(State::waitForVsyncStart),
myVblankMode(VblankMode::floating),
myLinesWaitingForVsyncToStart(0),
myCurrentVblankLines(0),
myLastVblankLines(0),
myVblankViolations(0),
myStableVblankFrames(0),
myVblankViolated(false)
{
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 YStartDetector::detectedYStart() const
{
return myLastVblankLines;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void YStartDetector::onReset()
{
myState = State::waitForVsyncStart;
myVblankMode = VblankMode::floating;
myLinesWaitingForVsyncToStart = 0;
myCurrentVblankLines = 0;
myLastVblankLines = 0;
myVblankViolations = 0;
myStableVblankFrames = 0;
myVblankViolated = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void YStartDetector::onSetVsync()
{
if (myVsync)
setState(State::waitForVsyncEnd);
else
setState(State::waitForFrameStart);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void YStartDetector::onNextLine()
{
const uInt32 frameLines = layout() == FrameLayout::ntsc ? Metrics::frameLinesNTSC : Metrics::frameLinesPAL;
switch (myState) {
case State::waitForVsyncStart:
// We start counting the number of "lines spent while waiting for vsync start" from
// the "ideal" frame size (corrected by the three scanlines spent in vsync).
if (myCurrentFrameTotalLines > frameLines - 3 || myTotalFrames == 0)
++myLinesWaitingForVsyncToStart;
if (myLinesWaitingForVsyncToStart > Metrics::waitForVsync) setState(State::waitForVsyncEnd);
break;
case State::waitForVsyncEnd:
if (++myLinesWaitingForVsyncToStart > Metrics::waitForVsync) setState(State::waitForFrameStart);
break;
case State::waitForFrameStart:
if (shouldTransitionToFrame()) setState(State::waitForVsyncStart);
else ++myCurrentVblankLines;
break;
default:
throw runtime_error("cannot happen");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool YStartDetector::shouldTransitionToFrame()
{
uInt32 vblankLines = layout() == FrameLayout::pal ? Metrics::vblankPAL : Metrics::vblankNTSC;
// Are we free to transition as per vblank cycle?
bool shouldTransition = myCurrentVblankLines + 1 >= (myVblank ? vblankLines : vblankLines - Metrics::maxUnderscan);
// Do we **actually** transition? This depends on what mode we are in.
bool transition = false;
switch (myVblankMode) {
// Floating mode: we still are looking for a stable frame start
case VblankMode::floating:
// Are we free to transition?
if (shouldTransition) {
// Is this same scanline in which the transition ocurred last frame?
if (myTotalFrames > Metrics::initialGarbageFrames && myCurrentVblankLines == myLastVblankLines)
// Yes? -> Increase the number of stable frames
++myStableVblankFrames;
else
// No? -> Frame start shifted again, set the number of consecutive stable frames to zero
myStableVblankFrames = 0;
// Save the transition point for checking on it next frame
myLastVblankLines = myCurrentVblankLines;
// In floating mode, we transition whenever we can.
transition = true;
}
// Transition to locked mode if we saw enough stable frames in a row.
if (myStableVblankFrames >= Metrics::minStableVblankFrames) {
myVblankMode = VblankMode::locked;
myVblankViolations = 0;
}
break;
// Locked mode: always transition at the same point, but check whether this is actually the
// detected transition point and revert state if applicable
case VblankMode::locked:
// Have we reached the transition point?
if (myCurrentVblankLines == myLastVblankLines) {
// Are we free to transition per the algorithm and didn't observe an violation before?
// (aka did the algorithm tell us to transition before reaching the actual line)
if (shouldTransition && !myVblankViolated)
// Reset the number of irregular frames (if any)
myVblankViolations = 0;
else {
// Record a violation if it wasn't recorded before
if (!myVblankViolated) ++myVblankViolations;
myVblankViolated = true;
}
// transition
transition = true;
// The algorithm tells us to transition although we haven't reached the trip line before
} else if (shouldTransition) {
// Record a violation if it wasn't recorded before
if (!myVblankViolated) ++myVblankViolations;
myVblankViolated = true;
}
// Revert to floating mode if there were too many irregular frames in a row
if (myVblankViolations > Metrics::maxVblankViolations) {
myVblankMode = VblankMode::floating;
myStableVblankFrames = 0;
}
break;
default:
throw runtime_error("cannot happen");
}
return transition;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void YStartDetector::setState(State state)
{
if (state == myState) return;
myState = state;
myLinesWaitingForVsyncToStart = 0;
switch (state) {
case State::waitForVsyncEnd:
break;
case State::waitForVsyncStart:
notifyFrameComplete();
notifyFrameStart();
break;
case State::waitForFrameStart:
myVblankViolated = false;
myCurrentVblankLines = 0;
break;
default:
throw new runtime_error("cannot happen");
}
}

View File

@ -1,155 +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-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef TIA_YSTART_DETECTOR
#define TIA_YSTART_DETECTOR
#include "AbstractFrameManager.hxx"
/**
* This frame manager detects ystart from the first line with vblank = off.
*/
class YStartDetector: public AbstractFrameManager {
public:
YStartDetector();
public:
/**
* Getter for the detected ystart value
*/
uInt32 detectedYStart() const;
/**
* We require frame layout to be set from outside.
*/
void setLayout(FrameLayout layout) override { this->layout(layout); }
protected:
/**
* We need to track vsync changes.
*/
void onSetVsync() override;
/**
* Reset hook.
*/
void onReset() override;
/**
* The workhorse.
*/
void onNextLine() override;
private:
/**
* Our various states.
*/
enum class State {
// Wait for vsync on
waitForVsyncStart,
// Wait for vsync off
waitForVsyncEnd,
// Wait for the visible frame to start
waitForFrameStart
};
/**
* Have we settled on a frame start?
*/
enum class VblankMode {
// We have settled on a frame start and have some hysteresis before we return to floating
locked,
// We are actively looking for the frame to start
floating
};
private:
/**
* Side effects for state transitions.
*/
void setState(State state);
/**
* Perform detection and decide whether the frame starts now.
*/
bool shouldTransitionToFrame();
private:
/**
* State.
*/
State myState;
/**
* locked / floating
*/
VblankMode myVblankMode;
/**
* Counts the scanlines that we wait for vsync to start.
*/
uInt32 myLinesWaitingForVsyncToStart;
/**
* The number of lines we are currently waiting for the frame to start (and vblank to end).
*/
uInt32 myCurrentVblankLines;
/**
* The number of vblank lines on the last frame.
*/
uInt32 myLastVblankLines;
/**
* Count "vblank violations" in fixed mode (the number of consecutive frames where ystart
* differs from the previously detected value). Once a trip point is reached, we transition
* back to floating mode.
*/
uInt32 myVblankViolations;
/**
* The number of frames in floating mode with stable ystart. Once a trip point is reacted,
* we transition to fixed mode
*/
uInt32 myStableVblankFrames;
/**
* Tracks deviations from the determined ystart value during a fixed mode frame in order to
* avoid double counting.
*/
bool myVblankViolated;
private:
YStartDetector(const YStartDetector&) = delete;
YStartDetector(YStartDetector&&) = delete;
YStartDetector& operator=(const YStartDetector&) = delete;
YStartDetector& operator=(YStartDetector&&) = delete;
};
#endif // TIA_YSTART_DETECTOR

View File

@ -4,7 +4,6 @@ MODULE_OBJS := \
src/emucore/tia/frame-manager/FrameManager.o \ src/emucore/tia/frame-manager/FrameManager.o \
src/emucore/tia/frame-manager/AbstractFrameManager.o \ src/emucore/tia/frame-manager/AbstractFrameManager.o \
src/emucore/tia/frame-manager/FrameLayoutDetector.o \ src/emucore/tia/frame-manager/FrameLayoutDetector.o \
src/emucore/tia/frame-manager/YStartDetector.o \
src/emucore/tia/frame-manager/JitterEmulation.o src/emucore/tia/frame-manager/JitterEmulation.o
MODULE_DIRS += \ MODULE_DIRS += \

View File

@ -138,9 +138,9 @@ GameInfoDialog::GameInfoDialog(
t = new StaticTextWidget(myTab, font, HBORDER, ypos + 1, "V-Center "); t = new StaticTextWidget(myTab, font, HBORDER, ypos + 1, "V-Center ");
myVCenter = new SliderWidget(myTab, font, t->getRight() + 2, ypos, myVCenter = new SliderWidget(myTab, font, t->getRight() + 2, ypos,
"", 0, kVCenterChanged, 7 * fontWidth, "px"); "", 0, kVCenterChanged, 7 * fontWidth, "px");
// TODO (SA):check relationship of ystart and vcenter
myVCenter->setMinValue(TIAConstants::minYStart - TIAConstants::defaultYStart); myVCenter->setMinValue(TIAConstants::minVcenter);
myVCenter->setMaxValue(TIAConstants::maxYStart - TIAConstants::defaultYStart); myVCenter->setMaxValue(TIAConstants::maxVcenter);
myVCenter->setTickmarkIntervals(4); myVCenter->setTickmarkIntervals(4);
wid.push_back(myVCenter); wid.push_back(myVCenter);
@ -458,20 +458,10 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props)
myPPBlend->setValue(atoi(blend.c_str())); myPPBlend->setValue(atoi(blend.c_str()));
// set vertical center // set vertical center
int vCenter = atoi(props.get(PropType::Display_VCenter).c_str()); Int32 vcenter = atoi(props.get(PropType::Display_VCenter).c_str());
if (vCenter) myVCenter->setValueLabel(vcenter);
{ myVCenter->setValue(vcenter);
// convert y-start into v-center myVCenter->setValueUnit(vcenter ? "px" : "");
// TODO (SA): fix this
vCenter = TIAConstants::defaultYStart - vCenter;
myVCenter->setValueLabel(vCenter);
}
else
{
myVCenter->setValueLabel("default");
}
myVCenter->setValue(vCenter);
myVCenter->setValueUnit(vCenter ? "px" : "");
mySound->setState(props.get(PropType::Cart_Sound) == "STEREO"); mySound->setState(props.get(PropType::Cart_Sound) == "STEREO");
// if stereo is always enabled, disable game specific stereo setting // if stereo is always enabled, disable game specific stereo setting
@ -548,11 +538,9 @@ void GameInfoDialog::saveConfig()
myGameProperties.set(PropType::Display_Phosphor, myPhosphor->getState() ? "YES" : "NO"); myGameProperties.set(PropType::Display_Phosphor, myPhosphor->getState() ? "YES" : "NO");
myGameProperties.set(PropType::Display_PPBlend, myPPBlend->getValueLabel() == "Off" ? "0" : myGameProperties.set(PropType::Display_PPBlend, myPPBlend->getValueLabel() == "Off" ? "0" :
myPPBlend->getValueLabel()); myPPBlend->getValueLabel());
int vCenter = myVCenter->getValue(); Int32 vcenter = myVCenter->getValue();
if (vCenter)
// convert v-center into y-start TODO (SA): fix this myGameProperties.set(PropType::Display_VCenter, std::to_string(vcenter));
vCenter = TIAConstants::defaultYStart - vCenter;
myGameProperties.set(PropType::Display_VCenter, std::to_string(vCenter));
myGameProperties.set(PropType::Cart_Sound, mySound->getState() ? "STEREO" : "MONO"); myGameProperties.set(PropType::Cart_Sound, mySound->getState() ? "STEREO" : "MONO");
// Console properties // Console properties
@ -595,7 +583,7 @@ void GameInfoDialog::saveConfig()
// update 'Emulation' tab settings immediately // update 'Emulation' tab settings immediately
instance().console().setFormat(myFormat->getSelected()); instance().console().setFormat(myFormat->getSelected());
instance().frameBuffer().tiaSurface().enablePhosphor(myPhosphor->getState(), myPPBlend->getValue()); instance().frameBuffer().tiaSurface().enablePhosphor(myPhosphor->getState(), myPPBlend->getValue());
instance().console().updateYStart(vCenter); instance().console().updateVcenter(vcenter);
instance().console().initializeAudio(); instance().console().initializeAudio();
// update 'Console' tab settings immediately // update 'Console' tab settings immediately