sa2: Add support for --headless and --fixed-speed.

Headless will skip the video refresh (and the vsync).
Fixed-Speed will avoid adaptive speed.

Used together, the emulator will go at maximum speed skipping video refresh (useful for profiling).
This commit is contained in:
Andrea Odetti 2020-11-21 15:07:31 +00:00
parent 7067ad150e
commit a121981e5a
7 changed files with 50 additions and 28 deletions

View file

@ -39,7 +39,8 @@ bool getEmulatorOptions(int argc, const char * argv [], const std::string & vers
po::options_description emulatorDesc("Emulator");
emulatorDesc.add_options()
("log", "Log to AppleWin.log")
("headless", "Headless: disable video")
("headless", "Headless: disable video (freewheel)")
("fixed-speed", "Fixed (non-adaptive) speed")
("ntsc,nt", "NTSC: execute NTSC code")
("benchmark,b", "Benchmark emulator")
("no-squaring", "Gamepad range is (already) a square");
@ -89,6 +90,7 @@ bool getEmulatorOptions(int argc, const char * argv [], const std::string & vers
options.log = vm.count("log") > 0;
options.ntsc = vm.count("ntsc") > 0;
options.squaring = vm.count("no-squaring") == 0;
options.fixedSpeed = vm.count("fixed-speed") > 0;
return true;
}

View file

@ -29,6 +29,7 @@ struct EmulatorOptions
bool multiThreaded = false;
bool looseMutex = false; // whether SDL_UpdateTexture is mutex protected (from CPU)
int timerInterval = 16; // only when multithreaded
bool fixedSpeed = false; // default adaptive
int sdlDriver = -1; // default = -1 to let SDL choose
};

View file

@ -4,32 +4,41 @@
#include "CPU.h"
#include "AppleWin.h"
Speed::Speed()
: myStartTime(std::chrono::steady_clock::now())
Speed::Speed(const bool fixedSpeed)
: myFixedSpeed(fixedSpeed)
, myStartTime(std::chrono::steady_clock::now())
, myStartCycles(g_nCumulativeCycles)
{
}
size_t Speed::getCyclesTillNext(const size_t milliseconds) const
{
const uint64_t currentCycles = g_nCumulativeCycles;
const auto currentTime = std::chrono::steady_clock::now();
const auto currentDelta = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - myStartTime).count();
// target the next time we will be called
const auto targetDeltaInMillis = currentDelta + milliseconds;
const uint64_t targetCycles = static_cast<uint64_t>(targetDeltaInMillis * g_fCurrentCLK6502 * 1.0e-3) + myStartCycles;
if (targetCycles > currentCycles)
if (myFixedSpeed)
{
// number of cycles to fill this period
const uint64_t cyclesToExecute = targetCycles - currentCycles;
return cyclesToExecute;
const size_t cycles = static_cast<uint64_t>(milliseconds * g_fCurrentCLK6502 * 1.0e-3);
return cycles;
}
else
{
// we got ahead, nothing to do this time
// CpuExecute will still execute 1 instruction, which does not cause any issues
return 0;
const uint64_t currentCycles = g_nCumulativeCycles;
const auto currentTime = std::chrono::steady_clock::now();
const auto currentDelta = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - myStartTime).count();
// target the next time we will be called
const auto targetDeltaInMillis = currentDelta + milliseconds;
const uint64_t targetCycles = static_cast<uint64_t>(targetDeltaInMillis * g_fCurrentCLK6502 * 1.0e-3) + myStartCycles;
if (targetCycles > currentCycles)
{
// number of cycles to fill this period
const uint64_t cyclesToExecute = targetCycles - currentCycles;
return cyclesToExecute;
}
else
{
// we got ahead, nothing to do this time
// CpuExecute will still execute 1 instruction, which does not cause any issues
return 0;
}
}
}

View file

@ -5,7 +5,7 @@
class Speed
{
public:
Speed();
Speed(const bool fixedSpeed);
// calculate the number of cycles to execute in the current period
// assuming the next call will happen in x milliseconds
@ -13,6 +13,7 @@ public:
private:
const bool myFixedSpeed;
std::chrono::time_point<std::chrono::steady_clock> myStartTime;
uint64_t myStartCycles;
};

View file

@ -135,13 +135,15 @@ namespace
Emulator::Emulator(
const std::shared_ptr<SDL_Window> & window,
const std::shared_ptr<SDL_Renderer> & renderer,
const std::shared_ptr<SDL_Texture> & texture
const std::shared_ptr<SDL_Texture> & texture,
const bool fixedSpeed
)
: myWindow(window)
, myRenderer(renderer)
, myTexture(texture)
, myMultiplier(1)
, myFullscreen(false)
, mySpeed(fixedSpeed)
{
}

View file

@ -11,7 +11,8 @@ public:
Emulator(
const std::shared_ptr<SDL_Window> & window,
const std::shared_ptr<SDL_Renderer> & renderer,
const std::shared_ptr<SDL_Texture> & texture
const std::shared_ptr<SDL_Texture> & texture,
const bool fixedSpeed
);
void execute(const size_t milliseconds);

View file

@ -253,7 +253,7 @@ void run_sdl(int argc, const char * argv [])
const int fps = getRefreshRate();
std::cerr << "Video refresh rate: " << fps << " Hz, " << 1000.0 / fps << " ms" << std::endl;
Emulator emulator(win, ren, tex);
Emulator emulator(win, ren, tex, options.fixedSpeed);
Timer global;
Timer updateTextureTimer;
@ -318,9 +318,12 @@ void run_sdl(int argc, const char * argv [])
SDL_UnlockMutex(data.mutex);
}
refreshScreenTimer.tic();
emulator.refreshVideo(rect);
refreshScreenTimer.toc();
if (!options.headless)
{
refreshScreenTimer.tic();
emulator.refreshVideo(rect);
refreshScreenTimer.toc();
}
} while (!quit);
@ -357,9 +360,12 @@ void run_sdl(int argc, const char * argv [])
const SDL_Rect rect = emulator.updateTexture();
updateTextureTimer.toc();
refreshScreenTimer.tic();
emulator.refreshVideo(rect);
refreshScreenTimer.toc();
if (!options.headless)
{
refreshScreenTimer.tic();
emulator.refreshVideo(rect);
refreshScreenTimer.toc();
}
} while (!quit);
}