Keep track of actual speed to ensure emulated CPU goes at the right speed (and keeps audio on track).

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
Andrea Odetti 2020-11-20 15:32:35 +00:00
parent ad335e2a5c
commit 48146f4c34
6 changed files with 68 additions and 11 deletions

View file

@ -6,6 +6,7 @@ add_library(common2 STATIC
programoptions.cpp
utils.cpp
timer.cpp
speed.cpp
)
find_package(Boost REQUIRED

View file

@ -0,0 +1,35 @@
#include "frontends/common2/speed.h"
#include "StdAfx.h"
#include "CPU.h"
#include "AppleWin.h"
Speed::Speed()
: 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)
{
// 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

@ -0,0 +1,18 @@
#pragma once
#include <chrono>
class Speed
{
public:
Speed();
// calculate the number of cycles to execute in the current period
// assuming the next call will happen in x milliseconds
size_t getCyclesTillNext(const size_t milliseconds) const;
private:
std::chrono::time_point<std::chrono::steady_clock> myStartTime;
uint64_t myStartCycles;
};

View file

@ -141,23 +141,22 @@ Emulator::Emulator(
, myTexture(texture)
, myMultiplier(1)
, myFullscreen(false)
, myExtraCycles(0)
{
}
void Emulator::executeCycles(const int targetCycles)
void Emulator::execute(const size_t next)
{
const size_t cyclesToExecute = mySpeed.getCyclesTillNext(next);
const bool bVideoUpdate = true;
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
const DWORD executedCycles = CpuExecute(targetCycles + myExtraCycles, bVideoUpdate);
const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate);
g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame;
GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles);
MB_PeriodicUpdate(executedCycles);
SpkrUpdate(executedCycles);
myExtraCycles = targetCycles - executedCycles;
}
SDL_Rect Emulator::updateTexture()

View file

@ -3,6 +3,8 @@
#include <SDL.h>
#include <memory>
#include "frontends/common2/speed.h"
class Emulator
{
public:
@ -12,7 +14,7 @@ public:
const std::shared_ptr<SDL_Texture> & texture
);
void executeCycles(const int cycles);
void execute(const size_t milliseconds);
void refreshVideo();
SDL_Rect updateTexture();
void refreshVideo(const SDL_Rect & rect);
@ -30,5 +32,6 @@ private:
int myMultiplier;
bool myFullscreen;
int myExtraCycles;
Speed mySpeed;
};

View file

@ -152,8 +152,7 @@ namespace
SDL_LockMutex(data->mutex);
data->timer->tic();
const int uCyclesToExecute = int(g_fCurrentCLK6502 * interval * 0.001);
data->emulator->executeCycles(uCyclesToExecute);
data->emulator->execute(interval);
data->timer->toc();
SDL_UnlockMutex(data->mutex);
@ -325,7 +324,9 @@ void run_sdl(int argc, const char * argv [])
updateTextureTimerTag = "0 .";
bool quit = false;
const int uCyclesToExecute = int(g_fCurrentCLK6502 / fps);
// it does not need to be exact
const size_t oneFrame = 1000 / fps;
do
{
@ -335,7 +336,7 @@ void run_sdl(int argc, const char * argv [])
eventTimer.toc();
cpuTimer.tic();
emulator.executeCycles(uCyclesToExecute);
emulator.execute(oneFrame);
cpuTimer.toc();
updateTextureTimer.tic();