mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-04-27 18:15:42 -04:00
Basic timer implementation
This commit is contained in:
parent
ad652c47ed
commit
ad316162f3
@ -10,6 +10,7 @@
|
|||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "pad.h"
|
#include "pad.h"
|
||||||
|
#include "timers.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
Log_SetChannel(Bus);
|
Log_SetChannel(Bus);
|
||||||
|
|
||||||
@ -25,7 +26,8 @@ Bus::Bus() = default;
|
|||||||
|
|
||||||
Bus::~Bus() = default;
|
Bus::~Bus() = default;
|
||||||
|
|
||||||
bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad)
|
bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom,
|
||||||
|
Pad* pad, Timers* timers)
|
||||||
{
|
{
|
||||||
if (!LoadBIOS())
|
if (!LoadBIOS())
|
||||||
return false;
|
return false;
|
||||||
@ -36,6 +38,7 @@ bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_co
|
|||||||
m_gpu = gpu;
|
m_gpu = gpu;
|
||||||
m_cdrom = cdrom;
|
m_cdrom = cdrom;
|
||||||
m_pad = pad;
|
m_pad = pad;
|
||||||
|
m_timers = timers;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +277,20 @@ bool Bus::DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 valu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bus::DoReadTimers(MemoryAccessSize size, u32 offset, u32& value)
|
||||||
|
{
|
||||||
|
FixupUnalignedWordAccessW32(offset, value);
|
||||||
|
value = m_timers->ReadRegister(offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bus::DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value)
|
||||||
|
{
|
||||||
|
FixupUnalignedWordAccessW32(offset, value);
|
||||||
|
m_timers->WriteRegister(offset, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
|
bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
|
||||||
{
|
{
|
||||||
if (offset == 0x1AE)
|
if (offset == 0x1AE)
|
||||||
|
@ -16,6 +16,7 @@ class InterruptController;
|
|||||||
class GPU;
|
class GPU;
|
||||||
class CDROM;
|
class CDROM;
|
||||||
class Pad;
|
class Pad;
|
||||||
|
class Timers;
|
||||||
class System;
|
class System;
|
||||||
|
|
||||||
class Bus
|
class Bus
|
||||||
@ -24,7 +25,7 @@ public:
|
|||||||
Bus();
|
Bus();
|
||||||
~Bus();
|
~Bus();
|
||||||
|
|
||||||
bool Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad);
|
bool Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad, Timers* timers);
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
@ -50,6 +51,9 @@ private:
|
|||||||
static constexpr u32 DMA_BASE = 0x1F801080;
|
static constexpr u32 DMA_BASE = 0x1F801080;
|
||||||
static constexpr u32 DMA_SIZE = 0x80;
|
static constexpr u32 DMA_SIZE = 0x80;
|
||||||
static constexpr u32 DMA_MASK = DMA_SIZE - 1;
|
static constexpr u32 DMA_MASK = DMA_SIZE - 1;
|
||||||
|
static constexpr u32 TIMERS_BASE = 0x1F801100;
|
||||||
|
static constexpr u32 TIMERS_SIZE = 0x40;
|
||||||
|
static constexpr u32 TIMERS_MASK = TIMERS_SIZE - 1;
|
||||||
static constexpr u32 CDROM_BASE = 0x1F801800;
|
static constexpr u32 CDROM_BASE = 0x1F801800;
|
||||||
static constexpr u32 CDROM_SIZE = 0x04;
|
static constexpr u32 CDROM_SIZE = 0x04;
|
||||||
static constexpr u32 CDROM_MASK = CDROM_SIZE - 1;
|
static constexpr u32 CDROM_MASK = CDROM_SIZE - 1;
|
||||||
@ -94,6 +98,9 @@ private:
|
|||||||
bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value);
|
bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
|
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
|
bool DoReadTimers(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
|
bool DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value);
|
bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value);
|
||||||
bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value);
|
bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value);
|
||||||
|
|
||||||
@ -103,6 +110,7 @@ private:
|
|||||||
GPU* m_gpu = nullptr;
|
GPU* m_gpu = nullptr;
|
||||||
CDROM* m_cdrom = nullptr;
|
CDROM* m_cdrom = nullptr;
|
||||||
Pad* m_pad = nullptr;
|
Pad* m_pad = nullptr;
|
||||||
|
Timers* m_timers = nullptr;
|
||||||
|
|
||||||
std::array<u8, 2097152> m_ram{}; // 2MB RAM
|
std::array<u8, 2097152> m_ram{}; // 2MB RAM
|
||||||
std::array<u8, 524288> m_bios{}; // 512K BIOS ROM
|
std::array<u8, 524288> m_bios{}; // 512K BIOS ROM
|
||||||
|
@ -108,6 +108,15 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddres
|
|||||||
return (type == MemoryAccessType::Read) ? DoReadDMA(size, bus_address & DMA_MASK, value) :
|
return (type == MemoryAccessType::Read) ? DoReadDMA(size, bus_address & DMA_MASK, value) :
|
||||||
DoWriteDMA(size, bus_address & DMA_MASK, value);
|
DoWriteDMA(size, bus_address & DMA_MASK, value);
|
||||||
}
|
}
|
||||||
|
else if (bus_address < TIMERS_BASE)
|
||||||
|
{
|
||||||
|
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
||||||
|
}
|
||||||
|
else if (bus_address < (TIMERS_BASE + TIMERS_SIZE))
|
||||||
|
{
|
||||||
|
return (type == MemoryAccessType::Read) ? DoReadTimers(size, bus_address & TIMERS_MASK, value) :
|
||||||
|
DoWriteTimers(size, bus_address & TIMERS_MASK, value);
|
||||||
|
}
|
||||||
else if (bus_address < CDROM_BASE)
|
else if (bus_address < CDROM_BASE)
|
||||||
{
|
{
|
||||||
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
|
||||||
|
@ -304,6 +304,17 @@ void CDROM::ExecuteTestCommand(u8 subcommand)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 0x22:
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Get CDROM region ID string");
|
||||||
|
static constexpr u8 response[] = {'f', 'o', 'r', ' ', 'U', '/', 'C'};
|
||||||
|
m_response_fifo.PushRange(response, countof(response));
|
||||||
|
m_param_fifo.Clear();
|
||||||
|
SetInterrupt(Interrupt::INT3);
|
||||||
|
UpdateStatusRegister();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Unknown test command 0x%02X", subcommand);
|
Log_ErrorPrintf("Unknown test command 0x%02X", subcommand);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "stb_image_write.h"
|
#include "stb_image_write.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "timers.h"
|
||||||
Log_SetChannel(GPU);
|
Log_SetChannel(GPU);
|
||||||
|
|
||||||
bool GPU::DUMP_CPU_TO_VRAM_COPIES = false;
|
bool GPU::DUMP_CPU_TO_VRAM_COPIES = false;
|
||||||
@ -16,11 +17,12 @@ GPU::GPU() = default;
|
|||||||
|
|
||||||
GPU::~GPU() = default;
|
GPU::~GPU() = default;
|
||||||
|
|
||||||
bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller)
|
bool GPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||||
{
|
{
|
||||||
m_system = system;
|
m_system = system;
|
||||||
m_dma = dma;
|
m_dma = dma;
|
||||||
m_interrupt_controller = interrupt_controller;
|
m_interrupt_controller = interrupt_controller;
|
||||||
|
m_timers = timers;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,12 +256,14 @@ 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
|
||||||
// const TickCount ticks_until_next_event = m_crtc_state.ticks_per_scanline - m_crtc_state.current_tick_in_scanline;
|
#if 1
|
||||||
|
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..
|
// or at vblank. this will depend on the timer config..
|
||||||
const TickCount ticks_until_next_event =
|
const TickCount ticks_until_next_event =
|
||||||
((m_crtc_state.total_scanlines_per_frame - m_crtc_state.current_scanline) * m_crtc_state.ticks_per_scanline) -
|
((m_crtc_state.total_scanlines_per_frame - m_crtc_state.current_scanline) * m_crtc_state.ticks_per_scanline) -
|
||||||
m_crtc_state.current_tick_in_scanline;
|
m_crtc_state.current_tick_in_scanline;
|
||||||
|
#endif
|
||||||
|
|
||||||
// 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;
|
||||||
@ -279,15 +283,24 @@ void GPU::Execute(TickCount ticks)
|
|||||||
{
|
{
|
||||||
m_crtc_state.current_tick_in_scanline -= m_crtc_state.ticks_per_scanline;
|
m_crtc_state.current_tick_in_scanline -= m_crtc_state.ticks_per_scanline;
|
||||||
m_crtc_state.current_scanline++;
|
m_crtc_state.current_scanline++;
|
||||||
|
if (m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||||
|
m_timers->AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||||
|
|
||||||
const bool old_vblank = m_crtc_state.in_vblank;
|
const bool old_vblank = m_crtc_state.in_vblank;
|
||||||
m_crtc_state.in_vblank = m_crtc_state.current_scanline >= m_crtc_state.visible_vertical_resolution;
|
const bool new_vblank = m_crtc_state.current_scanline >= m_crtc_state.visible_vertical_resolution;
|
||||||
if (m_crtc_state.in_vblank && !old_vblank)
|
if (new_vblank != old_vblank)
|
||||||
|
{
|
||||||
|
m_crtc_state.in_vblank = new_vblank;
|
||||||
|
|
||||||
|
if (!old_vblank)
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("Now in v-blank");
|
Log_DebugPrintf("Now in v-blank");
|
||||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::VBLANK);
|
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::VBLANK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||||
|
}
|
||||||
|
|
||||||
// past the end of vblank?
|
// past the end of vblank?
|
||||||
if (m_crtc_state.current_scanline >= m_crtc_state.total_scanlines_per_frame)
|
if (m_crtc_state.current_scanline >= m_crtc_state.total_scanlines_per_frame)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/bitfield.h"
|
#include "common/bitfield.h"
|
||||||
|
#include "timers.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
@ -10,6 +11,7 @@ class StateWrapper;
|
|||||||
class System;
|
class System;
|
||||||
class DMA;
|
class DMA;
|
||||||
class InterruptController;
|
class InterruptController;
|
||||||
|
class Timers;
|
||||||
|
|
||||||
class GPU
|
class GPU
|
||||||
{
|
{
|
||||||
@ -17,7 +19,7 @@ public:
|
|||||||
GPU();
|
GPU();
|
||||||
virtual ~GPU();
|
virtual ~GPU();
|
||||||
|
|
||||||
virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller);
|
virtual bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers);
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
virtual bool DoState(StateWrapper& sw);
|
virtual bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
@ -39,6 +41,8 @@ protected:
|
|||||||
static constexpr u32 VRAM_SIZE = VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16);
|
static constexpr u32 VRAM_SIZE = VRAM_WIDTH * VRAM_HEIGHT * sizeof(u16);
|
||||||
static constexpr u32 TEXTURE_PAGE_WIDTH = 256;
|
static constexpr u32 TEXTURE_PAGE_WIDTH = 256;
|
||||||
static constexpr u32 TEXTURE_PAGE_HEIGHT = 256;
|
static constexpr u32 TEXTURE_PAGE_HEIGHT = 256;
|
||||||
|
static constexpr u32 DOT_TIMER_INDEX = 0;
|
||||||
|
static constexpr u32 HBLANK_TIMER_INDEX = 1;
|
||||||
|
|
||||||
static constexpr s32 S11ToS32(u32 value)
|
static constexpr s32 S11ToS32(u32 value)
|
||||||
{
|
{
|
||||||
@ -184,6 +188,7 @@ protected:
|
|||||||
System* m_system = nullptr;
|
System* m_system = nullptr;
|
||||||
DMA* m_dma = nullptr;
|
DMA* m_dma = nullptr;
|
||||||
InterruptController* m_interrupt_controller = nullptr;
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
|
Timers* m_timers = nullptr;
|
||||||
|
|
||||||
union GPUSTAT
|
union GPUSTAT
|
||||||
{
|
{
|
||||||
|
@ -12,9 +12,9 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
|||||||
DestroyFramebuffer();
|
DestroyFramebuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller)
|
bool GPU_HW_OpenGL::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers)
|
||||||
{
|
{
|
||||||
if (!GPU_HW::Initialize(system, dma, interrupt_controller))
|
if (!GPU_HW::Initialize(system, dma, interrupt_controller, timers))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CreateFramebuffer();
|
CreateFramebuffer();
|
||||||
|
@ -13,7 +13,7 @@ public:
|
|||||||
GPU_HW_OpenGL();
|
GPU_HW_OpenGL();
|
||||||
~GPU_HW_OpenGL() override;
|
~GPU_HW_OpenGL() override;
|
||||||
|
|
||||||
bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller) override;
|
bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, Timers* timers) override;
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
<ClCompile Include="pad.cpp" />
|
<ClCompile Include="pad.cpp" />
|
||||||
<ClCompile Include="pad_device.cpp" />
|
<ClCompile Include="pad_device.cpp" />
|
||||||
<ClCompile Include="system.cpp" />
|
<ClCompile Include="system.cpp" />
|
||||||
|
<ClCompile Include="timers.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="bus.h" />
|
<ClInclude Include="bus.h" />
|
||||||
@ -70,6 +71,7 @@
|
|||||||
<ClInclude Include="pad_device.h" />
|
<ClInclude Include="pad_device.h" />
|
||||||
<ClInclude Include="save_state_version.h" />
|
<ClInclude Include="save_state_version.h" />
|
||||||
<ClInclude Include="system.h" />
|
<ClInclude Include="system.h" />
|
||||||
|
<ClInclude Include="timers.h" />
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<ClCompile Include="pad.cpp" />
|
<ClCompile Include="pad.cpp" />
|
||||||
<ClCompile Include="pad_device.cpp" />
|
<ClCompile Include="pad_device.cpp" />
|
||||||
<ClCompile Include="digital_controller.cpp" />
|
<ClCompile Include="digital_controller.cpp" />
|
||||||
|
<ClCompile Include="timers.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
@ -37,6 +38,7 @@
|
|||||||
<ClInclude Include="pad.h" />
|
<ClInclude Include="pad.h" />
|
||||||
<ClInclude Include="pad_device.h" />
|
<ClInclude Include="pad_device.h" />
|
||||||
<ClInclude Include="digital_controller.h" />
|
<ClInclude Include="digital_controller.h" />
|
||||||
|
<ClInclude Include="timers.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
#include "pad.h"
|
#include "pad.h"
|
||||||
#include "pad_device.h"
|
#include "pad_device.h"
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
{
|
{
|
||||||
@ -20,6 +21,7 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
|||||||
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
m_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||||
m_cdrom = std::make_unique<CDROM>();
|
m_cdrom = std::make_unique<CDROM>();
|
||||||
m_pad = std::make_unique<Pad>();
|
m_pad = std::make_unique<Pad>();
|
||||||
|
m_timers = std::make_unique<Timers>();
|
||||||
}
|
}
|
||||||
|
|
||||||
System::~System() = default;
|
System::~System() = default;
|
||||||
@ -30,7 +32,7 @@ bool System::Initialize()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(),
|
if (!m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(),
|
||||||
m_pad.get()))
|
m_pad.get(), m_timers.get()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -41,7 +43,7 @@ bool System::Initialize()
|
|||||||
if (!m_interrupt_controller->Initialize(m_cpu.get()))
|
if (!m_interrupt_controller->Initialize(m_cpu.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get()))
|
if (!m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get()))
|
if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get()))
|
||||||
@ -50,6 +52,9 @@ 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()))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +81,9 @@ bool System::DoState(StateWrapper& sw)
|
|||||||
if (!sw.DoMarker("Pad") || !m_pad->DoState(sw))
|
if (!sw.DoMarker("Pad") || !m_pad->DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!sw.DoMarker("Timers") || !m_timers->DoState(sw))
|
||||||
|
return false;
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +98,7 @@ void System::Reset()
|
|||||||
m_gpu->Reset();
|
m_gpu->Reset();
|
||||||
m_cdrom->Reset();
|
m_cdrom->Reset();
|
||||||
m_pad->Reset();
|
m_pad->Reset();
|
||||||
|
m_timers->Reset();
|
||||||
m_frame_number = 1;
|
m_frame_number = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +122,9 @@ void System::RunFrame()
|
|||||||
const TickCount pending_ticks = m_cpu->Execute();
|
const TickCount pending_ticks = m_cpu->Execute();
|
||||||
|
|
||||||
// run pending ticks from CPU for other components
|
// run pending ticks from CPU for other components
|
||||||
m_gpu->Execute(pending_ticks);
|
m_gpu->Execute(pending_ticks * 3);
|
||||||
|
|
||||||
|
m_timers->AddTicks(2, m_timers->IsUsingExternalClock(2) ? (pending_ticks / 8) : pending_ticks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ class GPU;
|
|||||||
class CDROM;
|
class CDROM;
|
||||||
class Pad;
|
class Pad;
|
||||||
class PadDevice;
|
class PadDevice;
|
||||||
|
class Timers;
|
||||||
|
|
||||||
class System
|
class System
|
||||||
{
|
{
|
||||||
@ -60,5 +61,6 @@ private:
|
|||||||
std::unique_ptr<GPU> m_gpu;
|
std::unique_ptr<GPU> m_gpu;
|
||||||
std::unique_ptr<CDROM> m_cdrom;
|
std::unique_ptr<CDROM> m_cdrom;
|
||||||
std::unique_ptr<Pad> m_pad;
|
std::unique_ptr<Pad> m_pad;
|
||||||
|
std::unique_ptr<Timers> m_timers;
|
||||||
u32 m_frame_number = 1;
|
u32 m_frame_number = 1;
|
||||||
};
|
};
|
||||||
|
203
src/pse/timers.cpp
Normal file
203
src/pse/timers.cpp
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#include "timers.h"
|
||||||
|
#include "YBaseLib/Log.h"
|
||||||
|
#include "common/state_wrapper.h"
|
||||||
|
#include "interrupt_controller.h"
|
||||||
|
Log_SetChannel(Timers);
|
||||||
|
|
||||||
|
Timers::Timers() = default;
|
||||||
|
|
||||||
|
Timers::~Timers() = default;
|
||||||
|
|
||||||
|
bool Timers::Initialize(InterruptController* interrupt_controller)
|
||||||
|
{
|
||||||
|
m_interrupt_controller = interrupt_controller;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timers::Reset()
|
||||||
|
{
|
||||||
|
for (CounterState& cs : m_states)
|
||||||
|
{
|
||||||
|
cs.mode.bits = 0;
|
||||||
|
cs.counter = 0;
|
||||||
|
cs.target = 0;
|
||||||
|
cs.gate = false;
|
||||||
|
cs.external_counting_enabled = false;
|
||||||
|
cs.counting_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
sw.Do(&cs.external_counting_enabled);
|
||||||
|
sw.Do(&cs.counting_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !sw.HasError();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timers::SetGate(u32 timer, bool state)
|
||||||
|
{
|
||||||
|
CounterState& cs = m_states[timer];
|
||||||
|
if (cs.gate == state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cs.gate = state;
|
||||||
|
|
||||||
|
if (cs.mode.sync_enable)
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
switch (cs.mode.sync_mode)
|
||||||
|
{
|
||||||
|
case SyncMode::ResetOnGate:
|
||||||
|
case SyncMode::ResetAndRunOnGate:
|
||||||
|
cs.counter = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SyncMode::FreeRunOnGate:
|
||||||
|
cs.mode.sync_enable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCountingEnabled(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timers::AddTicks(u32 timer, u32 count)
|
||||||
|
{
|
||||||
|
CounterState& cs = m_states[timer];
|
||||||
|
cs.counter += count;
|
||||||
|
|
||||||
|
const u32 reset_value = cs.mode.reset_at_target ? cs.target : u32(0xFFFF);
|
||||||
|
if (cs.counter < reset_value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool old_intr = cs.mode.interrupt_request;
|
||||||
|
|
||||||
|
if (cs.counter >= cs.target)
|
||||||
|
cs.mode.reached_target = true;
|
||||||
|
if (cs.counter >= u32(0xFFFF))
|
||||||
|
cs.mode.reached_overflow = true;
|
||||||
|
|
||||||
|
// TODO: Non-repeat mode.
|
||||||
|
const bool target_intr = cs.mode.reached_target & cs.mode.irq_at_target;
|
||||||
|
const bool overflow_intr = cs.mode.reached_overflow & cs.mode.irq_on_overflow;
|
||||||
|
const bool new_intr = target_intr | overflow_intr;
|
||||||
|
if (!old_intr && new_intr)
|
||||||
|
{
|
||||||
|
m_interrupt_controller->InterruptRequest(
|
||||||
|
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reset_value > 0)
|
||||||
|
cs.counter = cs.counter % reset_value;
|
||||||
|
else
|
||||||
|
cs.counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Timers::ReadRegister(u32 offset)
|
||||||
|
{
|
||||||
|
const u32 timer_index = (offset >> 4) & u32(0x03);
|
||||||
|
const u32 port_offset = offset & u32(0x0F);
|
||||||
|
|
||||||
|
CounterState& cs = m_states[timer_index];
|
||||||
|
|
||||||
|
switch (port_offset)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
return cs.counter;
|
||||||
|
|
||||||
|
case 0x04:
|
||||||
|
{
|
||||||
|
const u32 bits = cs.mode.bits;
|
||||||
|
cs.mode.reached_overflow = false;
|
||||||
|
cs.mode.reached_target = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
CounterState& cs = m_states[timer_index];
|
||||||
|
|
||||||
|
switch (port_offset)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
Log_DebugPrintf("Timer %u write counter %u", timer_index, value);
|
||||||
|
cs.counter = value & u32(0xFFFF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x04:
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Timer %u write mode register 0x%04X", timer_index, value);
|
||||||
|
cs.mode.bits = value & u32(0x1FFF);
|
||||||
|
cs.use_external_clock = (cs.mode.clock_source & (timer_index == 2 ? 2 : 1)) != 0;
|
||||||
|
cs.counter = 0;
|
||||||
|
UpdateCountingEnabled(cs);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x08:
|
||||||
|
Log_DebugPrintf("Timer %u write target 0x%04X", timer_index, ZeroExtend32(Truncate16(value)));
|
||||||
|
cs.target = value & u32(0xFFFF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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:
|
||||||
|
case SyncMode::FreeRunOnGate:
|
||||||
|
cs.counting_enabled = !cs.gate;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SyncMode::ResetOnGate:
|
||||||
|
cs.counting_enabled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SyncMode::ResetAndRunOnGate:
|
||||||
|
cs.counting_enabled = cs.gate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cs.counting_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.external_counting_enabled = cs.use_external_clock && cs.counting_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timers::UpdateDowncount() {}
|
||||||
|
|
||||||
|
u32 Timers::GetSystemTicksForTimerTicks(u32 timer) const
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
76
src/pse/timers.h
Normal file
76
src/pse/timers.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/bitfield.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
class StateWrapper;
|
||||||
|
|
||||||
|
class InterruptController;
|
||||||
|
|
||||||
|
class Timers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Timers();
|
||||||
|
~Timers();
|
||||||
|
|
||||||
|
bool Initialize(InterruptController* interrupt_controller);
|
||||||
|
void Reset();
|
||||||
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
void SetGate(u32 timer, bool state);
|
||||||
|
|
||||||
|
// dot clock/hblank/sysclk div 8
|
||||||
|
bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
||||||
|
void AddTicks(u32 timer, u32 ticks);
|
||||||
|
|
||||||
|
u32 ReadRegister(u32 offset);
|
||||||
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr u32 NUM_TIMERS = 3;
|
||||||
|
|
||||||
|
enum class SyncMode : u8
|
||||||
|
{
|
||||||
|
PauseOnGate = 0,
|
||||||
|
ResetOnGate = 1,
|
||||||
|
ResetAndRunOnGate = 2,
|
||||||
|
FreeRunOnGate = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
union CounterMode
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u32, bool, 0, 1> sync_enable;
|
||||||
|
BitField<u32, SyncMode, 1, 2> sync_mode;
|
||||||
|
BitField<u32, bool, 3, 1> reset_at_target;
|
||||||
|
BitField<u32, bool, 4, 1> irq_at_target;
|
||||||
|
BitField<u32, bool, 5, 1> irq_on_overflow;
|
||||||
|
BitField<u32, bool, 6, 1> irq_repeat;
|
||||||
|
BitField<u32, bool, 7, 1> irq_pulse;
|
||||||
|
BitField<u32, u8, 8, 2> clock_source;
|
||||||
|
BitField<u32, bool, 10, 1> interrupt_request;
|
||||||
|
BitField<u32, bool, 11, 1> reached_target;
|
||||||
|
BitField<u32, bool, 12, 1> reached_overflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CounterState
|
||||||
|
{
|
||||||
|
CounterMode mode;
|
||||||
|
u32 counter;
|
||||||
|
u32 target;
|
||||||
|
bool gate;
|
||||||
|
bool use_external_clock;
|
||||||
|
bool external_counting_enabled;
|
||||||
|
bool counting_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
void UpdateCountingEnabled(CounterState& cs);
|
||||||
|
|
||||||
|
void UpdateDowncount();
|
||||||
|
u32 GetSystemTicksForTimerTicks(u32 timer) const;
|
||||||
|
|
||||||
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
|
|
||||||
|
std::array<CounterState, NUM_TIMERS> m_states{};
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user