mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-19 08:25:47 -04:00
Implement event-based scheduler instead of lock-step components
This commit is contained in:
113
src/core/dma.cpp
113
src/core/dma.cpp
@ -1,8 +1,9 @@
|
||||
#include "dma.h"
|
||||
#include "common/log.h"
|
||||
#include "bus.h"
|
||||
#include "cdrom.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string_util.h"
|
||||
#include "gpu.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "mdec.h"
|
||||
@ -25,14 +26,28 @@ void DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_co
|
||||
m_spu = spu;
|
||||
m_mdec = mdec;
|
||||
m_transfer_buffer.resize(32);
|
||||
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
m_state[i].transfer_event = system->CreateTimingEvent(
|
||||
StringUtil::StdStringFromFormat("DMA%u Transfer", i), 1, 1,
|
||||
std::bind(&DMA::TransferChannel, this, static_cast<Channel>(i), std::placeholders::_2), false);
|
||||
}
|
||||
}
|
||||
|
||||
void DMA::Reset()
|
||||
{
|
||||
m_transfer_in_progress = false;
|
||||
std::memset(&m_state, 0, sizeof(m_state));
|
||||
m_DPCR.bits = 0x07654321;
|
||||
m_DICR.bits = 0;
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
ChannelState& cs = m_state[i];
|
||||
cs.base_address = 0;
|
||||
cs.block_control.bits = 0;
|
||||
cs.channel_control.bits = 0;
|
||||
cs.request = false;
|
||||
cs.transfer_event->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
bool DMA::DoState(StateWrapper& sw)
|
||||
@ -43,7 +58,6 @@ bool DMA::DoState(StateWrapper& sw)
|
||||
sw.Do(&cs.base_address);
|
||||
sw.Do(&cs.block_control.bits);
|
||||
sw.Do(&cs.channel_control.bits);
|
||||
sw.Do(&cs.transfer_ticks);
|
||||
sw.Do(&cs.request);
|
||||
}
|
||||
|
||||
@ -52,13 +66,11 @@ bool DMA::DoState(StateWrapper& sw)
|
||||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
m_transfer_min_ticks = std::numeric_limits<TickCount>::max();
|
||||
for (const ChannelState& cs : m_state)
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
if (cs.transfer_ticks > 0)
|
||||
m_transfer_min_ticks = std::min(m_transfer_min_ticks, cs.transfer_ticks);
|
||||
m_state[i].transfer_event->Deactivate();
|
||||
UpdateChannelTransferEvent(static_cast<Channel>(i));
|
||||
}
|
||||
m_system->SetDowncount(m_transfer_min_ticks);
|
||||
}
|
||||
|
||||
return !sw.HasError();
|
||||
@ -126,7 +138,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||
{
|
||||
Log_TracePrintf("DMA channel %u block control <- 0x%08X", channel_index, value);
|
||||
state.block_control.bits = value;
|
||||
QueueTransferChannel(static_cast<Channel>(channel_index));
|
||||
UpdateChannelTransferEvent(static_cast<Channel>(channel_index));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,7 +147,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
|
||||
(value & ChannelState::ChannelControl::WRITE_MASK);
|
||||
Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
|
||||
QueueTransferChannel(static_cast<Channel>(channel_index));
|
||||
UpdateChannelTransferEvent(static_cast<Channel>(channel_index));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -151,7 +163,8 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||
{
|
||||
Log_TracePrintf("DPCR <- 0x%08X", value);
|
||||
m_DPCR.bits = value;
|
||||
QueueTransfer();
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
UpdateChannelTransferEvent(static_cast<Channel>(i));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -180,7 +193,7 @@ void DMA::SetRequest(Channel channel, bool request)
|
||||
|
||||
cs.request = request;
|
||||
if (request)
|
||||
QueueTransfer();
|
||||
UpdateChannelTransferEvent(channel);
|
||||
}
|
||||
|
||||
TickCount DMA::GetTransferDelay(Channel channel) const
|
||||
@ -188,6 +201,10 @@ TickCount DMA::GetTransferDelay(Channel channel) const
|
||||
const ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||
switch (channel)
|
||||
{
|
||||
case Channel::MDECin:
|
||||
case Channel::MDECout:
|
||||
return 1;
|
||||
|
||||
case Channel::SPU:
|
||||
{
|
||||
if (cs.channel_control.sync_mode == SyncMode::Request)
|
||||
@ -198,7 +215,7 @@ TickCount DMA::GetTransferDelay(Channel channel) const
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,9 +234,6 @@ bool DMA::CanTransferChannel(Channel channel) const
|
||||
if (cs.channel_control.sync_mode == SyncMode::Manual && !cs.channel_control.start_trigger)
|
||||
return false;
|
||||
|
||||
if (cs.transfer_ticks > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -244,72 +258,34 @@ void DMA::UpdateIRQ()
|
||||
}
|
||||
}
|
||||
|
||||
void DMA::QueueTransferChannel(Channel channel)
|
||||
void DMA::UpdateChannelTransferEvent(Channel channel)
|
||||
{
|
||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||
if (cs.transfer_ticks > 0 || !CanTransferChannel(channel))
|
||||
if (!CanTransferChannel(channel))
|
||||
{
|
||||
cs.transfer_event->Deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cs.transfer_event->IsActive())
|
||||
return;
|
||||
|
||||
const TickCount ticks = GetTransferDelay(channel);
|
||||
if (ticks == 0)
|
||||
{
|
||||
// immediate transfer
|
||||
TransferChannel(channel);
|
||||
TransferChannel(channel, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_transfer_in_progress)
|
||||
m_system->Synchronize();
|
||||
|
||||
cs.transfer_ticks = ticks;
|
||||
m_transfer_min_ticks = std::min(m_transfer_min_ticks, ticks);
|
||||
m_system->SetDowncount(ticks);
|
||||
cs.transfer_event->SetPeriodAndSchedule(ticks);
|
||||
}
|
||||
|
||||
void DMA::QueueTransfer()
|
||||
{
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
QueueTransferChannel(static_cast<Channel>(i));
|
||||
}
|
||||
|
||||
void DMA::Execute(TickCount ticks)
|
||||
{
|
||||
m_transfer_min_ticks -= ticks;
|
||||
if (m_transfer_min_ticks > 0)
|
||||
{
|
||||
m_system->SetDowncount(m_transfer_min_ticks);
|
||||
return;
|
||||
}
|
||||
|
||||
DebugAssert(!m_transfer_in_progress);
|
||||
m_transfer_in_progress = true;
|
||||
|
||||
// keep going until all transfers are done. one channel can start others (e.g. MDEC)
|
||||
m_transfer_min_ticks = std::numeric_limits<TickCount>::max();
|
||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
const Channel channel = static_cast<Channel>(i);
|
||||
if (m_state[i].transfer_ticks <= 0)
|
||||
continue;
|
||||
|
||||
m_state[i].transfer_ticks -= ticks;
|
||||
if (CanTransferChannel(channel))
|
||||
{
|
||||
TransferChannel(channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_transfer_min_ticks = std::min(m_transfer_min_ticks, ticks);
|
||||
}
|
||||
}
|
||||
|
||||
m_system->SetDowncount(m_transfer_min_ticks);
|
||||
m_transfer_in_progress = false;
|
||||
}
|
||||
|
||||
void DMA::TransferChannel(Channel channel)
|
||||
void DMA::TransferChannel(Channel channel, TickCount ticks_late)
|
||||
{
|
||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||
cs.transfer_event->Deactivate();
|
||||
|
||||
const bool copy_to_device = cs.channel_control.copy_to_device;
|
||||
|
||||
// start/trigger bit is cleared on beginning of transfer
|
||||
@ -416,7 +392,6 @@ void DMA::TransferChannel(Channel channel)
|
||||
}
|
||||
|
||||
// start/busy bit is cleared on end of transfer
|
||||
cs.transfer_ticks = 0;
|
||||
cs.channel_control.enable_busy = false;
|
||||
if (m_DICR.IsIRQEnabled(channel))
|
||||
{
|
||||
|
Reference in New Issue
Block a user