mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-04-27 07:25:42 -04:00
Timers: Convert to namespace
This commit is contained in:
parent
3acf569d67
commit
db364d0e95
@ -1249,13 +1249,13 @@ ALWAYS_INLINE static TickCount DoAccessTimers(u32 offset, u32& value)
|
|||||||
{
|
{
|
||||||
if constexpr (type == MemoryAccessType::Read)
|
if constexpr (type == MemoryAccessType::Read)
|
||||||
{
|
{
|
||||||
value = g_timers.ReadRegister(FIXUP_WORD_OFFSET(size, offset));
|
value = Timers::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
|
||||||
value = FIXUP_WORD_READ_VALUE(size, offset, value);
|
value = FIXUP_WORD_READ_VALUE(size, offset, value);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
g_timers.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
|
Timers::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -779,7 +779,7 @@ void GPU::UpdateCRTCTickEvent()
|
|||||||
{
|
{
|
||||||
// figure out how many GPU ticks until the next vblank or event
|
// figure out how many GPU ticks until the next vblank or event
|
||||||
TickCount lines_until_event;
|
TickCount lines_until_event;
|
||||||
if (g_timers.IsSyncEnabled(HBLANK_TIMER_INDEX))
|
if (Timers::IsSyncEnabled(HBLANK_TIMER_INDEX))
|
||||||
{
|
{
|
||||||
// when the timer sync is enabled we need to sync at vblank start and end
|
// when the timer sync is enabled we need to sync at vblank start and end
|
||||||
lines_until_event =
|
lines_until_event =
|
||||||
@ -794,14 +794,14 @@ void GPU::UpdateCRTCTickEvent()
|
|||||||
(m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) :
|
(m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) :
|
||||||
(m_crtc_state.vertical_display_end - m_crtc_state.current_scanline));
|
(m_crtc_state.vertical_display_end - m_crtc_state.current_scanline));
|
||||||
}
|
}
|
||||||
if (g_timers.IsExternalIRQEnabled(HBLANK_TIMER_INDEX))
|
if (Timers::IsExternalIRQEnabled(HBLANK_TIMER_INDEX))
|
||||||
lines_until_event = std::min(lines_until_event, g_timers.GetTicksUntilIRQ(HBLANK_TIMER_INDEX));
|
lines_until_event = std::min(lines_until_event, Timers::GetTicksUntilIRQ(HBLANK_TIMER_INDEX));
|
||||||
|
|
||||||
TickCount ticks_until_event =
|
TickCount ticks_until_event =
|
||||||
lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
|
lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
|
||||||
if (g_timers.IsExternalIRQEnabled(DOT_TIMER_INDEX))
|
if (Timers::IsExternalIRQEnabled(DOT_TIMER_INDEX))
|
||||||
{
|
{
|
||||||
const TickCount dots_until_irq = g_timers.GetTicksUntilIRQ(DOT_TIMER_INDEX);
|
const TickCount dots_until_irq = Timers::GetTicksUntilIRQ(DOT_TIMER_INDEX);
|
||||||
const TickCount ticks_until_irq =
|
const TickCount ticks_until_irq =
|
||||||
(dots_until_irq * m_crtc_state.dot_clock_divider) - m_crtc_state.fractional_dot_ticks;
|
(dots_until_irq * m_crtc_state.dot_clock_divider) - m_crtc_state.fractional_dot_ticks;
|
||||||
ticks_until_event = std::min(ticks_until_event, std::max<TickCount>(ticks_until_irq, 0));
|
ticks_until_event = std::min(ticks_until_event, std::max<TickCount>(ticks_until_irq, 0));
|
||||||
@ -835,13 +835,13 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||||||
const TickCount gpu_ticks = SystemTicksToCRTCTicks(ticks, &m_crtc_state.fractional_ticks);
|
const TickCount gpu_ticks = SystemTicksToCRTCTicks(ticks, &m_crtc_state.fractional_ticks);
|
||||||
m_crtc_state.current_tick_in_scanline += gpu_ticks;
|
m_crtc_state.current_tick_in_scanline += gpu_ticks;
|
||||||
|
|
||||||
if (g_timers.IsUsingExternalClock(DOT_TIMER_INDEX))
|
if (Timers::IsUsingExternalClock(DOT_TIMER_INDEX))
|
||||||
{
|
{
|
||||||
m_crtc_state.fractional_dot_ticks += gpu_ticks;
|
m_crtc_state.fractional_dot_ticks += gpu_ticks;
|
||||||
const TickCount dots = m_crtc_state.fractional_dot_ticks / m_crtc_state.dot_clock_divider;
|
const TickCount dots = m_crtc_state.fractional_dot_ticks / m_crtc_state.dot_clock_divider;
|
||||||
m_crtc_state.fractional_dot_ticks = m_crtc_state.fractional_dot_ticks % m_crtc_state.dot_clock_divider;
|
m_crtc_state.fractional_dot_ticks = m_crtc_state.fractional_dot_ticks % m_crtc_state.dot_clock_divider;
|
||||||
if (dots > 0)
|
if (dots > 0)
|
||||||
g_timers.AddTicks(DOT_TIMER_INDEX, dots);
|
Timers::AddTicks(DOT_TIMER_INDEX, dots);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,8 +851,8 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||||||
const bool old_hblank = m_crtc_state.in_hblank;
|
const bool old_hblank = m_crtc_state.in_hblank;
|
||||||
const bool new_hblank = (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_sync_start);
|
const bool new_hblank = (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_sync_start);
|
||||||
m_crtc_state.in_hblank = new_hblank;
|
m_crtc_state.in_hblank = new_hblank;
|
||||||
if (!old_hblank && new_hblank && g_timers.IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
if (!old_hblank && new_hblank && Timers::IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||||
g_timers.AddTicks(HBLANK_TIMER_INDEX, 1);
|
Timers::AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||||
|
|
||||||
UpdateCRTCTickEvent();
|
UpdateCRTCTickEvent();
|
||||||
return;
|
return;
|
||||||
@ -868,10 +868,10 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||||||
const bool old_hblank = m_crtc_state.in_hblank;
|
const bool old_hblank = m_crtc_state.in_hblank;
|
||||||
const bool new_hblank = (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_sync_start);
|
const bool new_hblank = (m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_sync_start);
|
||||||
m_crtc_state.in_hblank = new_hblank;
|
m_crtc_state.in_hblank = new_hblank;
|
||||||
if (g_timers.IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
if (Timers::IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||||
{
|
{
|
||||||
const u32 hblank_timer_ticks = BoolToUInt32(!old_hblank) + BoolToUInt32(new_hblank) + (lines_to_draw - 1);
|
const u32 hblank_timer_ticks = BoolToUInt32(!old_hblank) + BoolToUInt32(new_hblank) + (lines_to_draw - 1);
|
||||||
g_timers.AddTicks(HBLANK_TIMER_INDEX, static_cast<TickCount>(hblank_timer_ticks));
|
Timers::AddTicks(HBLANK_TIMER_INDEX, static_cast<TickCount>(hblank_timer_ticks));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (lines_to_draw > 0)
|
while (lines_to_draw > 0)
|
||||||
@ -887,7 +887,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||||||
if (prev_scanline < m_crtc_state.vertical_display_start &&
|
if (prev_scanline < m_crtc_state.vertical_display_start &&
|
||||||
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end)
|
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end)
|
||||||
{
|
{
|
||||||
g_timers.SetGate(HBLANK_TIMER_INDEX, false);
|
Timers::SetGate(HBLANK_TIMER_INDEX, false);
|
||||||
m_crtc_state.in_vblank = false;
|
m_crtc_state.in_vblank = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,7 +912,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||||||
m_crtc_state.interlaced_display_field = 0;
|
m_crtc_state.interlaced_display_field = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_timers.SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
Timers::SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||||
m_crtc_state.in_vblank = new_vblank;
|
m_crtc_state.in_vblank = new_vblank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ class HostDisplay;
|
|||||||
class GPUTexture;
|
class GPUTexture;
|
||||||
|
|
||||||
class TimingEvent;
|
class TimingEvent;
|
||||||
class Timers;
|
|
||||||
|
|
||||||
namespace Threading
|
namespace Threading
|
||||||
{
|
{
|
||||||
|
@ -283,7 +283,7 @@ void System::UpdateOverclock()
|
|||||||
SPU::CPUClockChanged();
|
SPU::CPUClockChanged();
|
||||||
CDROM::CPUClockChanged();
|
CDROM::CPUClockChanged();
|
||||||
g_gpu->CPUClockChanged();
|
g_gpu->CPUClockChanged();
|
||||||
g_timers.CPUClocksChanged();
|
Timers::CPUClocksChanged();
|
||||||
UpdateThrottlePeriod();
|
UpdateThrottlePeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1391,7 +1391,7 @@ bool System::Initialize(bool force_software_renderer)
|
|||||||
|
|
||||||
CDROM::Initialize();
|
CDROM::Initialize();
|
||||||
g_pad.Initialize();
|
g_pad.Initialize();
|
||||||
g_timers.Initialize();
|
Timers::Initialize();
|
||||||
SPU::Initialize();
|
SPU::Initialize();
|
||||||
MDEC::Initialize();
|
MDEC::Initialize();
|
||||||
g_sio.Initialize();
|
g_sio.Initialize();
|
||||||
@ -1453,7 +1453,7 @@ void System::DestroySystem()
|
|||||||
g_sio.Shutdown();
|
g_sio.Shutdown();
|
||||||
MDEC::Shutdown();
|
MDEC::Shutdown();
|
||||||
SPU::Shutdown();
|
SPU::Shutdown();
|
||||||
g_timers.Shutdown();
|
Timers::Shutdown();
|
||||||
g_pad.Shutdown();
|
g_pad.Shutdown();
|
||||||
CDROM::Shutdown();
|
CDROM::Shutdown();
|
||||||
g_gpu.reset();
|
g_gpu.reset();
|
||||||
@ -1658,7 +1658,7 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
|||||||
if (!sw.DoMarker("Pad") || !g_pad.DoState(sw))
|
if (!sw.DoMarker("Pad") || !g_pad.DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!sw.DoMarker("Timers") || !g_timers.DoState(sw))
|
if (!sw.DoMarker("Timers") || !Timers::DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!sw.DoMarker("SPU") || !SPU::DoState(sw))
|
if (!sw.DoMarker("SPU") || !SPU::DoState(sw))
|
||||||
@ -1744,7 +1744,7 @@ void System::InternalReset()
|
|||||||
g_gpu->Reset(true);
|
g_gpu->Reset(true);
|
||||||
CDROM::Reset();
|
CDROM::Reset();
|
||||||
g_pad.Reset();
|
g_pad.Reset();
|
||||||
g_timers.Reset();
|
Timers::Reset();
|
||||||
SPU::Reset();
|
SPU::Reset();
|
||||||
MDEC::Reset();
|
MDEC::Reset();
|
||||||
g_sio.Reset();
|
g_sio.Reset();
|
||||||
|
@ -2,37 +2,88 @@
|
|||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
|
#include "common/bitfield.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
Log_SetChannel(Timers);
|
Log_SetChannel(Timers);
|
||||||
|
|
||||||
Timers g_timers;
|
namespace Timers {
|
||||||
|
static constexpr u32 NUM_TIMERS = 3;
|
||||||
|
|
||||||
Timers::Timers() = default;
|
enum class SyncMode : u8
|
||||||
|
{
|
||||||
|
PauseOnGate = 0,
|
||||||
|
ResetOnGate = 1,
|
||||||
|
ResetAndRunOnGate = 2,
|
||||||
|
FreeRunOnGate = 3
|
||||||
|
};
|
||||||
|
|
||||||
Timers::~Timers() = default;
|
union CounterMode
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u32, bool, 0, 1> sync_enable;
|
||||||
|
BitField<u32, SyncMode, 1, 2> sync_mode;
|
||||||
|
BitField<u32, bool, 3, 1> reset_at_target;
|
||||||
|
BitField<u32, bool, 4, 1> irq_at_target;
|
||||||
|
BitField<u32, bool, 5, 1> irq_on_overflow;
|
||||||
|
BitField<u32, bool, 6, 1> irq_repeat;
|
||||||
|
BitField<u32, bool, 7, 1> irq_pulse_n;
|
||||||
|
BitField<u32, u8, 8, 2> clock_source;
|
||||||
|
BitField<u32, bool, 10, 1> interrupt_request_n;
|
||||||
|
BitField<u32, bool, 11, 1> reached_target;
|
||||||
|
BitField<u32, bool, 12, 1> reached_overflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CounterState
|
||||||
|
{
|
||||||
|
CounterMode mode;
|
||||||
|
u32 counter;
|
||||||
|
u32 target;
|
||||||
|
bool gate;
|
||||||
|
bool use_external_clock;
|
||||||
|
bool external_counting_enabled;
|
||||||
|
bool counting_enabled;
|
||||||
|
bool irq_done;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void UpdateCountingEnabled(CounterState& cs);
|
||||||
|
static void CheckForIRQ(u32 index, u32 old_counter);
|
||||||
|
static void UpdateIRQ(u32 index);
|
||||||
|
|
||||||
|
static void AddSysClkTicks(void*, TickCount sysclk_ticks, TickCount ticks_late);
|
||||||
|
|
||||||
|
static TickCount GetTicksUntilNextInterrupt();
|
||||||
|
static void UpdateSysClkEvent();
|
||||||
|
|
||||||
|
static std::unique_ptr<TimingEvent> s_sysclk_event;
|
||||||
|
|
||||||
|
static std::array<CounterState, NUM_TIMERS> s_states{};
|
||||||
|
static TickCount s_syclk_ticks_carry = 0; // 0 unless overclocking is enabled
|
||||||
|
static u32 s_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
|
||||||
|
}; // namespace Timers
|
||||||
|
|
||||||
void Timers::Initialize()
|
void Timers::Initialize()
|
||||||
{
|
{
|
||||||
m_sysclk_event = TimingEvents::CreateTimingEvent(
|
s_sysclk_event =
|
||||||
"Timer SysClk Interrupt", 1, 1,
|
TimingEvents::CreateTimingEvent("Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr, false);
|
||||||
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<Timers*>(param)->AddSysClkTicks(ticks); },
|
|
||||||
this, false);
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timers::Shutdown()
|
void Timers::Shutdown()
|
||||||
{
|
{
|
||||||
m_sysclk_event.reset();
|
s_sysclk_event.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timers::Reset()
|
void Timers::Reset()
|
||||||
{
|
{
|
||||||
for (CounterState& cs : m_states)
|
for (CounterState& cs : s_states)
|
||||||
{
|
{
|
||||||
cs.mode.bits = 0;
|
cs.mode.bits = 0;
|
||||||
cs.mode.interrupt_request_n = true;
|
cs.mode.interrupt_request_n = true;
|
||||||
@ -44,14 +95,14 @@ void Timers::Reset()
|
|||||||
cs.irq_done = false;
|
cs.irq_done = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_syclk_ticks_carry = 0;
|
s_syclk_ticks_carry = 0;
|
||||||
m_sysclk_div_8_carry = 0;
|
s_sysclk_div_8_carry = 0;
|
||||||
UpdateSysClkEvent();
|
UpdateSysClkEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Timers::DoState(StateWrapper& sw)
|
bool Timers::DoState(StateWrapper& sw)
|
||||||
{
|
{
|
||||||
for (CounterState& cs : m_states)
|
for (CounterState& cs : s_states)
|
||||||
{
|
{
|
||||||
sw.Do(&cs.mode.bits);
|
sw.Do(&cs.mode.bits);
|
||||||
sw.Do(&cs.counter);
|
sw.Do(&cs.counter);
|
||||||
@ -63,8 +114,8 @@ bool Timers::DoState(StateWrapper& sw)
|
|||||||
sw.Do(&cs.irq_done);
|
sw.Do(&cs.irq_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Do(&m_syclk_ticks_carry);
|
sw.Do(&s_syclk_ticks_carry);
|
||||||
sw.Do(&m_sysclk_div_8_carry);
|
sw.Do(&s_sysclk_div_8_carry);
|
||||||
|
|
||||||
if (sw.IsReading())
|
if (sw.IsReading())
|
||||||
UpdateSysClkEvent();
|
UpdateSysClkEvent();
|
||||||
@ -74,12 +125,28 @@ bool Timers::DoState(StateWrapper& sw)
|
|||||||
|
|
||||||
void Timers::CPUClocksChanged()
|
void Timers::CPUClocksChanged()
|
||||||
{
|
{
|
||||||
m_syclk_ticks_carry = 0;
|
s_syclk_ticks_carry = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timers::IsUsingExternalClock(u32 timer)
|
||||||
|
{
|
||||||
|
return s_states[timer].external_counting_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timers::IsSyncEnabled(u32 timer)
|
||||||
|
{
|
||||||
|
return s_states[timer].mode.sync_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timers::IsExternalIRQEnabled(u32 timer)
|
||||||
|
{
|
||||||
|
const CounterState& cs = s_states[timer];
|
||||||
|
return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timers::SetGate(u32 timer, bool state)
|
void Timers::SetGate(u32 timer, bool state)
|
||||||
{
|
{
|
||||||
CounterState& cs = m_states[timer];
|
CounterState& cs = s_states[timer];
|
||||||
if (cs.gate == state)
|
if (cs.gate == state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -89,7 +156,7 @@ void Timers::SetGate(u32 timer, bool state)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (cs.counting_enabled && !cs.use_external_clock)
|
if (cs.counting_enabled && !cs.use_external_clock)
|
||||||
m_sysclk_event->InvokeEarly();
|
s_sysclk_event->InvokeEarly();
|
||||||
|
|
||||||
if (state)
|
if (state)
|
||||||
{
|
{
|
||||||
@ -110,9 +177,9 @@ void Timers::SetGate(u32 timer, bool state)
|
|||||||
UpdateSysClkEvent();
|
UpdateSysClkEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
TickCount Timers::GetTicksUntilIRQ(u32 timer) const
|
TickCount Timers::GetTicksUntilIRQ(u32 timer)
|
||||||
{
|
{
|
||||||
const CounterState& cs = m_states[timer];
|
const CounterState& cs = s_states[timer];
|
||||||
if (!cs.counting_enabled)
|
if (!cs.counting_enabled)
|
||||||
return std::numeric_limits<TickCount>::max();
|
return std::numeric_limits<TickCount>::max();
|
||||||
|
|
||||||
@ -127,7 +194,7 @@ TickCount Timers::GetTicksUntilIRQ(u32 timer) const
|
|||||||
|
|
||||||
void Timers::AddTicks(u32 timer, TickCount count)
|
void Timers::AddTicks(u32 timer, TickCount count)
|
||||||
{
|
{
|
||||||
CounterState& cs = m_states[timer];
|
CounterState& cs = s_states[timer];
|
||||||
const u32 old_counter = cs.counter;
|
const u32 old_counter = cs.counter;
|
||||||
cs.counter += static_cast<u32>(count);
|
cs.counter += static_cast<u32>(count);
|
||||||
CheckForIRQ(timer, old_counter);
|
CheckForIRQ(timer, old_counter);
|
||||||
@ -135,7 +202,7 @@ void Timers::AddTicks(u32 timer, TickCount count)
|
|||||||
|
|
||||||
void Timers::CheckForIRQ(u32 timer, u32 old_counter)
|
void Timers::CheckForIRQ(u32 timer, u32 old_counter)
|
||||||
{
|
{
|
||||||
CounterState& cs = m_states[timer];
|
CounterState& cs = s_states[timer];
|
||||||
|
|
||||||
bool interrupt_request = false;
|
bool interrupt_request = false;
|
||||||
if (cs.counter >= cs.target && (old_counter < cs.target || cs.target == 0))
|
if (cs.counter >= cs.target && (old_counter < cs.target || cs.target == 0))
|
||||||
@ -170,21 +237,21 @@ void Timers::CheckForIRQ(u32 timer, u32 old_counter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timers::AddSysClkTicks(TickCount sysclk_ticks)
|
void Timers::AddSysClkTicks(void*, TickCount sysclk_ticks, TickCount ticks_late)
|
||||||
{
|
{
|
||||||
sysclk_ticks = System::UnscaleTicksToOverclock(sysclk_ticks, &m_syclk_ticks_carry);
|
sysclk_ticks = System::UnscaleTicksToOverclock(sysclk_ticks, &s_syclk_ticks_carry);
|
||||||
|
|
||||||
if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled)
|
if (!s_states[0].external_counting_enabled && s_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 (!s_states[1].external_counting_enabled && s_states[1].counting_enabled)
|
||||||
AddTicks(1, sysclk_ticks);
|
AddTicks(1, sysclk_ticks);
|
||||||
if (m_states[2].external_counting_enabled)
|
if (s_states[2].external_counting_enabled)
|
||||||
{
|
{
|
||||||
TickCount sysclk_div_8_ticks = (sysclk_ticks + m_sysclk_div_8_carry) / 8;
|
TickCount sysclk_div_8_ticks = (sysclk_ticks + s_sysclk_div_8_carry) / 8;
|
||||||
m_sysclk_div_8_carry = (sysclk_ticks + m_sysclk_div_8_carry) % 8;
|
s_sysclk_div_8_carry = (sysclk_ticks + s_sysclk_div_8_carry) % 8;
|
||||||
AddTicks(2, sysclk_div_8_ticks);
|
AddTicks(2, sysclk_div_8_ticks);
|
||||||
}
|
}
|
||||||
else if (m_states[2].counting_enabled)
|
else if (s_states[2].counting_enabled)
|
||||||
{
|
{
|
||||||
AddTicks(2, sysclk_ticks);
|
AddTicks(2, sysclk_ticks);
|
||||||
}
|
}
|
||||||
@ -202,7 +269,7 @@ u32 Timers::ReadRegister(u32 offset)
|
|||||||
return UINT32_C(0xFFFFFFFF);
|
return UINT32_C(0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
CounterState& cs = m_states[timer_index];
|
CounterState& cs = s_states[timer_index];
|
||||||
|
|
||||||
switch (port_offset)
|
switch (port_offset)
|
||||||
{
|
{
|
||||||
@ -215,7 +282,7 @@ u32 Timers::ReadRegister(u32 offset)
|
|||||||
g_gpu->SynchronizeCRTC();
|
g_gpu->SynchronizeCRTC();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sysclk_event->InvokeEarly();
|
s_sysclk_event->InvokeEarly();
|
||||||
|
|
||||||
return cs.counter;
|
return cs.counter;
|
||||||
}
|
}
|
||||||
@ -229,7 +296,7 @@ u32 Timers::ReadRegister(u32 offset)
|
|||||||
g_gpu->SynchronizeCRTC();
|
g_gpu->SynchronizeCRTC();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sysclk_event->InvokeEarly();
|
s_sysclk_event->InvokeEarly();
|
||||||
|
|
||||||
const u32 bits = cs.mode.bits;
|
const u32 bits = cs.mode.bits;
|
||||||
cs.mode.reached_overflow = false;
|
cs.mode.reached_overflow = false;
|
||||||
@ -256,7 +323,7 @@ void Timers::WriteRegister(u32 offset, u32 value)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CounterState& cs = m_states[timer_index];
|
CounterState& cs = s_states[timer_index];
|
||||||
|
|
||||||
if (timer_index < 2 && cs.external_counting_enabled)
|
if (timer_index < 2 && cs.external_counting_enabled)
|
||||||
{
|
{
|
||||||
@ -265,7 +332,7 @@ void Timers::WriteRegister(u32 offset, u32 value)
|
|||||||
g_gpu->SynchronizeCRTC();
|
g_gpu->SynchronizeCRTC();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sysclk_event->InvokeEarly();
|
s_sysclk_event->InvokeEarly();
|
||||||
|
|
||||||
// Strictly speaking these IRQ checks should probably happen on the next tick.
|
// Strictly speaking these IRQ checks should probably happen on the next tick.
|
||||||
switch (port_offset)
|
switch (port_offset)
|
||||||
@ -344,7 +411,7 @@ void Timers::UpdateCountingEnabled(CounterState& cs)
|
|||||||
|
|
||||||
void Timers::UpdateIRQ(u32 index)
|
void Timers::UpdateIRQ(u32 index)
|
||||||
{
|
{
|
||||||
CounterState& cs = m_states[index];
|
CounterState& cs = s_states[index];
|
||||||
if (cs.mode.interrupt_request_n || (!cs.mode.irq_repeat && cs.irq_done))
|
if (cs.mode.interrupt_request_n || (!cs.mode.irq_repeat && cs.irq_done))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -354,12 +421,12 @@ void Timers::UpdateIRQ(u32 index)
|
|||||||
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + index));
|
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + index));
|
||||||
}
|
}
|
||||||
|
|
||||||
TickCount Timers::GetTicksUntilNextInterrupt() const
|
TickCount Timers::GetTicksUntilNextInterrupt()
|
||||||
{
|
{
|
||||||
TickCount min_ticks = System::GetMaxSliceTicks();
|
TickCount min_ticks = System::GetMaxSliceTicks();
|
||||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||||
{
|
{
|
||||||
const CounterState& cs = m_states[i];
|
const CounterState& cs = s_states[i];
|
||||||
if (!cs.counting_enabled || (i < 2 && cs.external_counting_enabled) ||
|
if (!cs.counting_enabled || (i < 2 && cs.external_counting_enabled) ||
|
||||||
(!cs.mode.irq_at_target && !cs.mode.irq_on_overflow && (cs.mode.irq_repeat || !cs.irq_done)))
|
(!cs.mode.irq_at_target && !cs.mode.irq_on_overflow && (cs.mode.irq_repeat || !cs.irq_done)))
|
||||||
{
|
{
|
||||||
@ -390,7 +457,7 @@ TickCount Timers::GetTicksUntilNextInterrupt() const
|
|||||||
|
|
||||||
void Timers::UpdateSysClkEvent()
|
void Timers::UpdateSysClkEvent()
|
||||||
{
|
{
|
||||||
m_sysclk_event->Schedule(GetTicksUntilNextInterrupt());
|
s_sysclk_event->Schedule(GetTicksUntilNextInterrupt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timers::DrawDebugStateWindow()
|
void Timers::DrawDebugStateWindow()
|
||||||
@ -434,7 +501,7 @@ void Timers::DrawDebugStateWindow()
|
|||||||
|
|
||||||
for (u32 i = 0; i < NUM_TIMERS; i++)
|
for (u32 i = 0; i < NUM_TIMERS; i++)
|
||||||
{
|
{
|
||||||
const CounterState& cs = m_states[i];
|
const CounterState& cs = s_states[i];
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||||
cs.counting_enabled ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
|
cs.counting_enabled ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
ImGui::Text("%u", i);
|
ImGui::Text("%u", i);
|
||||||
|
@ -2,105 +2,35 @@
|
|||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/bitfield.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
class TimingEvent;
|
namespace Timers {
|
||||||
class GPU;
|
|
||||||
|
|
||||||
class Timers final
|
void Initialize();
|
||||||
{
|
void Shutdown();
|
||||||
public:
|
void Reset();
|
||||||
Timers();
|
bool DoState(StateWrapper& sw);
|
||||||
~Timers();
|
|
||||||
|
|
||||||
void Initialize();
|
void SetGate(u32 timer, bool state);
|
||||||
void Shutdown();
|
|
||||||
void Reset();
|
|
||||||
bool DoState(StateWrapper& sw);
|
|
||||||
|
|
||||||
void SetGate(u32 timer, bool state);
|
void DrawDebugStateWindow();
|
||||||
|
|
||||||
void DrawDebugStateWindow();
|
void CPUClocksChanged();
|
||||||
|
|
||||||
void CPUClocksChanged();
|
// dot clock/hblank/sysclk div 8
|
||||||
|
bool IsUsingExternalClock(u32 timer);
|
||||||
|
bool IsSyncEnabled(u32 timer);
|
||||||
|
|
||||||
// dot clock/hblank/sysclk div 8
|
// queries for GPU
|
||||||
ALWAYS_INLINE bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
bool IsExternalIRQEnabled(u32 timer);
|
||||||
ALWAYS_INLINE bool IsSyncEnabled(u32 timer) const { return m_states[timer].mode.sync_enable; }
|
|
||||||
|
|
||||||
// queries for GPU
|
TickCount GetTicksUntilIRQ(u32 timer);
|
||||||
ALWAYS_INLINE bool IsExternalIRQEnabled(u32 timer) const
|
|
||||||
{
|
|
||||||
const CounterState& cs = m_states[timer];
|
|
||||||
return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TickCount GetTicksUntilIRQ(u32 timer) const;
|
void AddTicks(u32 timer, TickCount ticks);
|
||||||
|
|
||||||
void AddTicks(u32 timer, TickCount ticks);
|
u32 ReadRegister(u32 offset);
|
||||||
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
|
||||||
u32 ReadRegister(u32 offset);
|
} // namespace Timers
|
||||||
void WriteRegister(u32 offset, u32 value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr u32 NUM_TIMERS = 3;
|
|
||||||
|
|
||||||
enum class SyncMode : u8
|
|
||||||
{
|
|
||||||
PauseOnGate = 0,
|
|
||||||
ResetOnGate = 1,
|
|
||||||
ResetAndRunOnGate = 2,
|
|
||||||
FreeRunOnGate = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
union CounterMode
|
|
||||||
{
|
|
||||||
u32 bits;
|
|
||||||
|
|
||||||
BitField<u32, bool, 0, 1> sync_enable;
|
|
||||||
BitField<u32, SyncMode, 1, 2> sync_mode;
|
|
||||||
BitField<u32, bool, 3, 1> reset_at_target;
|
|
||||||
BitField<u32, bool, 4, 1> irq_at_target;
|
|
||||||
BitField<u32, bool, 5, 1> irq_on_overflow;
|
|
||||||
BitField<u32, bool, 6, 1> irq_repeat;
|
|
||||||
BitField<u32, bool, 7, 1> irq_pulse_n;
|
|
||||||
BitField<u32, u8, 8, 2> clock_source;
|
|
||||||
BitField<u32, bool, 10, 1> interrupt_request_n;
|
|
||||||
BitField<u32, bool, 11, 1> reached_target;
|
|
||||||
BitField<u32, bool, 12, 1> reached_overflow;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CounterState
|
|
||||||
{
|
|
||||||
CounterMode mode;
|
|
||||||
u32 counter;
|
|
||||||
u32 target;
|
|
||||||
bool gate;
|
|
||||||
bool use_external_clock;
|
|
||||||
bool external_counting_enabled;
|
|
||||||
bool counting_enabled;
|
|
||||||
bool irq_done;
|
|
||||||
};
|
|
||||||
|
|
||||||
void UpdateCountingEnabled(CounterState& cs);
|
|
||||||
void CheckForIRQ(u32 index, u32 old_counter);
|
|
||||||
void UpdateIRQ(u32 index);
|
|
||||||
|
|
||||||
void AddSysClkTicks(TickCount sysclk_ticks);
|
|
||||||
|
|
||||||
TickCount GetTicksUntilNextInterrupt() const;
|
|
||||||
void UpdateSysClkEvent();
|
|
||||||
|
|
||||||
std::unique_ptr<TimingEvent> m_sysclk_event;
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Timers g_timers;
|
|
||||||
|
@ -484,7 +484,7 @@ void ImGuiManager::RenderDebugWindows()
|
|||||||
if (g_settings.debugging.show_cdrom_state)
|
if (g_settings.debugging.show_cdrom_state)
|
||||||
CDROM::DrawDebugWindow();
|
CDROM::DrawDebugWindow();
|
||||||
if (g_settings.debugging.show_timers_state)
|
if (g_settings.debugging.show_timers_state)
|
||||||
g_timers.DrawDebugStateWindow();
|
Timers::DrawDebugStateWindow();
|
||||||
if (g_settings.debugging.show_spu_state)
|
if (g_settings.debugging.show_spu_state)
|
||||||
SPU::DrawDebugStateWindow();
|
SPU::DrawDebugStateWindow();
|
||||||
if (g_settings.debugging.show_mdec_state)
|
if (g_settings.debugging.show_mdec_state)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user