mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-08 04:25:37 +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();
|
m_reader.StopThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDROM::CPUClockChanged()
|
||||||
|
{
|
||||||
|
// reschedule the disc read event
|
||||||
|
if (IsReadingOrPlaying())
|
||||||
|
m_drive_event->SetInterval(GetTicksForRead());
|
||||||
|
}
|
||||||
|
|
||||||
u8 CDROM::ReadRegister(u32 offset)
|
u8 CDROM::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
switch (offset)
|
switch (offset)
|
||||||
@ -612,23 +619,24 @@ TickCount CDROM::GetAckDelayForCommand(Command command)
|
|||||||
|
|
||||||
TickCount CDROM::GetTicksForRead()
|
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)
|
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 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));
|
const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
|
||||||
|
|
||||||
// Formula from Mednafen.
|
// Formula from Mednafen.
|
||||||
TickCount ticks = std::max<TickCount>(
|
TickCount ticks = std::max<TickCount>(
|
||||||
20000, static_cast<u32>(
|
20000, static_cast<u32>(
|
||||||
((static_cast<u64>(lba_diff) * static_cast<u64>(MASTER_CLOCK) * static_cast<u64>(1000)) / (72 * 60 * 75)) /
|
((static_cast<u64>(lba_diff) * static_cast<u64>(tps) * static_cast<u64>(1000)) / (72 * 60 * 75)) / 1000));
|
||||||
1000));
|
|
||||||
if (!m_secondary_status.motor_on)
|
if (!m_secondary_status.motor_on)
|
||||||
ticks += MASTER_CLOCK;
|
ticks += tps;
|
||||||
if (lba_diff >= 2550)
|
if (lba_diff >= 2550)
|
||||||
ticks += static_cast<TickCount>(u64(MASTER_CLOCK) * 300 / 1000);
|
ticks += static_cast<TickCount>((u64(tps) * 300) / 1000);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// When paused, the CDC seems to keep reading the disc until it hits the position it's set to, then skip 10-15
|
// 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;
|
m_current_double_speed = m_mode.double_speed;
|
||||||
|
|
||||||
// Approximate time for the motor to change 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);
|
Log_DevPrintf("Seek time for %u LBAs: %d", lba_diff, ticks);
|
||||||
@ -788,7 +796,7 @@ void CDROM::ExecuteCommand()
|
|||||||
SendACKAndStat();
|
SendACKAndStat();
|
||||||
|
|
||||||
m_drive_state = DriveState::ReadingTOC;
|
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();
|
EndCommand();
|
||||||
@ -874,7 +882,7 @@ void CDROM::ExecuteCommand()
|
|||||||
|
|
||||||
m_async_command_parameter = session;
|
m_async_command_parameter = session;
|
||||||
m_drive_state = DriveState::ChangingSession;
|
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();
|
EndCommand();
|
||||||
@ -1011,7 +1019,7 @@ void CDROM::ExecuteCommand()
|
|||||||
SendACKAndStat();
|
SendACKAndStat();
|
||||||
|
|
||||||
m_drive_state = DriveState::Resetting;
|
m_drive_state = DriveState::Resetting;
|
||||||
m_drive_event->Schedule(MASTER_CLOCK);
|
m_drive_event->Schedule(System::GetTicksPerSecond());
|
||||||
}
|
}
|
||||||
|
|
||||||
EndCommand();
|
EndCommand();
|
||||||
|
@ -31,6 +31,8 @@ public:
|
|||||||
void InsertMedia(std::unique_ptr<CDImage> media);
|
void InsertMedia(std::unique_ptr<CDImage> media);
|
||||||
std::unique_ptr<CDImage> RemoveMedia(bool force = false);
|
std::unique_ptr<CDImage> RemoveMedia(bool force = false);
|
||||||
|
|
||||||
|
void CPUClockChanged();
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
u8 ReadRegister(u32 offset);
|
u8 ReadRegister(u32 offset);
|
||||||
void WriteRegister(u32 offset, u8 value);
|
void WriteRegister(u32 offset, u8 value);
|
||||||
|
@ -67,7 +67,7 @@ void Shutdown()
|
|||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
g_state.pending_ticks = 0;
|
g_state.pending_ticks = 0;
|
||||||
g_state.downcount = MAX_SLICE_SIZE;
|
g_state.downcount = 0;
|
||||||
|
|
||||||
g_state.regs = {};
|
g_state.regs = {};
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ struct State
|
|||||||
{
|
{
|
||||||
// ticks the CPU has executed
|
// ticks the CPU has executed
|
||||||
TickCount pending_ticks = 0;
|
TickCount pending_ticks = 0;
|
||||||
TickCount downcount = MAX_SLICE_SIZE;
|
TickCount downcount = 0;
|
||||||
|
|
||||||
Registers regs = {};
|
Registers regs = {};
|
||||||
Cop0Registers cop0_regs = {};
|
Cop0Registers cop0_regs = {};
|
||||||
|
@ -57,6 +57,11 @@ void GPU::UpdateSettings()
|
|||||||
UpdateCRTCDisplayParameters();
|
UpdateCRTCDisplayParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GPU::CPUClockChanged()
|
||||||
|
{
|
||||||
|
UpdateCRTCConfig();
|
||||||
|
}
|
||||||
|
|
||||||
void GPU::UpdateResolutionScale() {}
|
void GPU::UpdateResolutionScale() {}
|
||||||
|
|
||||||
std::tuple<u32, u32> GPU::GetEffectiveDisplayResolution()
|
std::tuple<u32, u32> GPU::GetEffectiveDisplayResolution()
|
||||||
@ -427,7 +432,8 @@ float GPU::ComputeHorizontalFrequency() const
|
|||||||
{
|
{
|
||||||
const CRTCState& cs = m_crtc_state;
|
const CRTCState& cs = m_crtc_state;
|
||||||
TickCount fractional_ticks = 0;
|
TickCount fractional_ticks = 0;
|
||||||
return static_cast<float>(static_cast<double>(SystemTicksToCRTCTicks(MASTER_CLOCK, &fractional_ticks)) /
|
return static_cast<float>(
|
||||||
|
static_cast<double>(SystemTicksToCRTCTicks(System::GetTicksPerSecond(), &fractional_ticks)) /
|
||||||
static_cast<double>(cs.horizontal_total));
|
static_cast<double>(cs.horizontal_total));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +442,8 @@ float GPU::ComputeVerticalFrequency() const
|
|||||||
const CRTCState& cs = m_crtc_state;
|
const CRTCState& cs = m_crtc_state;
|
||||||
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
||||||
TickCount fractional_ticks = 0;
|
TickCount fractional_ticks = 0;
|
||||||
return static_cast<float>(static_cast<double>(SystemTicksToCRTCTicks(MASTER_CLOCK, &fractional_ticks)) /
|
return static_cast<float>(
|
||||||
|
static_cast<double>(SystemTicksToCRTCTicks(System::GetTicksPerSecond(), &fractional_ticks)) /
|
||||||
static_cast<double>(ticks_per_frame));
|
static_cast<double>(ticks_per_frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,7 +466,7 @@ void GPU::UpdateCRTCConfig()
|
|||||||
cs.current_scanline %= PAL_TOTAL_LINES;
|
cs.current_scanline %= PAL_TOTAL_LINES;
|
||||||
cs.horizontal_total = PAL_TICKS_PER_LINE;
|
cs.horizontal_total = PAL_TICKS_PER_LINE;
|
||||||
cs.horizontal_sync_start = PAL_HSYNC_TICKS;
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -467,7 +474,7 @@ void GPU::UpdateCRTCConfig()
|
|||||||
cs.current_scanline %= NTSC_TOTAL_LINES;
|
cs.current_scanline %= NTSC_TOTAL_LINES;
|
||||||
cs.horizontal_total = NTSC_TICKS_PER_LINE;
|
cs.horizontal_total = NTSC_TICKS_PER_LINE;
|
||||||
cs.horizontal_sync_start = NTSC_HSYNC_TICKS;
|
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);
|
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.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());
|
System::SetThrottleFrequency(ComputeVerticalFrequency());
|
||||||
|
|
||||||
UpdateCRTCDisplayParameters();
|
UpdateCRTCDisplayParameters();
|
||||||
|
@ -132,6 +132,8 @@ public:
|
|||||||
// Render statistics debug window.
|
// Render statistics debug window.
|
||||||
void DrawDebugStateWindow();
|
void DrawDebugStateWindow();
|
||||||
|
|
||||||
|
void CPUClockChanged();
|
||||||
|
|
||||||
// MMIO access
|
// MMIO access
|
||||||
u32 ReadRegister(u32 offset);
|
u32 ReadRegister(u32 offset);
|
||||||
void WriteRegister(u32 offset, u32 value);
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
@ -543,6 +543,14 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||||||
{
|
{
|
||||||
if (System::IsValid())
|
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 ||
|
if (g_settings.gpu_renderer != old_settings.gpu_renderer ||
|
||||||
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
|
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
|
||||||
{
|
{
|
||||||
|
@ -13,8 +13,7 @@ MemoryCard::MemoryCard()
|
|||||||
{
|
{
|
||||||
m_FLAG.no_write_yet = true;
|
m_FLAG.no_write_yet = true;
|
||||||
|
|
||||||
m_save_event =
|
m_save_event = TimingEvents::CreateTimingEvent("Memory Card Host Flush", GetSaveDelayInTicks(), GetSaveDelayInTicks(),
|
||||||
TimingEvents::CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
|
|
||||||
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
|
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +22,11 @@ MemoryCard::~MemoryCard()
|
|||||||
SaveIfChanged(false);
|
SaveIfChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TickCount MemoryCard::GetSaveDelayInTicks()
|
||||||
|
{
|
||||||
|
return System::GetTicksPerSecond() * SAVE_DELAY_IN_SECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
void MemoryCard::Reset()
|
void MemoryCard::Reset()
|
||||||
{
|
{
|
||||||
ResetTransferState();
|
ResetTransferState();
|
||||||
@ -309,5 +313,5 @@ void MemoryCard::QueueFileSave()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// save in one second, that should be long enough for everything to finish writing
|
// 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 in three seconds, that should be long enough for everything to finish writing
|
||||||
SAVE_DELAY_IN_SECONDS = 5,
|
SAVE_DELAY_IN_SECONDS = 5,
|
||||||
SAVE_DELAY_IN_SYSCLK_TICKS = MASTER_CLOCK * SAVE_DELAY_IN_SECONDS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
union FLAG
|
union FLAG
|
||||||
@ -74,6 +73,8 @@ private:
|
|||||||
WriteEnd,
|
WriteEnd,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static TickCount GetSaveDelayInTicks();
|
||||||
|
|
||||||
bool LoadFromFile();
|
bool LoadFromFile();
|
||||||
bool SaveIfChanged(bool display_osd_message);
|
bool SaveIfChanged(bool display_osd_message);
|
||||||
void QueueFileSave();
|
void QueueFileSave();
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
|
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)
|
#pragma pack(push, 4)
|
||||||
struct SAVE_STATE_HEADER
|
struct SAVE_STATE_HEADER
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
Settings g_settings;
|
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)
|
void Settings::Load(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
region =
|
region =
|
||||||
@ -95,6 +118,10 @@ void Settings::Load(SettingsInterface& si)
|
|||||||
ParseCPUExecutionMode(
|
ParseCPUExecutionMode(
|
||||||
si.GetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(DEFAULT_CPU_EXECUTION_MODE)).c_str())
|
si.GetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(DEFAULT_CPU_EXECUTION_MODE)).c_str())
|
||||||
.value_or(DEFAULT_CPU_EXECUTION_MODE);
|
.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_memory_exceptions = si.GetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
||||||
cpu_recompiler_icache = si.GetBoolValue("CPU", "RecompilerICache", 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.SetBoolValue("Main", "AutoLoadCheats", auto_load_cheats);
|
||||||
|
|
||||||
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
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", "RecompilerMemoryExceptions", cpu_recompiler_memory_exceptions);
|
||||||
si.SetBoolValue("CPU", "RecompilerICache", cpu_recompiler_icache);
|
si.SetBoolValue("CPU", "RecompilerICache", cpu_recompiler_icache);
|
||||||
|
|
||||||
|
@ -70,6 +70,10 @@ struct Settings
|
|||||||
ConsoleRegion region = ConsoleRegion::Auto;
|
ConsoleRegion region = ConsoleRegion::Auto;
|
||||||
|
|
||||||
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
|
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_memory_exceptions = false;
|
||||||
bool cpu_recompiler_icache = false;
|
bool cpu_recompiler_icache = false;
|
||||||
|
|
||||||
@ -174,6 +178,12 @@ struct Settings
|
|||||||
|
|
||||||
bool HasAnyPerGameMemoryCards() const;
|
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
|
enum : u32
|
||||||
{
|
{
|
||||||
DEFAULT_DMA_MAX_SLICE_TICKS = 1000,
|
DEFAULT_DMA_MAX_SLICE_TICKS = 1000,
|
||||||
|
@ -21,7 +21,10 @@ SPU::~SPU() = default;
|
|||||||
|
|
||||||
void SPU::Initialize()
|
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);
|
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
|
||||||
m_transfer_event =
|
m_transfer_event =
|
||||||
TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
||||||
@ -30,6 +33,15 @@ void SPU::Initialize()
|
|||||||
Reset();
|
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()
|
void SPU::Shutdown()
|
||||||
{
|
{
|
||||||
m_tick_event.reset();
|
m_tick_event.reset();
|
||||||
@ -680,8 +692,19 @@ void SPU::IncrementCaptureBufferPosition()
|
|||||||
|
|
||||||
void SPU::Execute(TickCount ticks)
|
void SPU::Execute(TickCount ticks)
|
||||||
{
|
{
|
||||||
u32 remaining_frames = static_cast<u32>((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;
|
m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK;
|
||||||
|
}
|
||||||
|
|
||||||
while (remaining_frames > 0)
|
while (remaining_frames > 0)
|
||||||
{
|
{
|
||||||
@ -796,14 +819,19 @@ void SPU::UpdateEventInterval()
|
|||||||
|
|
||||||
// TODO: Make this predict how long until the interrupt will be hit instead...
|
// 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 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)
|
if (m_tick_event->IsActive() && m_tick_event->GetInterval() == interval_ticks)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Ensure all pending ticks have been executed, since we won't get them back after rescheduling.
|
// Ensure all pending ticks have been executed, since we won't get them back after rescheduling.
|
||||||
m_tick_event->InvokeEarly(true);
|
m_tick_event->InvokeEarly(true);
|
||||||
m_tick_event->SetInterval(interval_ticks);
|
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)
|
void SPU::ExecuteTransfer(TickCount ticks)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "common/bitfield.h"
|
#include "common/bitfield.h"
|
||||||
#include "common/fifo_queue.h"
|
#include "common/fifo_queue.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "system.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ public:
|
|||||||
~SPU();
|
~SPU();
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
void CPUClockChanged();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
@ -54,7 +56,7 @@ private:
|
|||||||
static constexpr u32 VOICE_ADDRESS_SHIFT = 3;
|
static constexpr u32 VOICE_ADDRESS_SHIFT = 3;
|
||||||
static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28;
|
static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28;
|
||||||
static constexpr u32 SAMPLE_RATE = 44100;
|
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_MIN_VOLUME = 0;
|
||||||
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
|
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
|
||||||
static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400;
|
static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400;
|
||||||
@ -370,6 +372,8 @@ private:
|
|||||||
std::unique_ptr<TimingEvent> m_transfer_event;
|
std::unique_ptr<TimingEvent> m_transfer_event;
|
||||||
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
||||||
TickCount m_ticks_carry = 0;
|
TickCount m_ticks_carry = 0;
|
||||||
|
TickCount m_cpu_ticks_per_spu_tick = 0;
|
||||||
|
TickCount m_cpu_tick_divider = 0;
|
||||||
|
|
||||||
SPUCNT m_SPUCNT = {};
|
SPUCNT m_SPUCNT = {};
|
||||||
SPUSTAT m_SPUSTAT = {};
|
SPUSTAT m_SPUSTAT = {};
|
||||||
|
@ -68,6 +68,8 @@ static void UpdateRunningGame(const char* path, CDImage* image);
|
|||||||
static State s_state = State::Shutdown;
|
static State s_state = State::Shutdown;
|
||||||
|
|
||||||
static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
|
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_frame_number = 1;
|
||||||
static u32 s_internal_frame_number = 1;
|
static u32 s_internal_frame_number = 1;
|
||||||
|
|
||||||
@ -143,6 +145,22 @@ bool IsPALRegion()
|
|||||||
return s_region == ConsoleRegion::PAL;
|
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()
|
u32 GetFrameNumber()
|
||||||
{
|
{
|
||||||
return s_frame_number;
|
return s_frame_number;
|
||||||
@ -682,6 +700,8 @@ bool Boot(const SystemBootParameters& params)
|
|||||||
|
|
||||||
bool Initialize(bool force_software_renderer)
|
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_frame_number = 1;
|
||||||
s_internal_frame_number = 1;
|
s_internal_frame_number = 1;
|
||||||
|
|
||||||
@ -725,6 +745,15 @@ bool Initialize(bool force_software_renderer)
|
|||||||
g_mdec.Initialize();
|
g_mdec.Initialize();
|
||||||
g_sio.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();
|
UpdateThrottlePeriod();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -845,6 +874,31 @@ bool DoState(StateWrapper& sw)
|
|||||||
if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw))
|
if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw))
|
||||||
return false;
|
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();
|
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_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time;
|
||||||
s_last_internal_frame_number = s_internal_frame_number;
|
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) /
|
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;
|
100.0f;
|
||||||
s_last_global_tick_counter = global_tick_counter;
|
s_last_global_tick_counter = global_tick_counter;
|
||||||
s_fps_timer.Reset();
|
s_fps_timer.Reset();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/timer.h"
|
#include "common/timer.h"
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "timing_event.h"
|
#include "timing_event.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -40,6 +41,11 @@ enum : u32
|
|||||||
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
|
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
|
enum class State
|
||||||
{
|
{
|
||||||
Shutdown,
|
Shutdown,
|
||||||
@ -48,6 +54,8 @@ enum class State
|
|||||||
Paused
|
Paused
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern TickCount g_ticks_per_second;
|
||||||
|
|
||||||
/// Returns true if the filename is a PlayStation executable we can inject.
|
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||||
bool IsExeFileName(const char* path);
|
bool IsExeFileName(const char* path);
|
||||||
|
|
||||||
@ -80,6 +88,36 @@ bool IsValid();
|
|||||||
|
|
||||||
ConsoleRegion GetRegion();
|
ConsoleRegion GetRegion();
|
||||||
bool IsPALRegion();
|
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 GetFrameNumber();
|
||||||
u32 GetInternalFrameNumber();
|
u32 GetInternalFrameNumber();
|
||||||
void FrameDone();
|
void FrameDone();
|
||||||
|
@ -41,6 +41,7 @@ void Timers::Reset()
|
|||||||
cs.irq_done = false;
|
cs.irq_done = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_syclk_ticks_carry = 0;
|
||||||
m_sysclk_div_8_carry = 0;
|
m_sysclk_div_8_carry = 0;
|
||||||
UpdateSysClkEvent();
|
UpdateSysClkEvent();
|
||||||
}
|
}
|
||||||
@ -59,6 +60,7 @@ bool Timers::DoState(StateWrapper& sw)
|
|||||||
sw.Do(&cs.irq_done);
|
sw.Do(&cs.irq_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sw.Do(&m_syclk_ticks_carry);
|
||||||
sw.Do(&m_sysclk_div_8_carry);
|
sw.Do(&m_sysclk_div_8_carry);
|
||||||
|
|
||||||
if (sw.IsReading())
|
if (sw.IsReading())
|
||||||
@ -67,6 +69,11 @@ bool Timers::DoState(StateWrapper& sw)
|
|||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Timers::CPUClocksChanged()
|
||||||
|
{
|
||||||
|
m_syclk_ticks_carry = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void Timers::SetGate(u32 timer, bool state)
|
void Timers::SetGate(u32 timer, bool state)
|
||||||
{
|
{
|
||||||
CounterState& cs = m_states[timer];
|
CounterState& cs = m_states[timer];
|
||||||
@ -157,6 +164,8 @@ void Timers::AddTicks(u32 timer, TickCount count)
|
|||||||
|
|
||||||
void Timers::AddSysClkTicks(TickCount sysclk_ticks)
|
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)
|
if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled)
|
||||||
AddTicks(0, sysclk_ticks);
|
AddTicks(0, sysclk_ticks);
|
||||||
if (!m_states[1].external_counting_enabled && m_states[1].counting_enabled)
|
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));
|
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
|
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);
|
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.
|
// Still update once every 100ms. If we get polled we'll execute sooner.
|
||||||
const TickCount ticks = GetTicksUntilNextInterrupt();
|
const TickCount ticks = GetTicksUntilNextInterrupt();
|
||||||
if (ticks == std::numeric_limits<TickCount>::max())
|
if (ticks == std::numeric_limits<TickCount>::max())
|
||||||
m_sysclk_event->Schedule(MAX_SLICE_SIZE);
|
m_sysclk_event->Schedule(System::GetMaxSliceTicks());
|
||||||
else
|
else
|
||||||
m_sysclk_event->Schedule(ticks);
|
m_sysclk_event->Schedule(ticks);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ public:
|
|||||||
|
|
||||||
void DrawDebugStateWindow();
|
void DrawDebugStateWindow();
|
||||||
|
|
||||||
|
void CPUClocksChanged();
|
||||||
|
|
||||||
// dot clock/hblank/sysclk div 8
|
// dot clock/hblank/sysclk div 8
|
||||||
ALWAYS_INLINE bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
ALWAYS_INLINE bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ private:
|
|||||||
std::unique_ptr<TimingEvent> m_sysclk_event;
|
std::unique_ptr<TimingEvent> m_sysclk_event;
|
||||||
|
|
||||||
std::array<CounterState, NUM_TIMERS> m_states{};
|
std::array<CounterState, NUM_TIMERS> m_states{};
|
||||||
|
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
|
u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,9 +19,6 @@ enum class MemoryAccessSize : u32
|
|||||||
|
|
||||||
using TickCount = s32;
|
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
|
enum class ConsoleRegion
|
||||||
{
|
{
|
||||||
Auto,
|
Auto,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user