2019-09-20 13:40:19 +00:00
|
|
|
#include "timers.h"
|
2020-01-10 03:31:12 +00:00
|
|
|
#include "common/log.h"
|
2019-09-20 13:40:19 +00:00
|
|
|
#include "common/state_wrapper.h"
|
2020-01-24 04:53:40 +00:00
|
|
|
#include "gpu.h"
|
2019-09-20 13:40:19 +00:00
|
|
|
#include "interrupt_controller.h"
|
2019-09-20 13:59:48 +00:00
|
|
|
#include "system.h"
|
2020-09-01 02:13:56 +00:00
|
|
|
#ifdef WITH_IMGUI
|
|
|
|
#include "imgui.h"
|
|
|
|
#endif
|
2019-09-20 13:40:19 +00:00
|
|
|
Log_SetChannel(Timers);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
Timers g_timers;
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
Timers::Timers() = default;
|
|
|
|
|
|
|
|
Timers::~Timers() = default;
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void Timers::Initialize()
|
|
|
|
{
|
|
|
|
m_sysclk_event = TimingEvents::CreateTimingEvent(
|
2021-01-09 15:43:59 +00:00
|
|
|
"Timer SysClk Interrupt", 1, 1,
|
|
|
|
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<Timers*>(param)->AddSysClkTicks(ticks); },
|
|
|
|
this, false);
|
2020-07-31 07:09:18 +00:00
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Timers::Shutdown()
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
m_sysclk_event.reset();
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Timers::Reset()
|
|
|
|
{
|
|
|
|
for (CounterState& cs : m_states)
|
|
|
|
{
|
|
|
|
cs.mode.bits = 0;
|
2020-04-25 04:17:44 +00:00
|
|
|
cs.mode.interrupt_request_n = true;
|
2019-09-20 13:40:19 +00:00
|
|
|
cs.counter = 0;
|
|
|
|
cs.target = 0;
|
|
|
|
cs.gate = false;
|
|
|
|
cs.external_counting_enabled = false;
|
|
|
|
cs.counting_enabled = true;
|
2019-10-08 08:21:15 +00:00
|
|
|
cs.irq_done = false;
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
2019-10-08 08:21:15 +00:00
|
|
|
|
2020-09-29 13:29:28 +00:00
|
|
|
m_syclk_ticks_carry = 0;
|
2019-10-08 08:21:15 +00:00
|
|
|
m_sysclk_div_8_carry = 0;
|
2020-01-24 04:53:40 +00:00
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Timers::DoState(StateWrapper& sw)
|
|
|
|
{
|
|
|
|
for (CounterState& cs : m_states)
|
|
|
|
{
|
|
|
|
sw.Do(&cs.mode.bits);
|
|
|
|
sw.Do(&cs.counter);
|
|
|
|
sw.Do(&cs.target);
|
|
|
|
sw.Do(&cs.gate);
|
2019-09-26 14:03:28 +00:00
|
|
|
sw.Do(&cs.use_external_clock);
|
2019-09-20 13:40:19 +00:00
|
|
|
sw.Do(&cs.external_counting_enabled);
|
|
|
|
sw.Do(&cs.counting_enabled);
|
2019-10-08 08:21:15 +00:00
|
|
|
sw.Do(&cs.irq_done);
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
|
2020-09-29 13:29:28 +00:00
|
|
|
sw.Do(&m_syclk_ticks_carry);
|
2019-10-08 08:21:15 +00:00
|
|
|
sw.Do(&m_sysclk_div_8_carry);
|
2020-01-24 04:53:40 +00:00
|
|
|
|
|
|
|
if (sw.IsReading())
|
|
|
|
UpdateSysClkEvent();
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
return !sw.HasError();
|
|
|
|
}
|
|
|
|
|
2020-09-29 13:29:28 +00:00
|
|
|
void Timers::CPUClocksChanged()
|
|
|
|
{
|
|
|
|
m_syclk_ticks_carry = 0;
|
|
|
|
}
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
void Timers::SetGate(u32 timer, bool state)
|
|
|
|
{
|
|
|
|
CounterState& cs = m_states[timer];
|
|
|
|
if (cs.gate == state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cs.gate = state;
|
|
|
|
|
2020-12-05 09:31:43 +00:00
|
|
|
if (!cs.mode.sync_enable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cs.counting_enabled && !cs.use_external_clock)
|
|
|
|
m_sysclk_event->InvokeEarly();
|
|
|
|
|
|
|
|
if (state)
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
2020-12-05 09:31:43 +00:00
|
|
|
switch (cs.mode.sync_mode)
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
2020-12-05 09:31:43 +00:00
|
|
|
case SyncMode::ResetOnGate:
|
|
|
|
case SyncMode::ResetAndRunOnGate:
|
|
|
|
cs.counter = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyncMode::FreeRunOnGate:
|
|
|
|
cs.mode.sync_enable = false;
|
|
|
|
break;
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-05 09:31:43 +00:00
|
|
|
|
|
|
|
UpdateCountingEnabled(cs);
|
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
|
2020-04-28 10:30:44 +00:00
|
|
|
TickCount Timers::GetTicksUntilIRQ(u32 timer) const
|
|
|
|
{
|
|
|
|
const CounterState& cs = m_states[timer];
|
|
|
|
if (!cs.counting_enabled)
|
|
|
|
return std::numeric_limits<TickCount>::max();
|
|
|
|
|
|
|
|
TickCount ticks_until_irq = std::numeric_limits<TickCount>::max();
|
2020-05-20 14:26:05 +00:00
|
|
|
if (cs.mode.irq_at_target && cs.counter < cs.target)
|
2020-04-28 10:30:44 +00:00
|
|
|
ticks_until_irq = static_cast<TickCount>(cs.target - cs.counter);
|
|
|
|
if (cs.mode.irq_on_overflow)
|
|
|
|
ticks_until_irq = std::min(ticks_until_irq, static_cast<TickCount>(0xFFFFu - cs.counter));
|
|
|
|
|
|
|
|
return ticks_until_irq;
|
|
|
|
}
|
|
|
|
|
2019-10-08 08:21:15 +00:00
|
|
|
void Timers::AddTicks(u32 timer, TickCount count)
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
|
|
|
CounterState& cs = m_states[timer];
|
2019-10-08 08:21:15 +00:00
|
|
|
const u32 old_counter = cs.counter;
|
|
|
|
cs.counter += static_cast<u32>(count);
|
2019-09-20 13:40:19 +00:00
|
|
|
|
2019-10-08 08:21:15 +00:00
|
|
|
bool interrupt_request = false;
|
|
|
|
if (cs.counter >= cs.target && old_counter < cs.target)
|
|
|
|
{
|
2020-01-24 04:53:40 +00:00
|
|
|
interrupt_request |= cs.mode.irq_at_target;
|
2019-09-20 13:40:19 +00:00
|
|
|
cs.mode.reached_target = true;
|
2020-06-09 15:37:11 +00:00
|
|
|
|
|
|
|
if (cs.mode.reset_at_target)
|
|
|
|
{
|
|
|
|
if (cs.target > 0)
|
|
|
|
cs.counter %= cs.target;
|
|
|
|
else
|
|
|
|
cs.counter = 0;
|
|
|
|
}
|
2019-10-08 08:21:15 +00:00
|
|
|
}
|
|
|
|
if (cs.counter >= 0xFFFF)
|
|
|
|
{
|
2020-01-24 04:53:40 +00:00
|
|
|
interrupt_request |= cs.mode.irq_on_overflow;
|
2019-09-20 13:40:19 +00:00
|
|
|
cs.mode.reached_overflow = true;
|
2020-06-09 15:37:11 +00:00
|
|
|
cs.counter %= 0xFFFFu;
|
2019-10-08 08:21:15 +00:00
|
|
|
}
|
2019-09-20 13:40:19 +00:00
|
|
|
|
2019-10-08 08:21:15 +00:00
|
|
|
if (interrupt_request)
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
2019-10-08 08:21:15 +00:00
|
|
|
if (!cs.mode.irq_pulse_n)
|
|
|
|
{
|
|
|
|
// this is actually low for a few cycles
|
|
|
|
cs.mode.interrupt_request_n = false;
|
|
|
|
UpdateIRQ(timer);
|
|
|
|
cs.mode.interrupt_request_n = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.mode.interrupt_request_n ^= true;
|
|
|
|
UpdateIRQ(timer);
|
|
|
|
}
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
void Timers::AddSysClkTicks(TickCount sysclk_ticks)
|
2019-09-20 13:59:48 +00:00
|
|
|
{
|
2020-09-29 13:29:28 +00:00
|
|
|
sysclk_ticks = System::UnscaleTicksToOverclock(sysclk_ticks, &m_syclk_ticks_carry);
|
|
|
|
|
2019-09-20 13:59:48 +00:00
|
|
|
if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled)
|
2019-10-08 08:21:15 +00:00
|
|
|
AddTicks(0, sysclk_ticks);
|
2019-09-20 13:59:48 +00:00
|
|
|
if (!m_states[1].external_counting_enabled && m_states[1].counting_enabled)
|
2019-10-08 08:21:15 +00:00
|
|
|
AddTicks(1, sysclk_ticks);
|
|
|
|
if (m_states[2].external_counting_enabled)
|
|
|
|
{
|
|
|
|
TickCount sysclk_div_8_ticks = (sysclk_ticks + m_sysclk_div_8_carry) / 8;
|
|
|
|
m_sysclk_div_8_carry = (sysclk_ticks + m_sysclk_div_8_carry) % 8;
|
|
|
|
AddTicks(2, sysclk_div_8_ticks);
|
|
|
|
}
|
|
|
|
else if (m_states[2].counting_enabled)
|
|
|
|
{
|
2019-10-12 12:15:38 +00:00
|
|
|
AddTicks(2, sysclk_ticks);
|
2019-10-08 08:21:15 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:59:48 +00:00
|
|
|
}
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
u32 Timers::ReadRegister(u32 offset)
|
|
|
|
{
|
|
|
|
const u32 timer_index = (offset >> 4) & u32(0x03);
|
|
|
|
const u32 port_offset = offset & u32(0x0F);
|
2020-08-23 04:03:36 +00:00
|
|
|
if (timer_index >= 3)
|
|
|
|
{
|
|
|
|
Log_ErrorPrintf("Timer read out of range: offset 0x%02X", offset);
|
|
|
|
return UINT32_C(0xFFFFFFFF);
|
|
|
|
}
|
2019-09-20 13:40:19 +00:00
|
|
|
|
|
|
|
CounterState& cs = m_states[timer_index];
|
|
|
|
|
|
|
|
switch (port_offset)
|
|
|
|
{
|
|
|
|
case 0x00:
|
2019-09-20 13:59:48 +00:00
|
|
|
{
|
2020-03-25 14:13:20 +00:00
|
|
|
if (timer_index < 2 && cs.external_counting_enabled)
|
2020-01-24 04:53:40 +00:00
|
|
|
{
|
|
|
|
// timers 0/1 depend on the GPU
|
2020-07-31 07:09:18 +00:00
|
|
|
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
|
|
|
g_gpu->SynchronizeCRTC();
|
2020-01-24 04:53:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_sysclk_event->InvokeEarly();
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
return cs.counter;
|
2019-09-20 13:59:48 +00:00
|
|
|
}
|
2019-09-20 13:40:19 +00:00
|
|
|
|
|
|
|
case 0x04:
|
|
|
|
{
|
2020-03-25 14:13:20 +00:00
|
|
|
if (timer_index < 2 && cs.external_counting_enabled)
|
2020-01-24 04:53:40 +00:00
|
|
|
{
|
|
|
|
// timers 0/1 depend on the GPU
|
2020-07-31 07:09:18 +00:00
|
|
|
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
|
|
|
g_gpu->SynchronizeCRTC();
|
2020-01-24 04:53:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_sysclk_event->InvokeEarly();
|
2019-09-20 13:59:48 +00:00
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
const u32 bits = cs.mode.bits;
|
|
|
|
cs.mode.reached_overflow = false;
|
|
|
|
cs.mode.reached_target = false;
|
2019-09-20 13:59:48 +00:00
|
|
|
return bits;
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 0x08:
|
|
|
|
return cs.target;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Log_ErrorPrintf("Read unknown register in timer %u (offset 0x%02X)", offset);
|
|
|
|
return UINT32_C(0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Timers::WriteRegister(u32 offset, u32 value)
|
|
|
|
{
|
|
|
|
const u32 timer_index = (offset >> 4) & u32(0x03);
|
|
|
|
const u32 port_offset = offset & u32(0x0F);
|
2020-08-23 04:03:36 +00:00
|
|
|
if (timer_index >= 3)
|
|
|
|
{
|
|
|
|
Log_ErrorPrintf("Timer write out of range: offset 0x%02X value 0x%08X", offset, value);
|
|
|
|
return;
|
|
|
|
}
|
2019-09-20 13:40:19 +00:00
|
|
|
|
|
|
|
CounterState& cs = m_states[timer_index];
|
|
|
|
|
2020-03-25 14:13:20 +00:00
|
|
|
if (timer_index < 2 && cs.external_counting_enabled)
|
|
|
|
{
|
|
|
|
// timers 0/1 depend on the GPU
|
2020-07-31 07:09:18 +00:00
|
|
|
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
|
|
|
g_gpu->SynchronizeCRTC();
|
2020-03-25 14:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 14:20:27 +00:00
|
|
|
m_sysclk_event->InvokeEarly();
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
switch (port_offset)
|
|
|
|
{
|
|
|
|
case 0x00:
|
2019-09-20 13:59:48 +00:00
|
|
|
{
|
2019-09-20 13:40:19 +00:00
|
|
|
Log_DebugPrintf("Timer %u write counter %u", timer_index, value);
|
|
|
|
cs.counter = value & u32(0xFFFF);
|
2020-06-09 15:37:11 +00:00
|
|
|
if (timer_index == 2 || !cs.external_counting_enabled)
|
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:59:48 +00:00
|
|
|
}
|
|
|
|
break;
|
2019-09-20 13:40:19 +00:00
|
|
|
|
|
|
|
case 0x04:
|
|
|
|
{
|
2020-04-25 04:17:44 +00:00
|
|
|
static constexpr u32 WRITE_MASK = 0b1110001111111111;
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
Log_DebugPrintf("Timer %u write mode register 0x%04X", timer_index, value);
|
2020-04-25 04:17:44 +00:00
|
|
|
cs.mode.bits = (value & WRITE_MASK) | (cs.mode.bits & ~WRITE_MASK);
|
2019-09-20 13:40:19 +00:00
|
|
|
cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0;
|
|
|
|
cs.counter = 0;
|
2019-10-08 08:21:15 +00:00
|
|
|
cs.irq_done = false;
|
|
|
|
|
2019-09-20 13:40:19 +00:00
|
|
|
UpdateCountingEnabled(cs);
|
2019-10-08 08:21:15 +00:00
|
|
|
UpdateIRQ(timer_index);
|
2020-01-24 04:53:40 +00:00
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x08:
|
2019-09-20 13:59:48 +00:00
|
|
|
{
|
2019-09-20 13:40:19 +00:00
|
|
|
Log_DebugPrintf("Timer %u write target 0x%04X", timer_index, ZeroExtend32(Truncate16(value)));
|
|
|
|
cs.target = value & u32(0xFFFF);
|
2020-06-09 15:37:11 +00:00
|
|
|
if (timer_index == 2 || !cs.external_counting_enabled)
|
|
|
|
UpdateSysClkEvent();
|
2019-09-20 13:59:48 +00:00
|
|
|
}
|
|
|
|
break;
|
2019-09-20 13:40:19 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
Log_ErrorPrintf("Write unknown register in timer %u (offset 0x%02X, value 0x%X)", offset, value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Timers::UpdateCountingEnabled(CounterState& cs)
|
|
|
|
{
|
|
|
|
if (cs.mode.sync_enable)
|
|
|
|
{
|
|
|
|
switch (cs.mode.sync_mode)
|
|
|
|
{
|
|
|
|
case SyncMode::PauseOnGate:
|
|
|
|
cs.counting_enabled = !cs.gate;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyncMode::ResetOnGate:
|
|
|
|
cs.counting_enabled = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyncMode::ResetAndRunOnGate:
|
2020-01-30 06:18:56 +00:00
|
|
|
case SyncMode::FreeRunOnGate:
|
2019-09-20 13:40:19 +00:00
|
|
|
cs.counting_enabled = cs.gate;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.counting_enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
cs.external_counting_enabled = cs.use_external_clock && cs.counting_enabled;
|
|
|
|
}
|
|
|
|
|
2019-10-08 08:21:15 +00:00
|
|
|
void Timers::UpdateIRQ(u32 index)
|
|
|
|
{
|
|
|
|
CounterState& cs = m_states[index];
|
|
|
|
if (cs.mode.interrupt_request_n || (!cs.mode.irq_repeat && cs.irq_done))
|
|
|
|
return;
|
2019-09-20 13:40:19 +00:00
|
|
|
|
2019-10-08 08:21:15 +00:00
|
|
|
Log_DebugPrintf("Raising timer %u IRQ", index);
|
|
|
|
cs.irq_done = true;
|
2020-07-31 07:09:18 +00:00
|
|
|
g_interrupt_controller.InterruptRequest(
|
2019-10-08 08:21:15 +00:00
|
|
|
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + index));
|
|
|
|
}
|
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
TickCount Timers::GetTicksUntilNextInterrupt() const
|
2019-09-20 13:40:19 +00:00
|
|
|
{
|
2019-10-08 08:21:15 +00:00
|
|
|
TickCount min_ticks = std::numeric_limits<TickCount>::max();
|
|
|
|
for (u32 i = 0; i < NUM_TIMERS; i++)
|
|
|
|
{
|
2020-01-24 04:53:40 +00:00
|
|
|
const CounterState& cs = m_states[i];
|
|
|
|
if (!cs.counting_enabled || (i < 2 && cs.external_counting_enabled) ||
|
|
|
|
(!cs.mode.irq_at_target && !cs.mode.irq_on_overflow))
|
|
|
|
{
|
2019-10-08 08:21:15 +00:00
|
|
|
continue;
|
2020-01-24 04:53:40 +00:00
|
|
|
}
|
2019-10-08 08:21:15 +00:00
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
TickCount min_ticks_for_this_timer = std::numeric_limits<TickCount>::max();
|
2019-10-08 08:21:15 +00:00
|
|
|
if (cs.mode.irq_at_target && cs.counter < cs.target)
|
|
|
|
min_ticks_for_this_timer = static_cast<TickCount>(cs.target - cs.counter);
|
|
|
|
if (cs.mode.irq_on_overflow && cs.counter < cs.target)
|
|
|
|
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
|
2020-09-29 13:29:28 +00:00
|
|
|
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));
|
2019-10-08 08:21:15 +00:00
|
|
|
|
|
|
|
min_ticks = std::min(min_ticks, min_ticks_for_this_timer);
|
|
|
|
}
|
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
return min_ticks;
|
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
2020-09-29 13:29:28 +00:00
|
|
|
m_sysclk_event->Schedule(System::GetMaxSliceTicks());
|
2020-01-24 04:53:40 +00:00
|
|
|
else
|
|
|
|
m_sysclk_event->Schedule(ticks);
|
2019-09-20 13:40:19 +00:00
|
|
|
}
|
2019-10-12 12:15:38 +00:00
|
|
|
|
2019-10-26 02:55:56 +00:00
|
|
|
void Timers::DrawDebugStateWindow()
|
2019-10-12 12:15:38 +00:00
|
|
|
{
|
2020-09-01 02:13:56 +00:00
|
|
|
#ifdef WITH_IMGUI
|
2019-10-12 12:15:38 +00:00
|
|
|
static constexpr u32 NUM_COLUMNS = 10;
|
|
|
|
static constexpr std::array<const char*, NUM_COLUMNS> column_names = {
|
|
|
|
{"#", "Value", "Target", "Sync", "Reset", "IRQ", "IRQRepeat", "IRQToggle", "Clock Source", "Reached"}};
|
|
|
|
static constexpr std::array<const char*, 4> sync_mode_names = {
|
|
|
|
{"PauseOnGate", "ResetOnGate", "ResetAndRunOnGate", "FreeRunOnGate"}};
|
|
|
|
static constexpr std::array<std::array<const char*, 4>, 3> clock_source_names = {
|
|
|
|
{{{"SysClk", "DotClk", "SysClk", "DotClk"}},
|
|
|
|
{{"SysClk", "HBlank", "SysClk", "HBlank"}},
|
|
|
|
{{"SysClk", "DotClk", "SysClk/8", "SysClk/8"}}}};
|
|
|
|
|
2020-02-28 06:59:51 +00:00
|
|
|
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
|
|
|
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 100.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
2020-07-31 07:09:18 +00:00
|
|
|
if (!ImGui::Begin("Timer State", &g_settings.debugging.show_timers_state))
|
2019-10-12 12:15:38 +00:00
|
|
|
{
|
|
|
|
ImGui::End();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Columns(NUM_COLUMNS);
|
2020-02-28 06:59:51 +00:00
|
|
|
ImGui::SetColumnWidth(0, 20.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(1, 50.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(2, 50.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(3, 100.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(4, 80.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(5, 80.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(6, 80.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(7, 80.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(8, 80.0f * framebuffer_scale);
|
|
|
|
ImGui::SetColumnWidth(9, 80.0f * framebuffer_scale);
|
2019-10-12 12:15:38 +00:00
|
|
|
|
|
|
|
for (const char* title : column_names)
|
|
|
|
{
|
|
|
|
ImGui::TextUnformatted(title);
|
|
|
|
ImGui::NextColumn();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (u32 i = 0; i < NUM_TIMERS; i++)
|
|
|
|
{
|
|
|
|
const CounterState& cs = m_states[i];
|
|
|
|
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));
|
|
|
|
ImGui::Text("%u", i);
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%u", cs.counter);
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%u", cs.target);
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s",
|
|
|
|
cs.mode.sync_enable ? sync_mode_names[static_cast<u8>(cs.mode.sync_mode.GetValue())] : "Disabled");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s", cs.mode.reset_at_target ? "@Target" : "@Overflow");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s%s", cs.mode.irq_at_target ? "Target " : "", cs.mode.irq_on_overflow ? "Overflow" : "");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s", cs.mode.irq_repeat ? "Yes" : "No");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s", cs.mode.irq_pulse_n ? "Yes" : "No");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%s%s", clock_source_names[i][cs.mode.clock_source], cs.external_counting_enabled ? " (External)" : "");
|
|
|
|
ImGui::NextColumn();
|
2019-10-22 13:07:51 +00:00
|
|
|
ImGui::Text("%s%s", cs.mode.reached_target ? "Target " : "", cs.mode.reached_overflow ? "Overflow" : "");
|
2019-10-12 12:15:38 +00:00
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::PopStyleColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Columns(1);
|
|
|
|
ImGui::End();
|
2020-09-01 02:13:56 +00:00
|
|
|
#endif
|
2019-10-12 12:15:38 +00:00
|
|
|
}
|