System: Implement CPU overclocking [SAVEVERSION+]

Partial credit to @CookiePLMonster as well.
This commit is contained in:
Connor McLaughlin 2020-09-29 23:29:28 +10:00
parent 8f9f039665
commit 27697d0508
19 changed files with 249 additions and 36 deletions

View file

@ -298,6 +298,13 @@ void CDROM::SetUseReadThread(bool enabled)
m_reader.StopThread();
}
void CDROM::CPUClockChanged()
{
// reschedule the disc read event
if (IsReadingOrPlaying())
m_drive_event->SetInterval(GetTicksForRead());
}
u8 CDROM::ReadRegister(u32 offset)
{
switch (offset)
@ -612,23 +619,24 @@ TickCount CDROM::GetAckDelayForCommand(Command command)
TickCount CDROM::GetTicksForRead()
{
return m_mode.double_speed ? (MASTER_CLOCK / 150) : (MASTER_CLOCK / 75);
const TickCount tps = System::GetTicksPerSecond();
return m_mode.double_speed ? (tps / 150) : (tps / 75);
}
TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
{
const TickCount tps = System::GetTicksPerSecond();
const CDImage::LBA current_lba = m_secondary_status.motor_on ? m_current_lba : 0;
const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
// Formula from Mednafen.
TickCount ticks = std::max<TickCount>(
20000, static_cast<u32>(
((static_cast<u64>(lba_diff) * static_cast<u64>(MASTER_CLOCK) * static_cast<u64>(1000)) / (72 * 60 * 75)) /
1000));
((static_cast<u64>(lba_diff) * static_cast<u64>(tps) * static_cast<u64>(1000)) / (72 * 60 * 75)) / 1000));
if (!m_secondary_status.motor_on)
ticks += MASTER_CLOCK;
ticks += tps;
if (lba_diff >= 2550)
ticks += static_cast<TickCount>(u64(MASTER_CLOCK) * 300 / 1000);
ticks += static_cast<TickCount>((u64(tps) * 300) / 1000);
else
{
// When paused, the CDC seems to keep reading the disc until it hits the position it's set to, then skip 10-15
@ -644,7 +652,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
m_current_double_speed = m_mode.double_speed;
// Approximate time for the motor to change speed?
ticks += static_cast<u32>(static_cast<double>(MASTER_CLOCK) * 0.1);
ticks += static_cast<u32>(static_cast<double>(tps) * 0.1);
}
Log_DevPrintf("Seek time for %u LBAs: %d", lba_diff, ticks);
@ -788,7 +796,7 @@ void CDROM::ExecuteCommand()
SendACKAndStat();
m_drive_state = DriveState::ReadingTOC;
m_drive_event->Schedule(MASTER_CLOCK / 2); // half a second
m_drive_event->Schedule(System::GetTicksPerSecond() / 2); // half a second
}
EndCommand();
@ -874,7 +882,7 @@ void CDROM::ExecuteCommand()
m_async_command_parameter = session;
m_drive_state = DriveState::ChangingSession;
m_drive_event->Schedule(MASTER_CLOCK / 2); // half a second
m_drive_event->Schedule(System::GetTicksPerSecond() / 2); // half a second
}
EndCommand();
@ -1011,7 +1019,7 @@ void CDROM::ExecuteCommand()
SendACKAndStat();
m_drive_state = DriveState::Resetting;
m_drive_event->Schedule(MASTER_CLOCK);
m_drive_event->Schedule(System::GetTicksPerSecond());
}
EndCommand();

View file

@ -31,6 +31,8 @@ public:
void InsertMedia(std::unique_ptr<CDImage> media);
std::unique_ptr<CDImage> RemoveMedia(bool force = false);
void CPUClockChanged();
// I/O
u8 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u8 value);

View file

@ -67,7 +67,7 @@ void Shutdown()
void Reset()
{
g_state.pending_ticks = 0;
g_state.downcount = MAX_SLICE_SIZE;
g_state.downcount = 0;
g_state.regs = {};

View file

@ -51,7 +51,7 @@ struct State
{
// ticks the CPU has executed
TickCount pending_ticks = 0;
TickCount downcount = MAX_SLICE_SIZE;
TickCount downcount = 0;
Registers regs = {};
Cop0Registers cop0_regs = {};

View file

@ -57,6 +57,11 @@ void GPU::UpdateSettings()
UpdateCRTCDisplayParameters();
}
void GPU::CPUClockChanged()
{
UpdateCRTCConfig();
}
void GPU::UpdateResolutionScale() {}
std::tuple<u32, u32> GPU::GetEffectiveDisplayResolution()
@ -427,8 +432,9 @@ float GPU::ComputeHorizontalFrequency() const
{
const CRTCState& cs = m_crtc_state;
TickCount fractional_ticks = 0;
return static_cast<float>(static_cast<double>(SystemTicksToCRTCTicks(MASTER_CLOCK, &fractional_ticks)) /
static_cast<double>(cs.horizontal_total));
return static_cast<float>(
static_cast<double>(SystemTicksToCRTCTicks(System::GetTicksPerSecond(), &fractional_ticks)) /
static_cast<double>(cs.horizontal_total));
}
float GPU::ComputeVerticalFrequency() const
@ -436,8 +442,9 @@ float GPU::ComputeVerticalFrequency() const
const CRTCState& cs = m_crtc_state;
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
TickCount fractional_ticks = 0;
return static_cast<float>(static_cast<double>(SystemTicksToCRTCTicks(MASTER_CLOCK, &fractional_ticks)) /
static_cast<double>(ticks_per_frame));
return static_cast<float>(
static_cast<double>(SystemTicksToCRTCTicks(System::GetTicksPerSecond(), &fractional_ticks)) /
static_cast<double>(ticks_per_frame));
}
float GPU::GetDisplayAspectRatio() const
@ -459,7 +466,7 @@ void GPU::UpdateCRTCConfig()
cs.current_scanline %= PAL_TOTAL_LINES;
cs.horizontal_total = PAL_TICKS_PER_LINE;
cs.horizontal_sync_start = PAL_HSYNC_TICKS;
cs.current_tick_in_scanline %= PAL_TICKS_PER_LINE;
cs.current_tick_in_scanline %= System::ScaleTicksToOverclock(PAL_TICKS_PER_LINE);
}
else
{
@ -467,7 +474,7 @@ void GPU::UpdateCRTCConfig()
cs.current_scanline %= NTSC_TOTAL_LINES;
cs.horizontal_total = NTSC_TICKS_PER_LINE;
cs.horizontal_sync_start = NTSC_HSYNC_TICKS;
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
cs.current_tick_in_scanline %= System::ScaleTicksToOverclock(NTSC_TICKS_PER_LINE);
}
cs.in_hblank = (cs.current_tick_in_scanline >= cs.horizontal_sync_start);
@ -498,6 +505,12 @@ void GPU::UpdateCRTCConfig()
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
}
cs.horizontal_display_start =
static_cast<u16>(System::ScaleTicksToOverclock(static_cast<TickCount>(cs.horizontal_display_start)));
cs.horizontal_display_end =
static_cast<u16>(System::ScaleTicksToOverclock(static_cast<TickCount>(cs.horizontal_display_end)));
cs.horizontal_total = static_cast<u16>(System::ScaleTicksToOverclock(static_cast<TickCount>(cs.horizontal_total)));
System::SetThrottleFrequency(ComputeVerticalFrequency());
UpdateCRTCDisplayParameters();

View file

@ -132,6 +132,8 @@ public:
// Render statistics debug window.
void DrawDebugStateWindow();
void CPUClockChanged();
// MMIO access
u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value);

View file

@ -543,6 +543,14 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
{
if (System::IsValid())
{
if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
(g_settings.cpu_overclock_active &&
(g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator ||
g_settings.cpu_overclock_denominator != old_settings.cpu_overclock_denominator)))
{
System::UpdateOverclock();
}
if (g_settings.gpu_renderer != old_settings.gpu_renderer ||
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
{

View file

@ -13,9 +13,8 @@ MemoryCard::MemoryCard()
{
m_FLAG.no_write_yet = true;
m_save_event =
TimingEvents::CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
m_save_event = TimingEvents::CreateTimingEvent("Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(),
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
}
MemoryCard::~MemoryCard()
@ -23,6 +22,11 @@ MemoryCard::~MemoryCard()
SaveIfChanged(false);
}
TickCount MemoryCard::GetSaveDelayInTicks()
{
return System::GetTicksPerSecond() * SAVE_DELAY_IN_SECONDS;
}
void MemoryCard::Reset()
{
ResetTransferState();
@ -309,5 +313,5 @@ void MemoryCard::QueueFileSave()
return;
// save in one second, that should be long enough for everything to finish writing
m_save_event->Schedule(SAVE_DELAY_IN_SYSCLK_TICKS);
m_save_event->Schedule(GetSaveDelayInTicks());
}

View file

@ -35,7 +35,6 @@ private:
{
// save in three seconds, that should be long enough for everything to finish writing
SAVE_DELAY_IN_SECONDS = 5,
SAVE_DELAY_IN_SYSCLK_TICKS = MASTER_CLOCK * SAVE_DELAY_IN_SECONDS,
};
union FLAG
@ -74,6 +73,8 @@ private:
WriteEnd,
};
static TickCount GetSaveDelayInTicks();
bool LoadFromFile();
bool SaveIfChanged(bool display_osd_message);
void QueueFileSave();

View file

@ -2,7 +2,7 @@
#include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 41;
static constexpr u32 SAVE_STATE_VERSION = 42;
#pragma pack(push, 4)
struct SAVE_STATE_HEADER

View file

@ -5,6 +5,7 @@
#include "host_interface.h"
#include <algorithm>
#include <array>
#include <numeric>
Settings g_settings;
@ -75,6 +76,28 @@ bool Settings::HasAnyPerGameMemoryCards() const
});
}
void Settings::CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator)
{
const u32 percent_gcd = std::gcd(percent, 100);
*numerator = percent / percent_gcd;
*denominator = 100u / percent_gcd;
}
u32 Settings::CPUOverclockFractionToPercent(u32 numerator, u32 denominator)
{
return (numerator * 100u) / denominator;
}
void Settings::SetCPUOverclockPercent(u32 percent)
{
CPUOverclockPercentToFraction(percent, &cpu_overclock_numerator, &cpu_overclock_denominator);
}
u32 Settings::GetCPUOverclockPercent() const
{
return CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator);
}
void Settings::Load(SettingsInterface& si)
{
region =
@ -95,6 +118,10 @@ void Settings::Load(SettingsInterface& si)
ParseCPUExecutionMode(
si.GetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(DEFAULT_CPU_EXECUTION_MODE)).c_str())
.value_or(DEFAULT_CPU_EXECUTION_MODE);
cpu_overclock_numerator = std::max(si.GetIntValue("CPU", "OverclockNumerator", 1), 1);
cpu_overclock_denominator = std::max(si.GetIntValue("CPU", "OverclockDenominator", 1), 1);
cpu_overclock_enable = si.GetBoolValue("CPU", "OverclockEnable", false);
cpu_overclock_active = (cpu_overclock_enable && (cpu_overclock_numerator != 1 || cpu_overclock_denominator != 1));
cpu_recompiler_memory_exceptions = si.GetBoolValue("CPU", "RecompilerMemoryExceptions", false);
cpu_recompiler_icache = si.GetBoolValue("CPU", "RecompilerICache", false);
@ -218,6 +245,9 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
si.SetIntValue("CPU", "OverclockNumerator", cpu_overclock_numerator);
si.SetIntValue("CPU", "OverclockDenominator", cpu_overclock_denominator);
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", cpu_recompiler_memory_exceptions);
si.SetBoolValue("CPU", "RecompilerICache", cpu_recompiler_icache);

View file

@ -70,6 +70,10 @@ struct Settings
ConsoleRegion region = ConsoleRegion::Auto;
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
u32 cpu_overclock_numerator = 1;
u32 cpu_overclock_denominator = 1;
bool cpu_overclock_enable = false;
bool cpu_overclock_active = false;
bool cpu_recompiler_memory_exceptions = false;
bool cpu_recompiler_icache = false;
@ -174,6 +178,12 @@ struct Settings
bool HasAnyPerGameMemoryCards() const;
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator);
void SetCPUOverclockPercent(u32 percent);
u32 GetCPUOverclockPercent() const;
enum : u32
{
DEFAULT_DMA_MAX_SLICE_TICKS = 1000,

View file

@ -21,7 +21,10 @@ SPU::~SPU() = default;
void SPU::Initialize()
{
m_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
// (X * D) / N / 768 -> (X * D) / (N * 768)
m_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK);
m_cpu_tick_divider = static_cast<TickCount>(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK);
m_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", m_cpu_ticks_per_spu_tick, m_cpu_ticks_per_spu_tick,
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
m_transfer_event =
TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
@ -30,6 +33,15 @@ void SPU::Initialize()
Reset();
}
void SPU::CPUClockChanged()
{
// (X * D) / N / 768 -> (X * D) / (N * 768)
m_cpu_ticks_per_spu_tick = System::ScaleTicksToOverclock(SYSCLK_TICKS_PER_SPU_TICK);
m_cpu_tick_divider = static_cast<TickCount>(g_settings.cpu_overclock_numerator * SYSCLK_TICKS_PER_SPU_TICK);
m_ticks_carry = 0;
UpdateEventInterval();
}
void SPU::Shutdown()
{
m_tick_event.reset();
@ -680,8 +692,19 @@ void SPU::IncrementCaptureBufferPosition()
void SPU::Execute(TickCount ticks)
{
u32 remaining_frames = static_cast<u32>((ticks + m_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK);
m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK;
u32 remaining_frames;
if (g_settings.cpu_overclock_active)
{
// (X * D) / N / 768 -> (X * D) / (N * 768)
const u64 num = (static_cast<u64>(ticks) * g_settings.cpu_overclock_denominator) + static_cast<u32>(m_ticks_carry);
remaining_frames = static_cast<u32>(num / m_cpu_tick_divider);
m_ticks_carry = static_cast<TickCount>(num % m_cpu_tick_divider);
}
else
{
remaining_frames = static_cast<u32>((ticks + m_ticks_carry) / SYSCLK_TICKS_PER_SPU_TICK);
m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK;
}
while (remaining_frames > 0)
{
@ -796,14 +819,19 @@ void SPU::UpdateEventInterval()
// TODO: Make this predict how long until the interrupt will be hit instead...
const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames;
const TickCount interval_ticks = static_cast<TickCount>(interval) * SYSCLK_TICKS_PER_SPU_TICK;
const TickCount interval_ticks = static_cast<TickCount>(interval) * m_cpu_ticks_per_spu_tick;
if (m_tick_event->IsActive() && m_tick_event->GetInterval() == interval_ticks)
return;
// Ensure all pending ticks have been executed, since we won't get them back after rescheduling.
m_tick_event->InvokeEarly(true);
m_tick_event->SetInterval(interval_ticks);
m_tick_event->Schedule(interval_ticks - m_ticks_carry);
TickCount downcount = interval_ticks;
if (!g_settings.cpu_overclock_active)
downcount -= m_ticks_carry;
m_tick_event->Schedule(downcount);
}
void SPU::ExecuteTransfer(TickCount ticks)

View file

@ -2,6 +2,7 @@
#include "common/bitfield.h"
#include "common/fifo_queue.h"
#include "types.h"
#include "system.h"
#include <array>
#include <memory>
@ -20,6 +21,7 @@ public:
~SPU();
void Initialize();
void CPUClockChanged();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
@ -54,7 +56,7 @@ private:
static constexpr u32 VOICE_ADDRESS_SHIFT = 3;
static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28;
static constexpr u32 SAMPLE_RATE = 44100;
static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = MASTER_CLOCK / SAMPLE_RATE; // 0x300
static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE; // 0x300
static constexpr s16 ENVELOPE_MIN_VOLUME = 0;
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400;
@ -370,6 +372,8 @@ private:
std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer;
TickCount m_ticks_carry = 0;
TickCount m_cpu_ticks_per_spu_tick = 0;
TickCount m_cpu_tick_divider = 0;
SPUCNT m_SPUCNT = {};
SPUSTAT m_SPUSTAT = {};

View file

@ -68,6 +68,8 @@ static void UpdateRunningGame(const char* path, CDImage* image);
static State s_state = State::Shutdown;
static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
TickCount g_ticks_per_second = MASTER_CLOCK;
static TickCount s_max_slice_ticks = MASTER_CLOCK / 10;
static u32 s_frame_number = 1;
static u32 s_internal_frame_number = 1;
@ -143,6 +145,22 @@ bool IsPALRegion()
return s_region == ConsoleRegion::PAL;
}
TickCount GetMaxSliceTicks()
{
return s_max_slice_ticks;
}
void UpdateOverclock()
{
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
g_spu.CPUClockChanged();
g_cdrom.CPUClockChanged();
g_gpu->CPUClockChanged();
g_timers.CPUClocksChanged();
UpdateThrottlePeriod();
}
u32 GetFrameNumber()
{
return s_frame_number;
@ -682,6 +700,8 @@ bool Boot(const SystemBootParameters& params)
bool Initialize(bool force_software_renderer)
{
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
s_frame_number = 1;
s_internal_frame_number = 1;
@ -725,6 +745,15 @@ bool Initialize(bool force_software_renderer)
g_mdec.Initialize();
g_sio.Initialize();
if (g_settings.cpu_overclock_active)
{
g_host_interface->AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage",
"CPU clock speed is set to %u%% (%u / %u). This may result in instability."),
g_settings.GetCPUOverclockPercent(), g_settings.cpu_overclock_numerator, g_settings.cpu_overclock_denominator);
}
UpdateThrottlePeriod();
return true;
}
@ -845,6 +874,31 @@ bool DoState(StateWrapper& sw)
if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw))
return false;
if (!sw.DoMarker("Overclock"))
return false;
bool cpu_overclock_active = g_settings.cpu_overclock_active;
u32 cpu_overclock_numerator = g_settings.cpu_overclock_numerator;
u32 cpu_overclock_denominator = g_settings.cpu_overclock_denominator;
sw.Do(&cpu_overclock_active);
sw.Do(&cpu_overclock_numerator);
sw.Do(&cpu_overclock_denominator);
if (sw.IsReading() && (cpu_overclock_active != g_settings.cpu_overclock_active ||
(cpu_overclock_active && (g_settings.cpu_overclock_numerator != cpu_overclock_numerator ||
g_settings.cpu_overclock_denominator != cpu_overclock_denominator))))
{
g_host_interface->AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage",
"WARNING: CPU overclock (%u%%) was different in save state (%u%%)."),
g_settings.cpu_overclock_enable ? g_settings.GetCPUOverclockPercent() : 100u,
cpu_overclock_active ?
Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) :
100u);
UpdateOverclock();
}
return !sw.HasError();
}
@ -1186,7 +1240,7 @@ void UpdatePerformanceCounters()
s_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time;
s_last_internal_frame_number = s_internal_frame_number;
s_speed = static_cast<float>(static_cast<double>(global_tick_counter - s_last_global_tick_counter) /
(static_cast<double>(MASTER_CLOCK) * time)) *
(static_cast<double>(g_ticks_per_second) * time)) *
100.0f;
s_last_global_tick_counter = global_tick_counter;
s_fps_timer.Reset();

View file

@ -1,6 +1,7 @@
#pragma once
#include "common/timer.h"
#include "host_interface.h"
#include "settings.h"
#include "timing_event.h"
#include "types.h"
#include <memory>
@ -40,6 +41,11 @@ enum : u32
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
};
enum : TickCount
{
MASTER_CLOCK = 44100 * 0x300 // 33868800Hz or 33.8688MHz, also used as CPU clock
};
enum class State
{
Shutdown,
@ -48,6 +54,8 @@ enum class State
Paused
};
extern TickCount g_ticks_per_second;
/// Returns true if the filename is a PlayStation executable we can inject.
bool IsExeFileName(const char* path);
@ -80,6 +88,36 @@ bool IsValid();
ConsoleRegion GetRegion();
bool IsPALRegion();
ALWAYS_INLINE TickCount GetTicksPerSecond()
{
return g_ticks_per_second;
}
ALWAYS_INLINE_RELEASE TickCount ScaleTicksToOverclock(TickCount ticks)
{
if (!g_settings.cpu_overclock_active)
return ticks;
return static_cast<TickCount>((static_cast<u64>(static_cast<u32>(ticks)) * g_settings.cpu_overclock_numerator) /
g_settings.cpu_overclock_denominator);
}
ALWAYS_INLINE_RELEASE TickCount UnscaleTicksToOverclock(TickCount ticks, TickCount* remainder)
{
if (!g_settings.cpu_overclock_active)
return ticks;
const u64 num =
(static_cast<u32>(ticks) * static_cast<u64>(g_settings.cpu_overclock_denominator)) + static_cast<u32>(*remainder);
const TickCount t = static_cast<u32>(num / g_settings.cpu_overclock_numerator);
*remainder = static_cast<u32>(num % g_settings.cpu_overclock_numerator);
return t;
}
TickCount GetMaxSliceTicks();
void UpdateOverclock();
u32 GetFrameNumber();
u32 GetInternalFrameNumber();
void FrameDone();

View file

@ -41,6 +41,7 @@ void Timers::Reset()
cs.irq_done = false;
}
m_syclk_ticks_carry = 0;
m_sysclk_div_8_carry = 0;
UpdateSysClkEvent();
}
@ -59,6 +60,7 @@ bool Timers::DoState(StateWrapper& sw)
sw.Do(&cs.irq_done);
}
sw.Do(&m_syclk_ticks_carry);
sw.Do(&m_sysclk_div_8_carry);
if (sw.IsReading())
@ -67,6 +69,11 @@ bool Timers::DoState(StateWrapper& sw)
return !sw.HasError();
}
void Timers::CPUClocksChanged()
{
m_syclk_ticks_carry = 0;
}
void Timers::SetGate(u32 timer, bool state)
{
CounterState& cs = m_states[timer];
@ -157,6 +164,8 @@ void Timers::AddTicks(u32 timer, TickCount count)
void Timers::AddSysClkTicks(TickCount sysclk_ticks)
{
sysclk_ticks = System::UnscaleTicksToOverclock(sysclk_ticks, &m_syclk_ticks_carry);
if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled)
AddTicks(0, sysclk_ticks);
if (!m_states[1].external_counting_enabled && m_states[1].counting_enabled)
@ -351,7 +360,9 @@ TickCount Timers::GetTicksUntilNextInterrupt() const
min_ticks_for_this_timer = std::min(min_ticks_for_this_timer, static_cast<TickCount>(0xFFFF - cs.counter));
if (cs.external_counting_enabled) // sysclk/8 for timer 2
min_ticks_for_this_timer = std::max<TickCount>(1, min_ticks_for_this_timer * 8);
min_ticks_for_this_timer = std::max<TickCount>(1, System::ScaleTicksToOverclock(min_ticks_for_this_timer * 8));
else
min_ticks_for_this_timer = std::max<TickCount>(1, System::ScaleTicksToOverclock(min_ticks_for_this_timer));
min_ticks = std::min(min_ticks, min_ticks_for_this_timer);
}
@ -364,7 +375,7 @@ void Timers::UpdateSysClkEvent()
// Still update once every 100ms. If we get polled we'll execute sooner.
const TickCount ticks = GetTicksUntilNextInterrupt();
if (ticks == std::numeric_limits<TickCount>::max())
m_sysclk_event->Schedule(MAX_SLICE_SIZE);
m_sysclk_event->Schedule(System::GetMaxSliceTicks());
else
m_sysclk_event->Schedule(ticks);
}

View file

@ -24,6 +24,8 @@ public:
void DrawDebugStateWindow();
void CPUClocksChanged();
// dot clock/hblank/sysclk div 8
ALWAYS_INLINE bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
@ -92,7 +94,8 @@ private:
std::unique_ptr<TimingEvent> m_sysclk_event;
std::array<CounterState, NUM_TIMERS> m_states{};
u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
TickCount m_syclk_ticks_carry = 0; // 0 unless overclocking is enabled
u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
};
extern Timers g_timers;

View file

@ -19,9 +19,6 @@ enum class MemoryAccessSize : u32
using TickCount = s32;
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock
static constexpr TickCount MAX_SLICE_SIZE = MASTER_CLOCK / 10;
enum class ConsoleRegion
{
Auto,