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); m_controller->SetButtonState(DigitalController::Button::L2, pressed);
return true; return true;
case SDL_SCANCODE_3: case SDL_SCANCODE_3:
m_controller->SetButtonState(DigitalController::Button::R3, pressed); m_controller->SetButtonState(DigitalController::Button::R2, pressed);
return true; return true;
case SDL_SCANCODE_RETURN: case SDL_SCANCODE_RETURN:

View file

@ -26,7 +26,8 @@ bool Core::Initialize(Bus* bus)
void Core::Reset() 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 = {}; m_regs = {};
@ -47,7 +48,8 @@ void Core::Reset()
bool Core::DoState(StateWrapper& sw) 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.DoArray(m_regs.r, countof(m_regs.r));
sw.Do(&m_regs.pc); sw.Do(&m_regs.pc);
sw.Do(&m_regs.hi); sw.Do(&m_regs.hi);
@ -312,12 +314,12 @@ void Core::DisassembleAndPrint(u32 addr)
PrintInstruction(bits, addr); PrintInstruction(bits, addr);
} }
TickCount Core::Execute() void Core::Execute()
{ {
TickCount executed_ticks = 0; while (m_downcount >= 0)
while (executed_ticks < m_slice_ticks)
{ {
executed_ticks++; m_pending_ticks += 3;
m_downcount -= 3;
// now executing the instruction we previously fetched // now executing the instruction we previously fetched
const Instruction inst = m_next_instruction; 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_load_delay_old_value = m_next_load_delay_old_value;
m_next_load_delay_old_value = 0; 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() bool Core::FetchInstruction()

View file

@ -27,12 +27,16 @@ public:
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
TickCount Execute(); void Execute();
const Registers& GetRegs() const { return m_regs; } const Registers& GetRegs() const { return m_regs; }
Registers& GetRegs() { 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. // Sets the PC and flushes the pipeline.
void SetPC(u32 new_pc); void SetPC(u32 new_pc);
@ -101,8 +105,9 @@ private:
Bus* m_bus = nullptr; Bus* m_bus = nullptr;
// ticks of master/CPU clock until the next event // ticks the CPU has executed
TickCount m_slice_ticks = 0; TickCount m_pending_ticks = 0;
TickCount m_downcount = MAX_SLICE_SIZE;
Registers m_regs = {}; Registers m_regs = {};
Cop0Registers m_cop0_regs = {}; Cop0Registers m_cop0_regs = {};

View file

@ -256,7 +256,7 @@ void GPU::UpdateCRTCConfig()
void GPU::UpdateSliceTicks() void GPU::UpdateSliceTicks()
{ {
// the next event is at the end of the next scanline // 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; const TickCount ticks_until_next_event = m_crtc_state.ticks_per_scanline - m_crtc_state.current_tick_in_scanline;
#else #else
// or at vblank. this will depend on the timer config.. // 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 // convert to master clock, rounding up as we want to overshoot not undershoot
const TickCount system_ticks = (ticks_until_next_event * 7 + 10) / 11; 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) void GPU::Execute(TickCount ticks)

View file

@ -57,7 +57,9 @@ void InterruptController::WriteRegister(u32 offset, u32 value)
{ {
case 0x00: // I_STATUS 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); m_interrupt_status_register = m_interrupt_status_register & (value & REGISTER_WRITE_MASK);
UpdateCPUInterruptRequest(); UpdateCPUInterruptRequest();
} }

View file

@ -52,7 +52,7 @@ bool System::Initialize()
if (!m_pad->Initialize(m_interrupt_controller.get())) if (!m_pad->Initialize(m_interrupt_controller.get()))
return false; return false;
if (!m_timers->Initialize(m_interrupt_controller.get())) if (!m_timers->Initialize(this, m_interrupt_controller.get()))
return false; return false;
return true; return true;
@ -89,8 +89,6 @@ bool System::DoState(StateWrapper& sw)
void System::Reset() void System::Reset()
{ {
SetSliceTicks(1);
m_cpu->Reset(); m_cpu->Reset();
m_bus->Reset(); m_bus->Reset();
m_dma->Reset(); m_dma->Reset();
@ -119,12 +117,8 @@ void System::RunFrame()
u32 current_frame_number = m_frame_number; u32 current_frame_number = m_frame_number;
while (current_frame_number == m_frame_number) while (current_frame_number == m_frame_number)
{ {
const TickCount pending_ticks = m_cpu->Execute(); m_cpu->Execute();
Synchronize();
// 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);
} }
} }
@ -215,9 +209,20 @@ bool System::LoadEXE(const char* filename)
return true; return true;
} }
void System::SetSliceTicks(TickCount downcount) void System::Synchronize()
{
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->SetSliceTicks(downcount); m_cpu->SetDowncount(downcount);
} }
void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev) void System::SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev)

View file

@ -42,7 +42,8 @@ public:
bool LoadEXE(const char* filename); bool LoadEXE(const char* filename);
void SetSliceTicks(TickCount downcount); void SetDowncount(TickCount downcount);
void Synchronize();
void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev); void SetPadDevice(u32 slot, std::shared_ptr<PadDevice> dev);

View file

@ -2,14 +2,16 @@
#include "YBaseLib/Log.h" #include "YBaseLib/Log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "system.h"
Log_SetChannel(Timers); Log_SetChannel(Timers);
Timers::Timers() = default; Timers::Timers() = default;
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; m_interrupt_controller = interrupt_controller;
return true; return true;
} }
@ -103,6 +105,16 @@ void Timers::AddTicks(u32 timer, u32 count)
cs.counter = 0; 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) u32 Timers::ReadRegister(u32 offset)
{ {
const u32 timer_index = (offset >> 4) & u32(0x03); const u32 timer_index = (offset >> 4) & u32(0x03);
@ -113,15 +125,20 @@ u32 Timers::ReadRegister(u32 offset)
switch (port_offset) switch (port_offset)
{ {
case 0x00: case 0x00:
{
m_system->Synchronize();
return cs.counter; return cs.counter;
}
case 0x04: case 0x04:
{ {
m_system->Synchronize();
const u32 bits = cs.mode.bits; const u32 bits = cs.mode.bits;
cs.mode.reached_overflow = false; cs.mode.reached_overflow = false;
cs.mode.reached_target = false; cs.mode.reached_target = false;
return bits;
} }
break;
case 0x08: case 0x08:
return cs.target; return cs.target;
@ -142,13 +159,17 @@ void Timers::WriteRegister(u32 offset, u32 value)
switch (port_offset) switch (port_offset)
{ {
case 0x00: case 0x00:
{
Log_DebugPrintf("Timer %u write counter %u", timer_index, value); Log_DebugPrintf("Timer %u write counter %u", timer_index, value);
m_system->Synchronize();
cs.counter = value & u32(0xFFFF); cs.counter = value & u32(0xFFFF);
}
break; break;
case 0x04: case 0x04:
{ {
Log_DebugPrintf("Timer %u write mode register 0x%04X", timer_index, value); Log_DebugPrintf("Timer %u write mode register 0x%04X", timer_index, value);
m_system->Synchronize();
cs.mode.bits = value & u32(0x1FFF); cs.mode.bits = value & u32(0x1FFF);
cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0; cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0;
cs.counter = 0; cs.counter = 0;
@ -157,8 +178,11 @@ void Timers::WriteRegister(u32 offset, u32 value)
break; break;
case 0x08: case 0x08:
{
Log_DebugPrintf("Timer %u write target 0x%04X", timer_index, ZeroExtend32(Truncate16(value))); Log_DebugPrintf("Timer %u write target 0x%04X", timer_index, ZeroExtend32(Truncate16(value)));
m_system->Synchronize();
cs.target = value & u32(0xFFFF); cs.target = value & u32(0xFFFF);
}
break; break;
default: default:

View file

@ -5,6 +5,7 @@
class StateWrapper; class StateWrapper;
class System;
class InterruptController; class InterruptController;
class Timers class Timers
@ -13,7 +14,7 @@ public:
Timers(); Timers();
~Timers(); ~Timers();
bool Initialize(InterruptController* interrupt_controller); bool Initialize(System* system, InterruptController* interrupt_controller);
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -22,6 +23,7 @@ public:
// dot clock/hblank/sysclk div 8 // dot clock/hblank/sysclk div 8
bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; } bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
void AddTicks(u32 timer, u32 ticks); void AddTicks(u32 timer, u32 ticks);
void AddSystemTicks(u32 ticks);
u32 ReadRegister(u32 offset); u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value); void WriteRegister(u32 offset, u32 value);
@ -70,6 +72,7 @@ private:
void UpdateDowncount(); void UpdateDowncount();
u32 GetSystemTicksForTimerTicks(u32 timer) const; u32 GetSystemTicksForTimerTicks(u32 timer) const;
System* m_system = nullptr;
InterruptController* m_interrupt_controller = nullptr; InterruptController* m_interrupt_controller = nullptr;
std::array<CounterState, NUM_TIMERS> m_states{}; std::array<CounterState, NUM_TIMERS> m_states{};

View file

@ -20,5 +20,5 @@ enum class MemoryAccessSize : u32
using TickCount = s32; using TickCount = s32;
static constexpr TickCount MASTER_CLOCK = 44100 * 0x300; // 33868800Hz or 33.8688MHz, also used as CPU clock 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;