mirror of https://github.com/stella-emu/stella.git
Sanitize and match emulation timing
-> no more perceivable audio latency -> fewer underruns
This commit is contained in:
parent
9079d77de0
commit
d2c930886b
|
@ -1,22 +1,13 @@
|
|||
# Debugging
|
||||
|
||||
* Log and check how queue fills
|
||||
|
||||
# Bugs
|
||||
|
||||
* Fix timeslice size
|
||||
|
||||
# Refactoring
|
||||
|
||||
* Move timing related constants and logic to separate class
|
||||
|
||||
# Verify
|
||||
|
||||
* Verify that the base unit for chrono is seconds
|
||||
* Verify that FPS are still measured correctly
|
||||
|
||||
# Missing features
|
||||
|
||||
* Reimplement target FPS mode
|
||||
* Implement Lanzcos resampling
|
||||
* Fixup OpenGL sync
|
||||
* Fixup OpenGL sync, ensure that FB only rerenders after a frame has been generated
|
||||
|
||||
# Cleanup
|
||||
|
||||
* Document EmulationTiming
|
||||
|
|
|
@ -21,7 +21,7 @@ using std::mutex;
|
|||
using std::lock_guard;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
AudioQueue::AudioQueue(uInt32 fragmentSize, uInt8 capacity, bool isStereo, uInt16 sampleRate)
|
||||
AudioQueue::AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo, uInt16 sampleRate)
|
||||
: myFragmentSize(fragmentSize),
|
||||
myIsStereo(isStereo),
|
||||
mySampleRate(sampleRate),
|
||||
|
@ -34,7 +34,7 @@ AudioQueue::AudioQueue(uInt32 fragmentSize, uInt8 capacity, bool isStereo, uInt1
|
|||
|
||||
myFragmentBuffer = new Int16[myFragmentSize * sampleSize * (capacity + 2)];
|
||||
|
||||
for (uInt8 i = 0; i < capacity; i++)
|
||||
for (uInt32 i = 0; i < capacity; i++)
|
||||
myFragmentQueue[i] = myAllFragments[i] = myFragmentBuffer + i * sampleSize * myFragmentSize;
|
||||
|
||||
myAllFragments[capacity] = myFirstFragmentForEnqueue =
|
||||
|
@ -51,13 +51,13 @@ AudioQueue::~AudioQueue()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 AudioQueue::capacity() const
|
||||
uInt32 AudioQueue::capacity() const
|
||||
{
|
||||
return myFragmentQueue.size();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 AudioQueue::size()
|
||||
uInt32 AudioQueue::size()
|
||||
{
|
||||
lock_guard<mutex> guard(myMutex);
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class AudioQueue
|
|||
@param isStereo Whether samples are stereo or mono.
|
||||
@param sampleRate The sample rate. This is not used, but can be queried.
|
||||
*/
|
||||
AudioQueue(uInt32 fragmentSize, uInt8 capacity, bool isStereo, uInt16 sampleRate);
|
||||
AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo, uInt16 sampleRate);
|
||||
|
||||
/**
|
||||
We need a destructor to deallocate the individual fragment buffers.
|
||||
|
@ -55,12 +55,12 @@ class AudioQueue
|
|||
/**
|
||||
Capacity getter.
|
||||
*/
|
||||
uInt8 capacity() const;
|
||||
uInt32 capacity() const;
|
||||
|
||||
/**
|
||||
Size getter.
|
||||
*/
|
||||
uInt8 size();
|
||||
uInt32 size();
|
||||
|
||||
/**
|
||||
Stereo / mono getter.
|
||||
|
@ -122,10 +122,10 @@ class AudioQueue
|
|||
Int16* myFragmentBuffer;
|
||||
|
||||
// The nubmer if queued fragments
|
||||
uInt8 mySize;
|
||||
uInt32 mySize;
|
||||
|
||||
// The next fragment.
|
||||
uInt8 myNextFragment;
|
||||
uInt32 myNextFragment;
|
||||
|
||||
// We need a mutex for thread safety.
|
||||
std::mutex myMutex;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "bspf.hxx"
|
||||
#include "Sound.hxx"
|
||||
#include "OSystem.hxx"
|
||||
#include "AudioQueue.hxx"
|
||||
#include "EmulationTiming.hxx"
|
||||
|
||||
/**
|
||||
This class implements a Null sound object, where-by sound generation
|
||||
|
@ -57,7 +59,7 @@ class SoundNull : public Sound
|
|||
Initializes the sound device. This must be called before any
|
||||
calls are made to derived methods.
|
||||
*/
|
||||
void open() override { }
|
||||
void open(shared_ptr<AudioQueue>, EmulationTiming*) override { }
|
||||
|
||||
/**
|
||||
Should be called to close the sound device. Once called the sound
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "Console.hxx"
|
||||
#include "SoundSDL2.hxx"
|
||||
#include "AudioQueue.hxx"
|
||||
#include "EmulationTiming.hxx"
|
||||
|
||||
namespace {
|
||||
inline Int16 applyVolume(Int16 sample, Int32 volumeFactor)
|
||||
|
@ -116,8 +117,10 @@ void SoundSDL2::setEnabled(bool state)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue)
|
||||
void SoundSDL2::open(shared_ptr<AudioQueue> audioQueue, EmulationTiming* emulationTiming)
|
||||
{
|
||||
this->emulationTiming = emulationTiming;
|
||||
|
||||
myOSystem.logMessage("SoundSDL2::open started ...", 2);
|
||||
mute(true);
|
||||
|
||||
|
@ -241,7 +244,7 @@ uInt32 SoundSDL2::getSampleRate() const
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void SoundSDL2::processFragment(Int16* stream, uInt32 length)
|
||||
{
|
||||
if (myUnderrun && myAudioQueue->size() > 0) {
|
||||
if (myUnderrun && myAudioQueue->size() > emulationTiming->prebufferFragmentCount()) {
|
||||
myUnderrun = false;
|
||||
myCurrentFragment = myAudioQueue->dequeue(myCurrentFragment);
|
||||
myFragmentIndex = 0;
|
||||
|
@ -273,8 +276,10 @@ void SoundSDL2::processFragment(Int16* stream, uInt32 length)
|
|||
Int16* nextFragment = myAudioQueue->dequeue(myCurrentFragment);
|
||||
if (nextFragment)
|
||||
myCurrentFragment = nextFragment;
|
||||
else
|
||||
else {
|
||||
myUnderrun = true;
|
||||
(cout << "audio underrun!\n").flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (isStereo) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
class OSystem;
|
||||
class AudioQueue;
|
||||
class EmulationTiming;
|
||||
|
||||
#include "SDL_lib.hxx"
|
||||
|
||||
|
@ -59,7 +60,7 @@ class SoundSDL2 : public Sound
|
|||
Initializes the sound device. This must be called before any
|
||||
calls are made to derived methods.
|
||||
*/
|
||||
void open(shared_ptr<AudioQueue> audioQueue) override;
|
||||
void open(shared_ptr<AudioQueue> audioQueue, EmulationTiming* emulationTiming) override;
|
||||
|
||||
/**
|
||||
Should be called to close the sound device. Once called the sound
|
||||
|
@ -124,6 +125,8 @@ class SoundSDL2 : public Sound
|
|||
|
||||
shared_ptr<AudioQueue> myAudioQueue;
|
||||
|
||||
EmulationTiming* emulationTiming;
|
||||
|
||||
Int16* myCurrentFragment;
|
||||
uInt32 myTimeIndex;
|
||||
uInt32 myFragmentIndex;
|
||||
|
|
|
@ -513,7 +513,7 @@ void Debugger::nextFrame(int frames)
|
|||
unlockSystem();
|
||||
while(frames)
|
||||
{
|
||||
myOSystem.console().tia().update();
|
||||
myOSystem.console().tia().update(myOSystem.console().emulationTiming().maxCyclesPerTimeslice());
|
||||
--frames;
|
||||
}
|
||||
lockSystem();
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
|
||||
namespace {
|
||||
constexpr uInt8 YSTART_EXTRA = 2;
|
||||
constexpr uInt8 AUDIO_QUEUE_HALF_FRAMES_PER_FRAGMENT = 1;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -358,6 +357,7 @@ void Console::toggleFormat(int direction)
|
|||
setTIAProperties();
|
||||
myTIA->frameReset();
|
||||
initializeVideo(); // takes care of refreshing the screen
|
||||
initializeAudio(); // ensure that audio synthesis is set up to match emulation speed
|
||||
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
|
||||
|
@ -556,7 +556,7 @@ void Console::initializeAudio()
|
|||
createAudioQueue();
|
||||
myTIA->setAudioQueue(myAudioQueue);
|
||||
|
||||
myOSystem.sound().open(myAudioQueue);
|
||||
myOSystem.sound().open(myAudioQueue, &myEmulationTiming);
|
||||
}
|
||||
|
||||
/* Original frying research and code by Fred Quimby.
|
||||
|
@ -699,37 +699,18 @@ void Console::setTIAProperties()
|
|||
|
||||
myTIA->setYStart(ystart != 0 ? ystart : myAutodetectedYstart);
|
||||
myTIA->setHeight(height);
|
||||
|
||||
myEmulationTiming.updateFrameLayout(myTIA->frameLayout());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Console::createAudioQueue()
|
||||
{
|
||||
uInt32 fragmentSize, sampleRate;
|
||||
|
||||
switch (myConsoleTiming) {
|
||||
case ConsoleTiming::ntsc:
|
||||
fragmentSize = 262 * AUDIO_QUEUE_HALF_FRAMES_PER_FRAGMENT;
|
||||
sampleRate = 2 * 262 * 60;
|
||||
break;
|
||||
|
||||
case ConsoleTiming::pal:
|
||||
case ConsoleTiming::secam:
|
||||
fragmentSize = 312 * AUDIO_QUEUE_HALF_FRAMES_PER_FRAGMENT;
|
||||
sampleRate = 2 * 312 * 50;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("invalid console timing");
|
||||
}
|
||||
|
||||
uInt32 queueSize =
|
||||
(2 * myOSystem.sound().getFragmentSize() * sampleRate) / (fragmentSize * myOSystem.sound().getSampleRate());
|
||||
|
||||
myAudioQueue = make_shared<AudioQueue>(
|
||||
fragmentSize,
|
||||
queueSize > 0 ? queueSize : 1,
|
||||
myEmulationTiming.audioFragmentSize(),
|
||||
myEmulationTiming.audioQueueCapacity(myOSystem.sound().getSampleRate(), myOSystem.sound().getFragmentSize()),
|
||||
myProperties.get(Cartridge_Sound) == "STEREO",
|
||||
sampleRate
|
||||
myEmulationTiming.audioSampleRate()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class AudioQueue;
|
|||
#include "Serializable.hxx"
|
||||
#include "EventHandlerConstants.hxx"
|
||||
#include "NTSCFilter.hxx"
|
||||
#include "EmulationTiming.hxx"
|
||||
#include "frame-manager/AbstractFrameManager.hxx"
|
||||
|
||||
/**
|
||||
|
@ -190,6 +191,11 @@ class Console : public Serializable
|
|||
*/
|
||||
void stateChanged(EventHandlerState state);
|
||||
|
||||
/**
|
||||
Retrieve emulation timing provider.
|
||||
*/
|
||||
EmulationTiming& emulationTiming() { return myEmulationTiming; }
|
||||
|
||||
public:
|
||||
/**
|
||||
Toggle between NTSC/PAL/SECAM (and variants) display format.
|
||||
|
@ -416,7 +422,9 @@ class Console : public Serializable
|
|||
// Contains timing information for this console
|
||||
ConsoleTiming myConsoleTiming;
|
||||
|
||||
uInt32 myFramerate;
|
||||
// Emulation timing provider. This ties together the timing of the core emulation loop
|
||||
// and the audio synthesis parameters
|
||||
EmulationTiming myEmulationTiming;
|
||||
|
||||
// Table of RGB values for NTSC, PAL and SECAM
|
||||
static uInt32 ourNTSCPalette[256];
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2018 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 "EmulationTiming.hxx"
|
||||
|
||||
namespace {
|
||||
constexpr uInt32 AUDIO_HALF_FRAMES_PER_FRAGMENT = 1;
|
||||
constexpr uInt32 QUEUE_CAPACITY_SAFETY_FACTOR = 2;
|
||||
constexpr uInt32 PREBUFFER_FRAGMENT_COUNT = 4;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
EmulationTiming::EmulationTiming(FrameLayout frameLayout) : frameLayout(frameLayout) {}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void EmulationTiming::updateFrameLayout(FrameLayout frameLayout) {
|
||||
this->frameLayout = frameLayout;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::maxCyclesPerTimeslice() const {
|
||||
return (3 * cyclesPerFrame()) / 2;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::minCyclesPerTimeslice() const {
|
||||
return cyclesPerFrame() / 2;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::linesPerFrame() const {
|
||||
switch (frameLayout) {
|
||||
case FrameLayout::ntsc:
|
||||
return 262;
|
||||
|
||||
case FrameLayout::pal:
|
||||
return 312;
|
||||
|
||||
default:
|
||||
throw runtime_error("invalid frame layout");
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::cyclesPerFrame() const {
|
||||
return 76 * linesPerFrame();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::framesPerSecond() const {
|
||||
switch (frameLayout) {
|
||||
case FrameLayout::ntsc:
|
||||
return 60;
|
||||
|
||||
case FrameLayout::pal:
|
||||
return 50;
|
||||
|
||||
default:
|
||||
throw runtime_error("invalid frame layout");
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::cyclesPerSecond() const {
|
||||
return cyclesPerFrame() * framesPerSecond();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::audioFragmentSize() const {
|
||||
return AUDIO_HALF_FRAMES_PER_FRAGMENT * linesPerFrame();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::audioSampleRate() const {
|
||||
return 2 * linesPerFrame() * framesPerSecond();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::audioQueueCapacity(uInt32 playbackRate, uInt32 playbackFragmentSize) const {
|
||||
uInt32 capacity = (playbackFragmentSize * audioSampleRate()) / (audioFragmentSize() * playbackRate) + 1;
|
||||
uInt32 minCapacity = (maxCyclesPerTimeslice() * audioSampleRate()) / (audioFragmentSize() * cyclesPerSecond()) + 1;
|
||||
|
||||
return std::max(prebufferFragmentCount() + 1, QUEUE_CAPACITY_SAFETY_FACTOR * std::max(capacity, minCapacity));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 EmulationTiming::prebufferFragmentCount() const {
|
||||
return PREBUFFER_FRAGMENT_COUNT;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2018 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 EMULATION_TIMING_HXX
|
||||
#define EMULATION_TIMING_HXX
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "FrameLayout.hxx"
|
||||
|
||||
class EmulationTiming {
|
||||
public:
|
||||
|
||||
EmulationTiming(FrameLayout frameLayout = FrameLayout::ntsc);
|
||||
|
||||
void updateFrameLayout(FrameLayout frameLayout);
|
||||
|
||||
uInt32 maxCyclesPerTimeslice() const;
|
||||
|
||||
uInt32 minCyclesPerTimeslice() const;
|
||||
|
||||
uInt32 linesPerFrame() const;
|
||||
|
||||
uInt32 cyclesPerFrame() const;
|
||||
|
||||
uInt32 framesPerSecond() const;
|
||||
|
||||
uInt32 cyclesPerSecond() const;
|
||||
|
||||
uInt32 audioFragmentSize() const;
|
||||
|
||||
uInt32 audioSampleRate() const;
|
||||
|
||||
uInt32 audioQueueCapacity(uInt32 playbackRate, uInt32 playbackFragmentSize) const;
|
||||
|
||||
uInt32 prebufferFragmentCount() const;
|
||||
|
||||
private:
|
||||
|
||||
FrameLayout frameLayout;
|
||||
|
||||
};
|
||||
|
||||
#endif // EMULATION_TIMING_HXX
|
|
@ -258,7 +258,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title,
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Int64 FrameBuffer::update()
|
||||
Int64 FrameBuffer::update(uInt32 maxCycles)
|
||||
{
|
||||
// Determine which mode we are in (from the EventHandler)
|
||||
// Take care of S_EMULATE mode here, otherwise let the GUI
|
||||
|
@ -274,7 +274,7 @@ Int64 FrameBuffer::update()
|
|||
// Run the console for one frame
|
||||
// Note that the debugger can cause a breakpoint to occur, which changes
|
||||
// the EventHandler state 'behind our back' - we need to check for that
|
||||
cycles = myOSystem.console().tia().update();
|
||||
cycles = myOSystem.console().tia().update(maxCycles);
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
if(myOSystem.eventHandler().state() != EventHandlerState::EMULATION) break;
|
||||
#endif
|
||||
|
|
|
@ -116,7 +116,7 @@ class FrameBuffer
|
|||
drawing the TIA, any pending menus, etc. Returns the numbers of CPU cycles
|
||||
spent during emulation, or -1 if not applicable.
|
||||
*/
|
||||
Int64 update();
|
||||
Int64 update(uInt32 maxCycles = 50000);
|
||||
|
||||
/**
|
||||
Shows a message onscreen.
|
||||
|
|
|
@ -229,7 +229,7 @@ bool M6502::execute(uInt32 number)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
inline bool M6502::_execute(uInt32 number)
|
||||
inline bool M6502::_execute(uInt32 cycles)
|
||||
{
|
||||
// Clear all of the execution status bits except for the fatal error bit
|
||||
myExecutionStatus &= FatalErrorBit;
|
||||
|
@ -239,10 +239,12 @@ inline bool M6502::_execute(uInt32 number)
|
|||
M6532& riot = mySystem->m6532();
|
||||
#endif
|
||||
|
||||
uInt32 currentCycles = 0;
|
||||
|
||||
// Loop until execution is stopped or a fatal error occurs
|
||||
for(;;)
|
||||
{
|
||||
for(; !myExecutionStatus && (number != 0); --number)
|
||||
for(; !myExecutionStatus && (currentCycles < cycles * SYSTEM_CYCLES_PER_CPU); currentCycles += SYSTEM_CYCLES_PER_CPU)
|
||||
{
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
if(myJustHitReadTrapFlag || myJustHitWriteTrapFlag)
|
||||
|
@ -329,7 +331,7 @@ inline bool M6502::_execute(uInt32 number)
|
|||
}
|
||||
|
||||
// See if we've executed the specified number of instructions
|
||||
if(number == 0)
|
||||
if (currentCycles >= cycles * SYSTEM_CYCLES_PER_CPU)
|
||||
{
|
||||
// Yes, so answer that everything finished fine
|
||||
return true;
|
||||
|
|
|
@ -110,10 +110,12 @@ class M6502 : public Serializable
|
|||
is executed, someone stops execution, or an error occurs. Answers
|
||||
true iff execution stops normally.
|
||||
|
||||
@param number Indicates the number of instructions to execute
|
||||
@param number Indicates the number of cycles to execute. Not that the actual
|
||||
granularity of the CPU is instructions, so this is only accurate up to
|
||||
a couple of cycles
|
||||
@return true iff execution stops normally
|
||||
*/
|
||||
bool execute(uInt32 number);
|
||||
bool execute(uInt32 cycles);
|
||||
|
||||
/**
|
||||
Tell the processor to stop executing instructions. Invoking this
|
||||
|
@ -318,7 +320,7 @@ class M6502 : public Serializable
|
|||
This is the actual dispatch function that does the grunt work. M6502::execute
|
||||
wraps it and makes sure that any pending halt is processed before returning.
|
||||
*/
|
||||
bool _execute(uInt32 number);
|
||||
bool _execute(uInt32 cycles);
|
||||
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
/**
|
||||
|
|
|
@ -646,10 +646,21 @@ void OSystem::mainLoop()
|
|||
myEventHandler->poll(getTicks());
|
||||
if(myQuitLoop) break; // Exit if the user wants to quit
|
||||
|
||||
Int64 cycles = myFrameBuffer->update();
|
||||
Int64 totalCycles = 0;
|
||||
const Int64 minCycles = myConsole ? myConsole->emulationTiming().minCyclesPerTimeslice() : 50000;
|
||||
const Int64 maxCycles = myConsole ? myConsole->emulationTiming().maxCyclesPerTimeslice() : 0;
|
||||
const uInt32 cyclesPerSecond = myConsole ? myConsole->emulationTiming().cyclesPerSecond() : 1;
|
||||
|
||||
do {
|
||||
Int64 cycles = myFrameBuffer->update(totalCycles > 0 ? minCycles - totalCycles : maxCycles);
|
||||
if (cycles < 0) break;
|
||||
|
||||
totalCycles += cycles;
|
||||
} while (totalCycles < minCycles);
|
||||
|
||||
duration<double> timeslice (
|
||||
(cycles >= 0) ?
|
||||
static_cast<double>(cycles) / static_cast<double>(76 * ((myConsole->timing() == ConsoleTiming::ntsc) ? (262 * 60) : (312 * 50))) :
|
||||
(totalCycles > 0) ?
|
||||
static_cast<double>(totalCycles) / static_cast<double>(cyclesPerSecond) :
|
||||
1. / 30.
|
||||
);
|
||||
|
||||
|
@ -659,7 +670,7 @@ void OSystem::mainLoop()
|
|||
if (duration_cast<duration<double>>(now - virtualTime).count() > 0)
|
||||
virtualTime = now;
|
||||
else if (virtualTime > now) {
|
||||
if (busyWait && cycles >= 0) {
|
||||
if (busyWait && totalCycles > 0) {
|
||||
while (high_resolution_clock::now() < virtualTime);
|
||||
}
|
||||
else std::this_thread::sleep_until(virtualTime);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
class OSystem;
|
||||
class AudioQueue;
|
||||
class EmulationTiming;
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
|
@ -51,7 +52,7 @@ class Sound
|
|||
Start the sound system, initializing it if necessary. This must be
|
||||
called before any calls are made to derived methods.
|
||||
*/
|
||||
virtual void open(shared_ptr<AudioQueue> audioQueue) = 0;
|
||||
virtual void open(shared_ptr<AudioQueue>, EmulationTiming*) = 0;
|
||||
|
||||
/**
|
||||
Should be called to stop the sound system. Once called the sound
|
||||
|
|
|
@ -53,6 +53,7 @@ MODULE_OBJS := \
|
|||
src/emucore/Control.o \
|
||||
src/emucore/Driving.o \
|
||||
src/emucore/EventHandler.o \
|
||||
src/emucore/EmulationTiming.o \
|
||||
src/emucore/FrameBuffer.o \
|
||||
src/emucore/FBSurface.o \
|
||||
src/emucore/FSNode.o \
|
||||
|
|
|
@ -805,11 +805,11 @@ bool TIA::loadDisplay(Serializer& in)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt64 TIA::update()
|
||||
uInt64 TIA::update(uInt32 maxCycles)
|
||||
{
|
||||
uInt64 timestampOld = myTimestamp;
|
||||
|
||||
mySystem->m6502().execute(25000);
|
||||
mySystem->m6502().execute(maxCycles);
|
||||
|
||||
updateEmulation();
|
||||
return (myTimestamp - timestampOld) / 3;
|
||||
|
|
|
@ -199,7 +199,7 @@ class TIA : public Device
|
|||
desired frame rate to update the TIA. Invoking this method will update
|
||||
the graphics buffer and generate the corresponding audio samples.
|
||||
*/
|
||||
uInt64 update();
|
||||
uInt64 update(uInt32 maxCycles = 50000);
|
||||
|
||||
/**
|
||||
Returns a pointer to the internal frame buffer.
|
||||
|
|
Loading…
Reference in New Issue