DMA: Delay transfer/interrupt

This commit is contained in:
Connor McLaughlin 2019-09-24 21:38:58 +10:00
parent 4cc83e2228
commit 4bb8fb211d
3 changed files with 83 additions and 7 deletions

View File

@ -5,14 +5,16 @@
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "gpu.h" #include "gpu.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "system.h"
Log_SetChannel(DMA); Log_SetChannel(DMA);
DMA::DMA() = default; DMA::DMA() = default;
DMA::~DMA() = default; DMA::~DMA() = default;
bool DMA::Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom) bool DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom)
{ {
m_system = system;
m_bus = bus; m_bus = bus;
m_interrupt_controller = interrupt_controller; m_interrupt_controller = interrupt_controller;
m_gpu = gpu; m_gpu = gpu;
@ -22,6 +24,8 @@ bool DMA::Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* g
void DMA::Reset() void DMA::Reset()
{ {
m_transfer_ticks = 0;
m_transfer_pending = false;
m_state = {}; m_state = {};
m_DPCR.bits = 0x07654321; m_DPCR.bits = 0x07654321;
m_DICR.bits = 0; m_DICR.bits = 0;
@ -99,7 +103,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
(value & ChannelState::ChannelControl::WRITE_MASK); (value & ChannelState::ChannelControl::WRITE_MASK);
Log_DebugPrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits); Log_DebugPrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
if (CanRunChannel(static_cast<Channel>(channel_index))) if (CanRunChannel(static_cast<Channel>(channel_index)))
RunDMA(static_cast<Channel>(channel_index)); UpdateTransferPending();
return; return;
} }
@ -143,8 +147,39 @@ void DMA::SetRequest(Channel channel, bool request)
return; return;
cs.request = request; cs.request = request;
if (CanRunChannel(channel)) UpdateTransferPending();
RunDMA(channel); }
void DMA::Execute(TickCount ticks)
{
if (!m_transfer_pending)
return;
m_transfer_ticks -= ticks;
if (m_transfer_ticks <= 0)
{
m_transfer_pending = false;
for (u32 i = 0; i < NUM_CHANNELS; i++)
{
const Channel channel = static_cast<Channel>(i);
if (CanRunChannel(channel))
{
RunDMA(channel);
m_transfer_pending |= CanRunChannel(channel);
}
}
if (m_transfer_pending)
{
m_transfer_ticks += TRANSFER_TICKS;
m_system->SetDowncount(m_transfer_ticks);
}
}
else
{
m_system->SetDowncount(m_transfer_ticks);
}
} }
bool DMA::CanRunChannel(Channel channel) const bool DMA::CanRunChannel(Channel channel) const
@ -159,6 +194,17 @@ bool DMA::CanRunChannel(Channel channel) const
return (cs.channel_control.enable_busy && cs.request); return (cs.channel_control.enable_busy && cs.request);
} }
bool DMA::CanRunAnyChannels() const
{
for (u32 i = 0; i < NUM_CHANNELS; i++)
{
if (CanRunChannel(static_cast<Channel>(i)))
return true;
}
return false;
}
void DMA::RunDMA(Channel channel) void DMA::RunDMA(Channel channel)
{ {
ChannelState& cs = m_state[static_cast<u32>(channel)]; ChannelState& cs = m_state[static_cast<u32>(channel)];
@ -353,3 +399,22 @@ void DMA::DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address
break; break;
} }
} }
void DMA::UpdateTransferPending()
{
if (CanRunAnyChannels())
{
if (m_transfer_pending)
return;
m_system->Synchronize();
m_transfer_pending = true;
m_transfer_ticks = TRANSFER_TICKS;
m_system->SetDowncount(m_transfer_ticks);
}
else
{
m_transfer_pending = false;
m_transfer_ticks = 0;
}
}

View File

@ -5,6 +5,7 @@
class StateWrapper; class StateWrapper;
class System;
class Bus; class Bus;
class InterruptController; class InterruptController;
class GPU; class GPU;
@ -32,7 +33,7 @@ public:
DMA(); DMA();
~DMA(); ~DMA();
bool Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom); bool Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom);
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -41,8 +42,11 @@ public:
void SetRequest(Channel channel, bool request); void SetRequest(Channel channel, bool request);
void Execute(TickCount ticks);
private: private:
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF); static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF);
static constexpr u32 TRANSFER_TICKS = 10;
enum class SyncMode : u32 enum class SyncMode : u32
{ {
@ -54,6 +58,7 @@ private:
// is everything enabled for a channel to operate? // is everything enabled for a channel to operate?
bool CanRunChannel(Channel channel) const; bool CanRunChannel(Channel channel) const;
bool CanRunAnyChannels() const;
void RunDMA(Channel channel); void RunDMA(Channel channel);
@ -63,11 +68,17 @@ private:
// from memory -> device // from memory -> device
void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words); void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words);
void UpdateTransferPending();
System* m_system = nullptr;
Bus* m_bus = nullptr; Bus* m_bus = nullptr;
InterruptController* m_interrupt_controller = nullptr; InterruptController* m_interrupt_controller = nullptr;
GPU* m_gpu = nullptr; GPU* m_gpu = nullptr;
CDROM* m_cdrom = nullptr; CDROM* m_cdrom = nullptr;
TickCount m_transfer_ticks = 0;
bool m_transfer_pending = false;
struct ChannelState struct ChannelState
{ {
u32 base_address; u32 base_address;
@ -107,7 +118,6 @@ private:
} channel_control; } channel_control;
bool request = false; bool request = false;
bool irq = false;
}; };
std::array<ChannelState, NUM_CHANNELS> m_state = {}; std::array<ChannelState, NUM_CHANNELS> m_state = {};

View File

@ -39,7 +39,7 @@ bool System::Initialize()
return false; return false;
} }
if (!m_dma->Initialize(m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get())) if (!m_dma->Initialize(this, m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get()))
return false; return false;
if (!m_interrupt_controller->Initialize(m_cpu.get())) if (!m_interrupt_controller->Initialize(m_cpu.get()))
@ -246,6 +246,7 @@ void System::Synchronize()
const TickCount pending_ticks = m_cpu->GetPendingTicks(); const TickCount pending_ticks = m_cpu->GetPendingTicks();
m_cpu->ResetPendingTicks(); m_cpu->ResetPendingTicks();
m_dma->Execute(pending_ticks);
m_gpu->Execute(pending_ticks); m_gpu->Execute(pending_ticks);
m_timers->AddSystemTicks(pending_ticks); m_timers->AddSystemTicks(pending_ticks);
m_cdrom->Execute(pending_ticks); m_cdrom->Execute(pending_ticks);