mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 06:25:37 +00:00
DMA: Support delaying transfers
Fixes Syphon Filter 2/3.
This commit is contained in:
parent
e02ebb1b2a
commit
30fd7a6683
106
src/core/dma.cpp
106
src/core/dma.cpp
|
@ -48,6 +48,18 @@ bool DMA::DoState(StateWrapper& sw)
|
||||||
|
|
||||||
sw.Do(&m_DPCR.bits);
|
sw.Do(&m_DPCR.bits);
|
||||||
sw.Do(&m_DICR.bits);
|
sw.Do(&m_DICR.bits);
|
||||||
|
|
||||||
|
if (sw.IsReading())
|
||||||
|
{
|
||||||
|
m_transfer_min_ticks = std::numeric_limits<TickCount>::max();
|
||||||
|
for (const ChannelState& cs : m_state)
|
||||||
|
{
|
||||||
|
if (cs.transfer_ticks > 0)
|
||||||
|
m_transfer_min_ticks = std::min(m_transfer_min_ticks, cs.transfer_ticks);
|
||||||
|
}
|
||||||
|
m_system->SetDowncount(m_transfer_min_ticks);
|
||||||
|
}
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +125,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DMA channel %u block control <- 0x%08X", channel_index, value);
|
Log_TracePrintf("DMA channel %u block control <- 0x%08X", channel_index, value);
|
||||||
state.block_control.bits = value;
|
state.block_control.bits = value;
|
||||||
Transfer();
|
QueueTransferChannel(static_cast<Channel>(channel_index));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +134,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
|
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
|
||||||
(value & ChannelState::ChannelControl::WRITE_MASK);
|
(value & ChannelState::ChannelControl::WRITE_MASK);
|
||||||
Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
|
Log_TracePrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
|
||||||
Transfer();
|
QueueTransferChannel(static_cast<Channel>(channel_index));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +150,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DPCR <- 0x%08X", value);
|
Log_TracePrintf("DPCR <- 0x%08X", value);
|
||||||
m_DPCR.bits = value;
|
m_DPCR.bits = value;
|
||||||
Transfer();
|
QueueTransfer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +179,26 @@ void DMA::SetRequest(Channel channel, bool request)
|
||||||
|
|
||||||
cs.request = request;
|
cs.request = request;
|
||||||
if (request)
|
if (request)
|
||||||
Transfer();
|
QueueTransfer();
|
||||||
|
}
|
||||||
|
|
||||||
|
TickCount DMA::GetTransferDelay(Channel channel) const
|
||||||
|
{
|
||||||
|
const ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||||
|
switch (channel)
|
||||||
|
{
|
||||||
|
case Channel::SPU:
|
||||||
|
{
|
||||||
|
if (cs.channel_control.sync_mode == SyncMode::Request)
|
||||||
|
return (cs.block_control.request.GetBlockCount() * (cs.block_control.request.GetBlockSize() / 2));
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DMA::CanTransferChannel(Channel channel) const
|
bool DMA::CanTransferChannel(Channel channel) const
|
||||||
|
@ -185,6 +216,9 @@ bool DMA::CanTransferChannel(Channel channel) const
|
||||||
if (cs.channel_control.sync_mode == SyncMode::Manual && !cs.channel_control.start_trigger)
|
if (cs.channel_control.sync_mode == SyncMode::Manual && !cs.channel_control.start_trigger)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (cs.transfer_ticks > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,33 +243,66 @@ void DMA::UpdateIRQ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::Transfer()
|
void DMA::QueueTransferChannel(Channel channel)
|
||||||
{
|
{
|
||||||
if (m_transfer_in_progress)
|
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||||
|
if (cs.transfer_ticks > 0 || !CanTransferChannel(channel))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// prevent recursive calls
|
const TickCount ticks = GetTransferDelay(channel);
|
||||||
|
if (ticks == 0)
|
||||||
|
{
|
||||||
|
// immediate transfer
|
||||||
|
TransferChannel(channel);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
m_transfer_in_progress = true;
|
||||||
|
|
||||||
// keep going until all transfers are done. one channel can start others (e.g. MDEC)
|
// keep going until all transfers are done. one channel can start others (e.g. MDEC)
|
||||||
for (;;)
|
m_transfer_min_ticks = std::numeric_limits<TickCount>::max();
|
||||||
|
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||||
{
|
{
|
||||||
bool any_channels_active = false;
|
const Channel channel = static_cast<Channel>(i);
|
||||||
|
if (m_state[i].transfer_ticks <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
m_state[i].transfer_ticks -= ticks;
|
||||||
|
if (CanTransferChannel(channel))
|
||||||
{
|
{
|
||||||
const Channel channel = static_cast<Channel>(i);
|
TransferChannel(channel);
|
||||||
if (CanTransferChannel(channel))
|
}
|
||||||
{
|
else
|
||||||
TransferChannel(channel);
|
{
|
||||||
any_channels_active = true;
|
m_transfer_min_ticks = std::min(m_transfer_min_ticks, ticks);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!any_channels_active)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_system->SetDowncount(m_transfer_min_ticks);
|
||||||
m_transfer_in_progress = false;
|
m_transfer_in_progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,6 +415,7 @@ void DMA::TransferChannel(Channel channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start/busy bit is cleared on end of transfer
|
// start/busy bit is cleared on end of transfer
|
||||||
|
cs.transfer_ticks = 0;
|
||||||
cs.channel_control.enable_busy = false;
|
cs.channel_control.enable_busy = false;
|
||||||
if (m_DICR.IsIRQEnabled(channel))
|
if (m_DICR.IsIRQEnabled(channel))
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
// changing interfaces
|
// changing interfaces
|
||||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||||
|
|
||||||
|
void Execute(TickCount ticks);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF);
|
static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF);
|
||||||
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC);
|
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC);
|
||||||
|
@ -61,12 +63,17 @@ private:
|
||||||
Reserved = 3
|
Reserved = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns the number of ticks for a given channel's transfer.
|
||||||
|
TickCount GetTransferDelay(Channel channel) const;
|
||||||
|
|
||||||
// is everything enabled for a channel to operate?
|
// is everything enabled for a channel to operate?
|
||||||
bool CanTransferChannel(Channel channel) const;
|
bool CanTransferChannel(Channel channel) const;
|
||||||
bool CanRunAnyChannels() const;
|
bool CanRunAnyChannels() const;
|
||||||
void UpdateIRQ();
|
void UpdateIRQ();
|
||||||
|
|
||||||
void Transfer();
|
void QueueTransferChannel(Channel channel);
|
||||||
|
void QueueTransfer();
|
||||||
|
|
||||||
void TransferChannel(Channel channel);
|
void TransferChannel(Channel channel);
|
||||||
|
|
||||||
// from device -> memory
|
// from device -> memory
|
||||||
|
@ -84,7 +91,6 @@ private:
|
||||||
MDEC* m_mdec = nullptr;
|
MDEC* m_mdec = nullptr;
|
||||||
|
|
||||||
std::vector<u32> m_transfer_buffer;
|
std::vector<u32> m_transfer_buffer;
|
||||||
bool m_transfer_in_progress = false;
|
|
||||||
|
|
||||||
struct ChannelState
|
struct ChannelState
|
||||||
{
|
{
|
||||||
|
@ -124,6 +130,7 @@ private:
|
||||||
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
|
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
|
||||||
} channel_control;
|
} channel_control;
|
||||||
|
|
||||||
|
TickCount transfer_ticks = 0;
|
||||||
bool request = false;
|
bool request = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -199,4 +206,7 @@ private:
|
||||||
master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0);
|
master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0);
|
||||||
}
|
}
|
||||||
} m_DICR = {};
|
} m_DICR = {};
|
||||||
|
|
||||||
|
TickCount m_transfer_min_ticks = 0;
|
||||||
|
bool m_transfer_in_progress = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -367,6 +367,9 @@ bool System::SetExpansionROM(const char* filename)
|
||||||
void System::Synchronize()
|
void System::Synchronize()
|
||||||
{
|
{
|
||||||
const TickCount pending_ticks = m_cpu->GetPendingTicks();
|
const TickCount pending_ticks = m_cpu->GetPendingTicks();
|
||||||
|
if (pending_ticks == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
m_cpu->ResetPendingTicks();
|
m_cpu->ResetPendingTicks();
|
||||||
m_cpu->ResetDowncount();
|
m_cpu->ResetDowncount();
|
||||||
|
|
||||||
|
@ -378,6 +381,7 @@ void System::Synchronize()
|
||||||
m_pad->Execute(pending_ticks);
|
m_pad->Execute(pending_ticks);
|
||||||
m_spu->Execute(pending_ticks);
|
m_spu->Execute(pending_ticks);
|
||||||
m_mdec->Execute(pending_ticks);
|
m_mdec->Execute(pending_ticks);
|
||||||
|
m_dma->Execute(pending_ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetDowncount(TickCount downcount)
|
void System::SetDowncount(TickCount downcount)
|
||||||
|
|
Loading…
Reference in a new issue