mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-02-17 03:15:39 +00:00
System: Implement CPU overclocking [SAVEVERSION+]
Partial credit to @CookiePLMonster as well.
This commit is contained in:
parent
8f9f039665
commit
27697d0508
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {};
|
||||
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue