Refactor timing to allow sync/updates in the middle of a slice
This commit is contained in:
parent
ad316162f3
commit
c988af453c
|
@ -264,7 +264,7 @@ bool SDLInterface::HandleSDLEvent(const SDL_Event* event)
|
|||
m_controller->SetButtonState(DigitalController::Button::L2, pressed);
|
||||
return true;
|
||||
case SDL_SCANCODE_3:
|
||||
m_controller->SetButtonState(DigitalController::Button::R3, pressed);
|
||||
m_controller->SetButtonState(DigitalController::Button::R2, pressed);
|
||||
return true;
|
||||
|
||||
case SDL_SCANCODE_RETURN:
|
||||
|
|
|
@ -26,7 +26,8 @@ bool Core::Initialize(Bus* bus)
|
|||
|
||||
void Core::Reset()
|
||||
{
|
||||
m_slice_ticks = std::numeric_limits<decltype(m_slice_ticks)>::max();
|
||||
m_pending_ticks = 0;
|
||||
m_downcount = MAX_SLICE_SIZE;
|
||||
|
||||
m_regs = {};
|
||||
|
||||
|
@ -47,7 +48,8 @@ void Core::Reset()
|
|||
|
||||
bool Core::DoState(StateWrapper& sw)
|
||||
{
|
||||
sw.Do(&m_slice_ticks);
|
||||
sw.Do(&m_pending_ticks);
|
||||
sw.Do(&m_downcount);
|
||||
sw.DoArray(m_regs.r, countof(m_regs.r));
|
||||
sw.Do(&m_regs.pc);
|
||||
sw.Do(&m_regs.hi);
|
||||
|
@ -312,12 +314,12 @@ void Core::DisassembleAndPrint(u32 addr)
|
|||
PrintInstruction(bits, addr);
|
||||
}
|
||||
|
||||
TickCount Core::Execute()
|
||||
void Core::Execute()
|
||||
{
|
||||
TickCount executed_ticks = 0;
|
||||
while (executed_ticks < m_slice_ticks)
|
||||
while (m_downcount >= 0)
|
||||
{
|
||||
executed_ticks++;
|
||||
m_pending_ticks += 3;
|
||||
m_downcount -= 3;
|
||||
|
||||
// now executing the instruction we previously fetched
|
||||
const Instruction inst = m_next_instruction;
|
||||
|
@ -340,10 +342,6 @@ TickCount Core::Execute()
|
|||
m_load_delay_old_value = m_next_load_delay_old_value;
|
||||
m_next_load_delay_old_value = 0;
|
||||
}
|
||||
|
||||
// reset slice ticks, it'll be updated when the components execute
|
||||
m_slice_ticks = MAX_CPU_SLICE_SIZE;
|
||||
return executed_ticks;
|
||||
}
|
||||
|
||||
bool Core::FetchInstruction()
|
||||
|
|
|
@ -27,12 +27,16 @@ public:
|
|||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
TickCount Execute();
|
||||
void Execute();
|
||||
|
||||
const Registers& GetRegs() const { return m_regs; }
|
||||
Registers& GetRegs() { return m_regs; }
|
||||
|
||||
void SetSliceTicks(TickCount downcount) { m_slice_ticks = (downcount < m_slice_ticks ? downcount : m_slice_ticks); }
|
||||
TickCount GetPendingTicks() const { return m_pending_ticks; }
|
||||
void ResetPendingTicks() { m_pending_ticks = 0; }
|
||||
|
||||
void SetDowncount(TickCount downcount) { m_downcount = (downcount < m_downcount) ? downcount : m_downcount; }
|
||||
void ResetDowncount() { m_downcount = MAX_SLICE_SIZE; }
|
||||
|
||||
// Sets the PC and flushes the pipeline.
|
||||
void SetPC(u32 new_pc);
|
||||
|
@ -101,8 +105,9 @@ private:
|
|||
|
||||
Bus* m_bus = nullptr;
|
||||
|
||||
// ticks of master/CPU clock until the next event
|
||||
TickCount m_slice_ticks = 0;
|
||||
// ticks the CPU has executed
|
||||
TickCount m_pending_ticks = 0;
|
||||
TickCount m_downcount = MAX_SLICE_SIZE;
|
||||
|
||||
Registers m_regs = {};
|
||||
Cop0Registers m_cop0_regs = {};
|
||||
|
|
|
@ -256,7 +256,7 @@ void GPU::UpdateCRTCConfig()
|
|||
void GPU::UpdateSliceTicks()
|
||||
{
|
||||
// the next event is at the end of the next scanline
|
||||
#if 1
|
||||
#if 0
|
||||
const TickCount ticks_until_next_event = m_crtc_state.ticks_per_scanline - m_crtc_state.current_tick_in_scanline;
|
||||
#else
|
||||
// or at vblank. this will depend on the timer config..
|
||||
|
@ -267,7 +267,7 @@ void GPU::UpdateSliceTicks()
|
|||
|
||||
// convert to master clock, rounding up as we want to overshoot not undershoot
|
||||
const TickCount system_ticks = (ticks_until_next_event * 7 + 10) / 11;
|
||||
m_system->SetSliceTicks(system_ticks);
|
||||
m_system->SetDowncount(system_ticks);
|
||||
}
|
||||
|
||||
void GPU::Execute(TickCount ticks)
|
||||
|
|
|
@ -57,7 +57,9 @@ void InterruptController::WriteRegister(u32 offset, u32 value)
|
|||
{
|
||||
case 0x00: // I_STATUS
|
||||
{
|
||||
Log_DebugPrintf("Clearing bits 0x%08X", value);
|
||||
if ((m_interrupt_status_register & ~value) != 0)
|
||||
Log_DebugPrintf("Clearing bits 0x%08X", (m_interrupt_status_register & ~value));
|
||||
|
||||
m_interrupt_status_register = m_interrupt_status_register & (value & REGISTER_WRITE_MASK);
|
||||
UpdateCPUInterruptRequest();
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ bool System::Initialize()
|
|||
if (!m_pad->Initialize(m_interrupt_controller.get()))
|
||||
return false;
|
||||
|
||||
if (!m_timers->Initialize(m_interrupt_controller.get()))
|
||||
if (!m_timers->Initialize(this, m_interrupt_controller.get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -89,8 +89,6 @@ bool System::DoState(StateWrapper& sw)
|
|||
|
||||
void System::Reset()
|
||||
{
|
||||
SetSliceTicks(1);
|
||||
|
||||
m_cpu->Reset();
|
||||
m_bus->Reset();
|
||||
m_dma->Reset();
|
||||
|
@ -119,12 +117,8 @@ void System::RunFrame()
|
|||
u32 current_frame_number = m_frame_number;
|
||||
while (current_frame_number == m_frame_number)
|
||||
{
|
||||
const TickCount pending_ticks = m_cpu->Execute();
|
||||
|
||||
// run pending ticks from CPU for other components
|
||||
m_gpu->Execute(pending_ticks * 3);
|
||||
|
||||
m_timers->AddTicks(2, m_timers->IsUsingExternalClock(2) ? (pending_ticks / 8) : pending_ticks);
|
||||
m_cpu->Execute();
|
||||
Synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,9 +209,20 @@ bool System::LoadEXE(const char* filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
void System::SetSliceTicks(TickCount downcount)
|
||||
void System::Synchronize()
|
||||
{
|
||||
m_cpu->SetSliceTicks(downcount);
|
||||
m_cpu->ResetDowncount();
|
||||
|
||||
const TickCount pending_ticks = m_cpu->GetPendingTicks();
|
||||
m_cpu->ResetPendingTicks();
|
||||
|
||||
m_gpu->Execute(pending_ticks);
|
||||
m_timers->AddSystemTicks(pending_ticks);
|
||||
}
|
||||
|
||||
void System::SetDowncount(TickCount downcount)
|
||||
{
|
||||
m_cpu->SetDowncount(downcount);
|
||||
}
|
||||
|
||||
void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev)
|
||||
|
|
|
@ -42,7 +42,8 @@ public:
|
|||
|
||||
bool LoadEXE(const char* filename);
|
||||
|
||||
void SetSliceTicks(TickCount downcount);
|
||||
void SetDowncount(TickCount downcount);
|
||||
void Synchronize();
|
||||
|
||||
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);
|
||||
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
#include "YBaseLib/Log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(Timers);
|
||||
|
||||
Timers::Timers() = default;
|
||||
|
||||
Timers::~Timers() = default;
|
||||
|
||||
bool Timers::Initialize(InterruptController* interrupt_controller)
|
||||
bool Timers::Initialize(System* system, InterruptController* interrupt_controller)
|
||||
{
|
||||
m_system = system;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
return true;
|
||||
}
|
||||
|
@ -103,6 +105,16 @@ void Timers::AddTicks(u32 timer, u32 count)
|
|||
cs.counter = 0;
|
||||
}
|
||||
|
||||
void Timers::AddSystemTicks(u32 ticks)
|
||||
{
|
||||
if (!m_states[0].external_counting_enabled && m_states[0].counting_enabled)
|
||||
AddTicks(0, ticks);
|
||||
if (!m_states[1].external_counting_enabled && m_states[1].counting_enabled)
|
||||
AddTicks(1, ticks);
|
||||
if (m_states[2].counting_enabled)
|
||||
AddTicks(2, m_states[2].external_counting_enabled ? (ticks / 8) : (ticks));
|
||||
}
|
||||
|
||||
u32 Timers::ReadRegister(u32 offset)
|
||||
{
|
||||
const u32 timer_index = (offset >> 4) & u32(0x03);
|
||||
|
@ -113,15 +125,20 @@ u32 Timers::ReadRegister(u32 offset)
|
|||
switch (port_offset)
|
||||
{
|
||||
case 0x00:
|
||||
{
|
||||
m_system->Synchronize();
|
||||
return cs.counter;
|
||||
}
|
||||
|
||||
case 0x04:
|
||||
{
|
||||
m_system->Synchronize();
|
||||
|
||||
const u32 bits = cs.mode.bits;
|
||||
cs.mode.reached_overflow = false;
|
||||
cs.mode.reached_target = false;
|
||||
return bits;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
return cs.target;
|
||||
|
@ -142,13 +159,17 @@ void Timers::WriteRegister(u32 offset, u32 value)
|
|||
switch (port_offset)
|
||||
{
|
||||
case 0x00:
|
||||
{
|
||||
Log_DebugPrintf("Timer %u write counter %u", timer_index, value);
|
||||
m_system->Synchronize();
|
||||
cs.counter = value & u32(0xFFFF);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
{
|
||||
Log_DebugPrintf("Timer %u write mode register 0x%04X", timer_index, value);
|
||||
m_system->Synchronize();
|
||||
cs.mode.bits = value & u32(0x1FFF);
|
||||
cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0;
|
||||
cs.counter = 0;
|
||||
|
@ -157,9 +178,12 @@ void Timers::WriteRegister(u32 offset, u32 value)
|
|||
break;
|
||||
|
||||
case 0x08:
|
||||
{
|
||||
Log_DebugPrintf("Timer %u write target 0x%04X", timer_index, ZeroExtend32(Truncate16(value)));
|
||||
m_system->Synchronize();
|
||||
cs.target = value & u32(0xFFFF);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Log_ErrorPrintf("Write unknown register in timer %u (offset 0x%02X, value 0x%X)", offset, value);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class InterruptController;
|
||||
|
||||
class Timers
|
||||
|
@ -13,7 +14,7 @@ public:
|
|||
Timers();
|
||||
~Timers();
|
||||
|
||||
bool Initialize(InterruptController* interrupt_controller);
|
||||
bool Initialize(System* system, InterruptController* interrupt_controller);
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -22,6 +23,7 @@ public:
|
|||
// dot clock/hblank/sysclk div 8
|
||||
bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
||||
void AddTicks(u32 timer, u32 ticks);
|
||||
void AddSystemTicks(u32 ticks);
|
||||
|
||||
u32 ReadRegister(u32 offset);
|
||||
void WriteRegister(u32 offset, u32 value);
|
||||
|
@ -70,6 +72,7 @@ private:
|
|||
void UpdateDowncount();
|
||||
u32 GetSystemTicksForTimerTicks(u32 timer) const;
|
||||
|
||||
System* m_system = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
|
||||
std::array<CounterState, NUM_TIMERS> m_states{};
|
||||
|
|
|
@ -20,5 +20,5 @@ enum class MemoryAccessSize : u32
|
|||
using TickCount = s32;
|
||||
|
||||
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock
|
||||
static constexpr TickCount MAX_CPU_SLICE_SIZE = MASTER_CLOCK / 10;
|
||||
static constexpr TickCount MAX_SLICE_SIZE = MASTER_CLOCK / 10;
|
||||
|
||||
|
|
Loading…
Reference in New Issue