Refactor timing to allow sync/updates in the middle of a slice

This commit is contained in:
Connor McLaughlin 2019-09-20 23:59:48 +10:00
parent ad316162f3
commit c988af453c
10 changed files with 74 additions and 36 deletions

View File

@ -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:

View File

@ -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()

View File

@ -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 = {};

View File

@ -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)

View File

@ -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();
}

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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{};

View File

@ -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;