nestopia/source/win32/NstManagerFrameClock.cpp

243 lines
6.2 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#include "NstObjectHeap.hpp"
#include "NstIoScreen.hpp"
#include "NstIoLog.hpp"
#include "NstResourceString.hpp"
#include "NstManager.hpp"
#include "NstDialogFrameClock.hpp"
#include "NstManagerFrameClock.hpp"
#include "../core/api/NstApiSound.hpp"
#include "../core/api/NstApiRewinder.hpp"
namespace Nestopia
{
namespace Managers
{
FrameClock::FrameClock(Window::Menu& m,Emulator& e,const Configuration& cfg,bool modernGPU)
:
Manager ( e, m, this, &FrameClock::OnEmuEvent, IDM_OPTIONS_TIMING, &FrameClock::OnMenuOptionsTiming ),
dialog ( new Window::FrameClock(cfg,modernGPU) )
{
UpdateSettings();
Io::Log log;
log << "Timer: performance counter ";
if (System::Timer::HasPerformanceCounter())
log << "present (" << uint(System::Timer::GetPerformanceCounterFrequency()) << " hz)\r\n";
else
log << "not present\r\n";
}
FrameClock::~FrameClock()
{
}
void FrameClock::OnMenuOptionsTiming(uint)
{
dialog->Open();
UpdateSettings();
}
void FrameClock::Save(Configuration& cfg) const
{
dialog->Save( cfg );
}
void FrameClock::UpdateSettings()
{
UpdateRewinderState();
settings.autoFrameSkip = dialog->UseAutoFrameSkip();
settings.maxFrameSkips = dialog->GetMaxFrameSkips();
emulator.ResetSpeed
(
dialog->UseDefaultSpeed() ? Emulator::DEFAULT_SPEED : dialog->GetSpeed(),
dialog->UseVSync(),
dialog->UseTrippleBuffering()
);
ResetTimer();
}
void FrameClock::UpdateRewinderState(bool force) const
{
if (NES_SUCCEEDED(Nes::Rewinder(emulator).Enable( force && dialog->UseRewinder() )))
Nes::Rewinder(emulator).EnableSound( !dialog->NoRewindSound() );
}
void FrameClock::ResetTimer()
{
timer.Reset( dialog->UsePerformanceCounter() ? System::Timer::PERFORMANCE : System::Timer::MULTIMEDIA );
counter = 0;
clkMul = timer.GetFrequency();
clkDiv = settings.refreshRate;
if (Nes::Machine(emulator).Is(Nes::Machine::NTSC))
{
if (settings.refreshRate == Nes::Machine::CLK_NTSC_DOT/Nes::Machine::CLK_NTSC_VSYNC)
{
clkMul *= uint(Nes::Machine::CLK_NTSC_VSYNC);
clkDiv = Nes::Machine::CLK_NTSC_DOT;
}
}
else
{
if (settings.refreshRate == Nes::Machine::CLK_PAL_DOT/Nes::Machine::CLK_PAL_VSYNC)
{
clkMul *= uint(Nes::Machine::CLK_PAL_VSYNC);
clkDiv = Nes::Machine::CLK_PAL_DOT;
}
}
}
void FrameClock::OnEmuEvent(const Emulator::Event event,const Emulator::Data data)
{
typedef Nes::Rewinder Rewinder;
switch (event)
{
case Emulator::EVENT_SPEEDING_ON:
settings.autoFrameSkip = (dialog->UseAutoFrameSkip() || dialog->GetAltSpeed() > emulator.GetDefaultSpeed());
emulator.SetSpeed( dialog->GetAltSpeed() );
Nes::Sound(emulator).Mute( dialog->NoAltSpeedSound() );
break;
case Emulator::EVENT_SPEEDING_OFF:
Nes::Sound(emulator).Mute( false );
if (dialog->UseDefaultRewindSpeed() || Rewinder(emulator).GetDirection() == Rewinder::FORWARD)
{
settings.autoFrameSkip = dialog->UseAutoFrameSkip();
emulator.SetSpeed( Emulator::DEFAULT_SPEED );
}
else
{
settings.autoFrameSkip = (dialog->UseAutoFrameSkip() || dialog->GetRewindSpeed() > emulator.GetDefaultSpeed());
emulator.SetSpeed( dialog->GetRewindSpeed() );
}
break;
case Emulator::EVENT_REWINDING_ON:
if (dialog->UseRewinder())
{
if (NES_FAILED(Rewinder(emulator).SetDirection( Rewinder::BACKWARD )))
Io::Screen() << Resource::String( IDS_EMU_ERR_CANT_REWIND );
}
break;
case Emulator::EVENT_REWINDING_OFF:
Rewinder(emulator).SetDirection( Rewinder::FORWARD );
break;
case Emulator::EVENT_SPEED:
settings.refreshRate = emulator.GetSpeed();
ResetTimer();
break;
case Emulator::EVENT_REWINDING_START:
if (!dialog->UseDefaultRewindSpeed() && !emulator.Speeding())
{
settings.autoFrameSkip = (dialog->UseAutoFrameSkip() || dialog->GetRewindSpeed() > emulator.GetDefaultSpeed());
emulator.SetSpeed( dialog->GetRewindSpeed() );
}
ResetTimer();
break;
case Emulator::EVENT_REWINDING_STOP:
if (!emulator.Speeding())
{
settings.autoFrameSkip = dialog->UseAutoFrameSkip();
emulator.SetSpeed( Emulator::DEFAULT_SPEED );
}
ResetTimer();
break;
case Emulator::EVENT_NETPLAY_MODE:
UpdateRewinderState( !data );
menu[IDM_OPTIONS_TIMING].Enable( !data );
break;
}
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("t", on)
#endif
uint FrameClock::Synchronize(const bool throttle,uint skips)
{
const System::Timer::Value current( timer.Elapsed() );
const System::Timer::Value next( clkMul * ++counter / clkDiv );
if (current > next)
{
const uint frames = current * clkDiv / clkMul;
if (skips & settings.autoFrameSkip)
{
skips = frames + 1 - counter;
if (skips > settings.maxFrameSkips)
skips = settings.maxFrameSkips;
counter += skips;
}
else
{
skips = 0;
}
if (counter < frames)
counter = frames;
return skips;
}
else if (throttle)
{
if (!timer.Wait( current, next ))
ResetTimer();
}
return 0;
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
}
}