duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

timers.cpp (15717B)


      1 // SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "timers.h"
      5 #include "gpu.h"
      6 #include "interrupt_controller.h"
      7 #include "system.h"
      8 
      9 #include "util/imgui_manager.h"
     10 #include "util/state_wrapper.h"
     11 
     12 #include "common/bitfield.h"
     13 #include "common/log.h"
     14 
     15 #include "imgui.h"
     16 
     17 #include <array>
     18 #include <memory>
     19 
     20 Log_SetChannel(Timers);
     21 
     22 namespace Timers {
     23 namespace {
     24 
     25 static constexpr u32 NUM_TIMERS = 3;
     26 
     27 enum class SyncMode : u8
     28 {
     29   PauseWhileGateActive = 0,
     30   ResetOnGateEnd = 1,
     31   ResetAndRunOnGateStart = 2,
     32   FreeRunOnGateEnd = 3
     33 };
     34 
     35 union CounterMode
     36 {
     37   u32 bits;
     38 
     39   BitField<u32, bool, 0, 1> sync_enable;
     40   BitField<u32, SyncMode, 1, 2> sync_mode;
     41   BitField<u32, bool, 3, 1> reset_at_target;
     42   BitField<u32, bool, 4, 1> irq_at_target;
     43   BitField<u32, bool, 5, 1> irq_on_overflow;
     44   BitField<u32, bool, 6, 1> irq_repeat;
     45   BitField<u32, bool, 7, 1> irq_pulse_n;
     46   BitField<u32, u8, 8, 2> clock_source;
     47   BitField<u32, bool, 10, 1> interrupt_request_n;
     48   BitField<u32, bool, 11, 1> reached_target;
     49   BitField<u32, bool, 12, 1> reached_overflow;
     50 };
     51 
     52 struct CounterState
     53 {
     54   CounterMode mode;
     55   u32 counter;
     56   u32 target;
     57   bool gate;
     58   bool use_external_clock;
     59   bool external_counting_enabled;
     60   bool counting_enabled;
     61   bool irq_done;
     62 };
     63 
     64 } // namespace
     65 
     66 static void UpdateCountingEnabled(CounterState& cs);
     67 static void CheckForIRQ(u32 index, u32 old_counter);
     68 
     69 static void AddSysClkTicks(void*, TickCount sysclk_ticks, TickCount ticks_late);
     70 
     71 static TickCount GetTicksUntilNextInterrupt();
     72 static void UpdateSysClkEvent();
     73 
     74 namespace {
     75 struct TimersState
     76 {
     77   TimingEvent sysclk_event{ "Timer SysClk Interrupt", 1, 1, &Timers::AddSysClkTicks, nullptr };
     78 
     79   std::array<CounterState, NUM_TIMERS> counters{};
     80   TickCount sysclk_ticks_carry = 0; // 0 unless overclocking is enabled
     81   u32 sysclk_div_8_carry = 0;       // partial ticks for timer 3 with sysclk/8
     82 };
     83 } // namespace
     84 
     85 ALIGN_TO_CACHE_LINE static TimersState s_state;
     86 }; // namespace Timers
     87 
     88 void Timers::Initialize()
     89 {
     90   Reset();
     91 }
     92 
     93 void Timers::Shutdown()
     94 {
     95   s_state.sysclk_event.Deactivate();
     96 }
     97 
     98 void Timers::Reset()
     99 {
    100   for (CounterState& cs : s_state.counters)
    101   {
    102     cs.mode.bits = 0;
    103     cs.mode.interrupt_request_n = true;
    104     cs.counter = 0;
    105     cs.target = 0;
    106     cs.gate = false;
    107     cs.external_counting_enabled = false;
    108     cs.counting_enabled = true;
    109     cs.irq_done = false;
    110   }
    111 
    112   s_state.sysclk_event.Deactivate();
    113   s_state.sysclk_ticks_carry = 0;
    114   s_state.sysclk_div_8_carry = 0;
    115   UpdateSysClkEvent();
    116 }
    117 
    118 bool Timers::DoState(StateWrapper& sw)
    119 {
    120   for (CounterState& cs : s_state.counters)
    121   {
    122     sw.Do(&cs.mode.bits);
    123     sw.Do(&cs.counter);
    124     sw.Do(&cs.target);
    125     sw.Do(&cs.gate);
    126     sw.Do(&cs.use_external_clock);
    127     sw.Do(&cs.external_counting_enabled);
    128     sw.Do(&cs.counting_enabled);
    129     sw.Do(&cs.irq_done);
    130   }
    131 
    132   sw.Do(&s_state.sysclk_ticks_carry);
    133   sw.Do(&s_state.sysclk_div_8_carry);
    134 
    135   if (sw.IsReading())
    136     UpdateSysClkEvent();
    137 
    138   return !sw.HasError();
    139 }
    140 
    141 void Timers::CPUClocksChanged()
    142 {
    143   s_state.sysclk_ticks_carry = 0;
    144 }
    145 
    146 bool Timers::IsUsingExternalClock(u32 timer)
    147 {
    148   return s_state.counters[timer].external_counting_enabled;
    149 }
    150 
    151 bool Timers::IsSyncEnabled(u32 timer)
    152 {
    153   return s_state.counters[timer].mode.sync_enable;
    154 }
    155 
    156 bool Timers::IsExternalIRQEnabled(u32 timer)
    157 {
    158   const CounterState& cs = s_state.counters[timer];
    159   return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0);
    160 }
    161 
    162 void Timers::SetGate(u32 timer, bool state)
    163 {
    164   CounterState& cs = s_state.counters[timer];
    165   if (cs.gate == state)
    166     return;
    167 
    168   cs.gate = state;
    169 
    170   if (!cs.mode.sync_enable)
    171     return;
    172 
    173   // Because the gate prevents counting in or outside of the gate, we need a correct counter.
    174   // For reset, we _can_ skip it, until the gate clears.
    175   if (!cs.use_external_clock && (cs.mode.sync_mode != SyncMode::ResetOnGateEnd || !state))
    176     s_state.sysclk_event.InvokeEarly();
    177 
    178   switch (cs.mode.sync_mode)
    179   {
    180     case SyncMode::PauseWhileGateActive:
    181       break;
    182 
    183     case SyncMode::ResetOnGateEnd:
    184       cs.counter = state ? cs.counter : 0;
    185       break;
    186 
    187     case SyncMode::ResetAndRunOnGateStart:
    188       // PS2 hardwires the counter to 0 outside of gate. Needs to be tested for PSX too.
    189       cs.counter = state ? 0 : cs.counter;
    190       break;
    191 
    192     case SyncMode::FreeRunOnGateEnd:
    193       cs.mode.sync_enable &= state;
    194       break;
    195 
    196     default:
    197       UnreachableCode();
    198   }
    199 
    200   UpdateCountingEnabled(cs);
    201   UpdateSysClkEvent();
    202 }
    203 
    204 TickCount Timers::GetTicksUntilIRQ(u32 timer)
    205 {
    206   const CounterState& cs = s_state.counters[timer];
    207   if (!cs.counting_enabled)
    208     return std::numeric_limits<TickCount>::max();
    209 
    210   TickCount ticks_until_irq = std::numeric_limits<TickCount>::max();
    211   if (cs.mode.irq_at_target && cs.counter < cs.target)
    212     ticks_until_irq = static_cast<TickCount>(cs.target - cs.counter);
    213   if (cs.mode.irq_on_overflow)
    214     ticks_until_irq = std::min(ticks_until_irq, static_cast<TickCount>(0xFFFFu - cs.counter));
    215 
    216   return ticks_until_irq;
    217 }
    218 
    219 void Timers::AddTicks(u32 timer, TickCount count)
    220 {
    221   CounterState& cs = s_state.counters[timer];
    222   const u32 old_counter = cs.counter;
    223   cs.counter += static_cast<u32>(count);
    224   CheckForIRQ(timer, old_counter);
    225 }
    226 
    227 void Timers::CheckForIRQ(u32 timer, u32 old_counter)
    228 {
    229   CounterState& cs = s_state.counters[timer];
    230 
    231   bool interrupt_request = false;
    232   if (cs.counter >= cs.target && (old_counter < cs.target || cs.target == 0))
    233   {
    234     interrupt_request |= cs.mode.irq_at_target;
    235     cs.mode.reached_target = true;
    236 
    237     if (cs.mode.reset_at_target && cs.target > 0)
    238       cs.counter %= cs.target;
    239   }
    240   if (cs.counter >= 0xFFFF)
    241   {
    242     interrupt_request |= cs.mode.irq_on_overflow;
    243     cs.mode.reached_overflow = true;
    244     cs.counter %= 0xFFFFu;
    245   }
    246 
    247   if (interrupt_request)
    248   {
    249     const InterruptController::IRQ irqnum =
    250       static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + timer);
    251     if (!cs.mode.irq_pulse_n)
    252     {
    253       if (!cs.irq_done || cs.mode.irq_repeat)
    254       {
    255         // this is actually low for a few cycles
    256         DEBUG_LOG("Raising timer {} pulse IRQ", timer);
    257         InterruptController::SetLineState(irqnum, false);
    258         InterruptController::SetLineState(irqnum, true);
    259       }
    260 
    261       cs.irq_done = true;
    262       cs.mode.interrupt_request_n = true;
    263     }
    264     else
    265     {
    266       // TODO: How does the non-repeat mode work here?
    267       cs.mode.interrupt_request_n ^= true;
    268       if (!cs.mode.interrupt_request_n)
    269         DEBUG_LOG("Raising timer {} alternate IRQ", timer);
    270 
    271       InterruptController::SetLineState(irqnum, !cs.mode.interrupt_request_n);
    272     }
    273   }
    274 }
    275 
    276 void Timers::AddSysClkTicks(void*, TickCount sysclk_ticks, TickCount ticks_late)
    277 {
    278   sysclk_ticks = System::UnscaleTicksToOverclock(sysclk_ticks, &s_state.sysclk_ticks_carry);
    279 
    280   if (!s_state.counters[0].external_counting_enabled && s_state.counters[0].counting_enabled)
    281     AddTicks(0, sysclk_ticks);
    282   if (!s_state.counters[1].external_counting_enabled && s_state.counters[1].counting_enabled)
    283     AddTicks(1, sysclk_ticks);
    284   if (s_state.counters[2].external_counting_enabled)
    285   {
    286     TickCount sysclk_div_8_ticks = (sysclk_ticks + s_state.sysclk_div_8_carry) / 8;
    287     s_state.sysclk_div_8_carry = (sysclk_ticks + s_state.sysclk_div_8_carry) % 8;
    288     AddTicks(2, sysclk_div_8_ticks);
    289   }
    290   else if (s_state.counters[2].counting_enabled)
    291   {
    292     AddTicks(2, sysclk_ticks);
    293   }
    294 
    295   UpdateSysClkEvent();
    296 }
    297 
    298 u32 Timers::ReadRegister(u32 offset)
    299 {
    300   const u32 timer_index = (offset >> 4) & u32(0x03);
    301   const u32 port_offset = offset & u32(0x0F);
    302   if (timer_index >= 3) [[unlikely]]
    303   {
    304     ERROR_LOG("Timer read out of range: offset 0x{:02X}", offset);
    305     return UINT32_C(0xFFFFFFFF);
    306   }
    307 
    308   CounterState& cs = s_state.counters[timer_index];
    309 
    310   switch (port_offset)
    311   {
    312     case 0x00:
    313     {
    314       if (timer_index < 2 && cs.external_counting_enabled)
    315       {
    316         // timers 0/1 depend on the GPU
    317         if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
    318           g_gpu->SynchronizeCRTC();
    319       }
    320 
    321       s_state.sysclk_event.InvokeEarly();
    322 
    323       return cs.counter;
    324     }
    325 
    326     case 0x04:
    327     {
    328       if (timer_index < 2 && cs.external_counting_enabled)
    329       {
    330         // timers 0/1 depend on the GPU
    331         if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
    332           g_gpu->SynchronizeCRTC();
    333       }
    334 
    335       s_state.sysclk_event.InvokeEarly();
    336 
    337       const u32 bits = cs.mode.bits;
    338       cs.mode.reached_overflow = false;
    339       cs.mode.reached_target = false;
    340       return bits;
    341     }
    342 
    343     case 0x08:
    344       return cs.target;
    345 
    346     default:
    347       [[unlikely]] ERROR_LOG("Read unknown register in timer {} (offset 0x{:02X})", timer_index, offset);
    348       return UINT32_C(0xFFFFFFFF);
    349   }
    350 }
    351 
    352 void Timers::WriteRegister(u32 offset, u32 value)
    353 {
    354   const u32 timer_index = (offset >> 4) & u32(0x03);
    355   const u32 port_offset = offset & u32(0x0F);
    356   if (timer_index >= 3) [[unlikely]]
    357   {
    358     ERROR_LOG("Timer write out of range: offset 0{:02X} value 0x{:08X}", offset, value);
    359     return;
    360   }
    361 
    362   CounterState& cs = s_state.counters[timer_index];
    363 
    364   if (timer_index < 2 && cs.external_counting_enabled)
    365   {
    366     // timers 0/1 depend on the GPU
    367     if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
    368       g_gpu->SynchronizeCRTC();
    369   }
    370 
    371   s_state.sysclk_event.InvokeEarly();
    372 
    373   // Strictly speaking these IRQ checks should probably happen on the next tick.
    374   switch (port_offset)
    375   {
    376     case 0x00:
    377     {
    378       const u32 old_counter = cs.counter;
    379       DEBUG_LOG("Timer {} write counter {}", timer_index, value);
    380       cs.counter = value & u32(0xFFFF);
    381       CheckForIRQ(timer_index, old_counter);
    382       if (timer_index == 2 || !cs.external_counting_enabled)
    383         UpdateSysClkEvent();
    384     }
    385     break;
    386 
    387     case 0x04:
    388     {
    389       static constexpr u32 WRITE_MASK = 0b1110001111111111;
    390 
    391       DEBUG_LOG("Timer {} write mode register 0x{:04X}", timer_index, value);
    392       cs.mode.bits = (value & WRITE_MASK) | (cs.mode.bits & ~WRITE_MASK);
    393       cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0;
    394       cs.counter = 0;
    395       cs.irq_done = false;
    396       InterruptController::SetLineState(
    397         static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + timer_index), false);
    398 
    399       UpdateCountingEnabled(cs);
    400       CheckForIRQ(timer_index, cs.counter);
    401       UpdateSysClkEvent();
    402     }
    403     break;
    404 
    405     case 0x08:
    406     {
    407       DEBUG_LOG("Timer {} write target 0x{:04X}", timer_index, ZeroExtend32(Truncate16(value)));
    408       cs.target = value & u32(0xFFFF);
    409       CheckForIRQ(timer_index, cs.counter);
    410       if (timer_index == 2 || !cs.external_counting_enabled)
    411         UpdateSysClkEvent();
    412     }
    413     break;
    414 
    415     default:
    416       ERROR_LOG("Write unknown register in timer {} (offset 0x{:02X}, value 0x{:X})", timer_index, offset, value);
    417       break;
    418   }
    419 }
    420 
    421 void Timers::UpdateCountingEnabled(CounterState& cs)
    422 {
    423   if (cs.mode.sync_enable)
    424   {
    425     switch (cs.mode.sync_mode)
    426     {
    427       case SyncMode::PauseWhileGateActive:
    428         cs.counting_enabled = !cs.gate;
    429         break;
    430 
    431       case SyncMode::ResetOnGateEnd:
    432         cs.counting_enabled = true;
    433         break;
    434 
    435       case SyncMode::ResetAndRunOnGateStart:
    436       case SyncMode::FreeRunOnGateEnd:
    437         cs.counting_enabled = cs.gate;
    438         break;
    439 
    440       default:
    441         UnreachableCode();
    442     }
    443   }
    444   else
    445   {
    446     cs.counting_enabled = true;
    447   }
    448 
    449   cs.external_counting_enabled = cs.use_external_clock && cs.counting_enabled;
    450 }
    451 
    452 TickCount Timers::GetTicksUntilNextInterrupt()
    453 {
    454   TickCount min_ticks = System::GetMaxSliceTicks();
    455   for (u32 i = 0; i < NUM_TIMERS; i++)
    456   {
    457     const CounterState& cs = s_state.counters[i];
    458     if (!cs.counting_enabled || (i < 2 && cs.external_counting_enabled) ||
    459         (!cs.mode.irq_at_target && !cs.mode.irq_on_overflow && (cs.mode.irq_repeat || !cs.irq_done)))
    460     {
    461       continue;
    462     }
    463 
    464     if (cs.mode.irq_at_target)
    465     {
    466       TickCount ticks = (cs.counter <= cs.target) ? static_cast<TickCount>(cs.target - cs.counter) :
    467                                                     static_cast<TickCount>((0xFFFFu - cs.counter) + cs.target);
    468       if (cs.external_counting_enabled) // sysclk/8 for timer 2
    469         ticks *= 8;
    470 
    471       min_ticks = std::min(min_ticks, ticks);
    472     }
    473     if (cs.mode.irq_on_overflow)
    474     {
    475       TickCount ticks = static_cast<TickCount>(0xFFFFu - cs.counter);
    476       if (cs.external_counting_enabled) // sysclk/8 for timer 2
    477         ticks *= 8;
    478 
    479       min_ticks = std::min(min_ticks, ticks);
    480     }
    481   }
    482 
    483   return System::ScaleTicksToOverclock(std::max<TickCount>(1, min_ticks));
    484 }
    485 
    486 void Timers::UpdateSysClkEvent()
    487 {
    488   s_state.sysclk_event.Schedule(GetTicksUntilNextInterrupt());
    489 }
    490 
    491 void Timers::DrawDebugStateWindow()
    492 {
    493   static constexpr u32 NUM_COLUMNS = 10;
    494   static constexpr std::array<const char*, NUM_COLUMNS> column_names = {
    495     {"#", "Value", "Target", "Sync", "Reset", "IRQ", "IRQRepeat", "IRQToggle", "Clock Source", "Reached"}};
    496   static constexpr std::array<const char*, 4> sync_mode_names = {
    497     {"PauseOnGate", "ResetOnGate", "ResetAndRunOnGate", "FreeRunOnGate"}};
    498   static constexpr std::array<std::array<const char*, 4>, 3> clock_source_names = {
    499     {{{"SysClk", "DotClk", "SysClk", "DotClk"}},
    500      {{"SysClk", "HBlank", "SysClk", "HBlank"}},
    501      {{"SysClk", "DotClk", "SysClk/8", "SysClk/8"}}}};
    502 
    503   const float framebuffer_scale = ImGuiManager::GetGlobalScale();
    504 
    505   ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 115.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
    506   if (!ImGui::Begin("Timer State", nullptr))
    507   {
    508     ImGui::End();
    509     return;
    510   }
    511 
    512   ImGui::Columns(NUM_COLUMNS);
    513   ImGui::SetColumnWidth(0, 20.0f * framebuffer_scale);
    514   ImGui::SetColumnWidth(1, 50.0f * framebuffer_scale);
    515   ImGui::SetColumnWidth(2, 50.0f * framebuffer_scale);
    516   ImGui::SetColumnWidth(3, 100.0f * framebuffer_scale);
    517   ImGui::SetColumnWidth(4, 80.0f * framebuffer_scale);
    518   ImGui::SetColumnWidth(5, 80.0f * framebuffer_scale);
    519   ImGui::SetColumnWidth(6, 80.0f * framebuffer_scale);
    520   ImGui::SetColumnWidth(7, 80.0f * framebuffer_scale);
    521   ImGui::SetColumnWidth(8, 80.0f * framebuffer_scale);
    522   ImGui::SetColumnWidth(9, 80.0f * framebuffer_scale);
    523 
    524   for (const char* title : column_names)
    525   {
    526     ImGui::TextUnformatted(title);
    527     ImGui::NextColumn();
    528   }
    529 
    530   for (u32 i = 0; i < NUM_TIMERS; i++)
    531   {
    532     const CounterState& cs = s_state.counters[i];
    533     ImGui::PushStyleColor(ImGuiCol_Text,
    534                           cs.counting_enabled ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f));
    535     ImGui::Text("%u", i);
    536     ImGui::NextColumn();
    537     ImGui::Text("%u", cs.counter);
    538     ImGui::NextColumn();
    539     ImGui::Text("%u", cs.target);
    540     ImGui::NextColumn();
    541     ImGui::Text("%s",
    542                 cs.mode.sync_enable ? sync_mode_names[static_cast<u8>(cs.mode.sync_mode.GetValue())] : "Disabled");
    543     ImGui::NextColumn();
    544     ImGui::Text("%s", cs.mode.reset_at_target ? "@Target" : "@Overflow");
    545     ImGui::NextColumn();
    546     ImGui::Text("%s%s", cs.mode.irq_at_target ? "Target " : "", cs.mode.irq_on_overflow ? "Overflow" : "");
    547     ImGui::NextColumn();
    548     ImGui::Text("%s", cs.mode.irq_repeat ? "Yes" : "No");
    549     ImGui::NextColumn();
    550     ImGui::Text("%s", cs.mode.irq_pulse_n ? "Yes" : "No");
    551     ImGui::NextColumn();
    552     ImGui::Text("%s%s", clock_source_names[i][cs.mode.clock_source], cs.external_counting_enabled ? " (External)" : "");
    553     ImGui::NextColumn();
    554     ImGui::Text("%s%s", cs.mode.reached_target ? "Target " : "", cs.mode.reached_overflow ? "Overflow" : "");
    555     ImGui::NextColumn();
    556     ImGui::PopStyleColor();
    557   }
    558 
    559   ImGui::Columns(1);
    560   ImGui::End();
    561 }