DMA: Run manual channels while halted

Fixes games which have looping linked lists but still expect CD/OTC
reads to work.

Also caps the number of ticks used when looping linked lists are
present, which doesn't steal so much time from the CPU per batch.

Fixes:
 - Victory Spike
 - Magical Drop III - Yokubari Tokudai-gou!
 - Yuukyuu no Eden - The Eternal Eden
 - Loading screen in World Cup Golf - Professional Edition
This commit is contained in:
Connor McLaughlin 2020-05-31 02:42:12 +10:00
parent 2d067bb101
commit e293c22cde

View file

@ -154,7 +154,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
if (static_cast<Channel>(channel_index) == Channel::OTC) if (static_cast<Channel>(channel_index) == Channel::OTC)
SetRequest(static_cast<Channel>(channel_index), state.channel_control.start_trigger); SetRequest(static_cast<Channel>(channel_index), state.channel_control.start_trigger);
if (!IsTransferHalted() && CanTransferChannel(static_cast<Channel>(channel_index))) if (CanTransferChannel(static_cast<Channel>(channel_index)))
TransferChannel(static_cast<Channel>(channel_index)); TransferChannel(static_cast<Channel>(channel_index));
return; return;
} }
@ -171,8 +171,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;
if (!IsTransferHalted())
{
for (u32 i = 0; i < NUM_CHANNELS; i++) for (u32 i = 0; i < NUM_CHANNELS; i++)
{ {
if (CanTransferChannel(static_cast<Channel>(i))) if (CanTransferChannel(static_cast<Channel>(i)))
@ -181,7 +180,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
break; break;
} }
} }
}
return; return;
} }
@ -209,7 +208,7 @@ void DMA::SetRequest(Channel channel, bool request)
return; return;
cs.request = request; cs.request = request;
if (!IsTransferHalted() && CanTransferChannel(channel)) if (CanTransferChannel(channel))
TransferChannel(channel); TransferChannel(channel);
} }
@ -222,6 +221,9 @@ bool DMA::CanTransferChannel(Channel channel) const
if (!cs.channel_control.enable_busy) if (!cs.channel_control.enable_busy)
return false; return false;
if (cs.channel_control.sync_mode != SyncMode::Manual && IsTransferHalted())
return false;
return cs.request; return cs.request;
} }
@ -275,14 +277,15 @@ bool DMA::TransferChannel(Channel channel)
if (!copy_to_device) if (!copy_to_device)
{ {
Panic("Linked list not implemented for DMA reads"); Panic("Linked list not implemented for DMA reads");
return true;
} }
else
{
Log_DebugPrintf("DMA%u: Copying linked list starting at 0x%08X to device", static_cast<u32>(channel), Log_DebugPrintf("DMA%u: Copying linked list starting at 0x%08X to device", static_cast<u32>(channel),
current_address & ADDRESS_MASK); current_address & ADDRESS_MASK);
u8* ram_pointer = m_bus->GetRAM(); u8* ram_pointer = m_bus->GetRAM();
while (cs.request && used_ticks < m_max_slice_ticks) bool halt_transfer = false;
while (cs.request)
{ {
u32 header; u32 header;
std::memcpy(&header, &ram_pointer[current_address & ADDRESS_MASK], sizeof(header)); std::memcpy(&header, &ram_pointer[current_address & ADDRESS_MASK], sizeof(header));
@ -290,33 +293,45 @@ bool DMA::TransferChannel(Channel channel)
const u32 word_count = header >> 24; const u32 word_count = header >> 24;
const u32 next_address = header & UINT32_C(0x00FFFFFF); const u32 next_address = header & UINT32_C(0x00FFFFFF);
Log_TracePrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", Log_TracePrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", current_address & ADDRESS_MASK,
current_address & ADDRESS_MASK, word_count * UINT32_C(4), word_count, next_address); word_count * UINT32_C(4), word_count, next_address);
if (word_count > 0) if (word_count > 0)
{ {
used_ticks += used_ticks +=
TransferMemoryToDevice(channel, (current_address + sizeof(header)) & ADDRESS_MASK, 4, word_count); TransferMemoryToDevice(channel, (current_address + sizeof(header)) & ADDRESS_MASK, 4, word_count);
} }
else if ((current_address & ADDRESS_MASK) == (next_address & ADDRESS_MASK))
{
current_address = next_address;
halt_transfer = true;
break;
}
current_address = next_address; current_address = next_address;
if (current_address & UINT32_C(0x800000)) if (current_address & UINT32_C(0x800000))
break; break;
if (used_ticks >= m_max_slice_ticks)
{
halt_transfer = true;
break;
} }
} }
cs.base_address = current_address; cs.base_address = current_address;
m_system->StallCPU(used_ticks); m_system->StallCPU(used_ticks);
if ((current_address & UINT32_C(0x800000)) == 0) if (current_address & UINT32_C(0x800000))
{ break;
if (used_ticks >= m_max_slice_ticks && cs.request)
if (halt_transfer)
{ {
// stall the transfer for a bit if we ran for too long // stall the transfer for a bit if we ran for too long
// Log_WarningPrintf("breaking dma chain at 0x%08X", current_address);
HaltTransfer(m_halt_ticks); HaltTransfer(m_halt_ticks);
return false; return false;
} }
else
{
// linked list not yet complete // linked list not yet complete
return true; return true;
} }