SPU: Drain transfer FIFO when cancelling transfer

The busy bit got stuck on otherwise, which broke the Spanish translation
of Vagrant Story.
This commit is contained in:
Connor McLaughlin 2021-01-18 20:43:09 +10:00
parent 699d599d00
commit 914b9bf738
2 changed files with 56 additions and 35 deletions

View file

@ -453,7 +453,8 @@ void SPU::WriteRegister(u32 offset, u16 value)
m_transfer_address = ZeroExtend32(value) * 8; m_transfer_address = ZeroExtend32(value) * 8;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address)) if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{ {
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer address reg set", m_transfer_address, m_transfer_address / 8); Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer address reg set", m_transfer_address,
m_transfer_address / 8);
TriggerRAMIRQ(); TriggerRAMIRQ();
} }
return; return;
@ -481,11 +482,21 @@ void SPU::WriteRegister(u32 offset, u16 value)
if (!m_transfer_fifo.IsEmpty()) if (!m_transfer_fifo.IsEmpty())
{ {
if (m_SPUCNT.ram_transfer_mode == RAMTransferMode::DMAWrite) if (m_SPUCNT.ram_transfer_mode == RAMTransferMode::DMAWrite)
Log_WarningPrintf("Clearing SPU transfer FIFO with %u bytes left", m_transfer_fifo.GetSize()); {
// I would guess on the console it would gradually write the FIFO out. Hopefully nothing relies on this
// level of timing granularity if we force it all out here.
Log_WarningPrintf("Draining write SPU transfer FIFO with %u bytes left", m_transfer_fifo.GetSize());
TickCount ticks = std::numeric_limits<TickCount>::max();
ExecuteFIFOWriteToRAM(ticks);
DebugAssert(m_transfer_fifo.IsEmpty());
}
else
{
Log_DebugPrintf("Clearing read SPU transfer FIFO with %u bytes left", m_transfer_fifo.GetSize());
m_transfer_fifo.Clear(); m_transfer_fifo.Clear();
} }
} }
}
if (!new_value.enable && m_SPUCNT.enable) if (!new_value.enable && m_SPUCNT.enable)
{ {
@ -744,14 +755,7 @@ void SPU::IncrementCaptureBufferPosition()
m_SPUSTAT.second_half_capture_buffer = m_capture_buffer_position >= (CAPTURE_BUFFER_SIZE_PER_CHANNEL / 2); m_SPUSTAT.second_half_capture_buffer = m_capture_buffer_position >= (CAPTURE_BUFFER_SIZE_PER_CHANNEL / 2);
} }
void SPU::ExecuteTransfer(TickCount ticks) void ALWAYS_INLINE SPU::ExecuteFIFOReadFromRAM(TickCount& ticks)
{
const RAMTransferMode mode = m_SPUCNT.ram_transfer_mode;
Assert(mode != RAMTransferMode::Stopped);
if (mode == RAMTransferMode::DMARead)
{
while (ticks > 0 && !m_transfer_fifo.IsFull())
{ {
while (ticks > 0 && !m_transfer_fifo.IsFull()) while (ticks > 0 && !m_transfer_fifo.IsFull())
{ {
@ -767,6 +771,35 @@ void SPU::ExecuteTransfer(TickCount ticks)
TriggerRAMIRQ(); TriggerRAMIRQ();
} }
} }
}
void ALWAYS_INLINE SPU::ExecuteFIFOWriteToRAM(TickCount& ticks)
{
while (ticks > 0 && !m_transfer_fifo.IsEmpty())
{
u16 value = m_transfer_fifo.Pop();
std::memcpy(&m_ram[m_transfer_address], &value, sizeof(u16));
m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK;
ticks -= TRANSFER_TICKS_PER_HALFWORD;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer write", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
}
}
}
void SPU::ExecuteTransfer(TickCount ticks)
{
const RAMTransferMode mode = m_SPUCNT.ram_transfer_mode;
Assert(mode != RAMTransferMode::Stopped);
if (mode == RAMTransferMode::DMARead)
{
while (ticks > 0 && !m_transfer_fifo.IsFull())
{
ExecuteFIFOReadFromRAM(ticks);
// this can result in the FIFO being emptied, hence double the while loop // this can result in the FIFO being emptied, hence double the while loop
UpdateDMARequest(); UpdateDMARequest();
@ -790,19 +823,7 @@ void SPU::ExecuteTransfer(TickCount ticks)
// write the fifo to ram, request dma again when empty // write the fifo to ram, request dma again when empty
while (ticks > 0 && !m_transfer_fifo.IsEmpty()) while (ticks > 0 && !m_transfer_fifo.IsEmpty())
{ {
while (ticks > 0 && !m_transfer_fifo.IsEmpty()) ExecuteFIFOWriteToRAM(ticks);
{
u16 value = m_transfer_fifo.Pop();
std::memcpy(&m_ram[m_transfer_address], &value, sizeof(u16));
m_transfer_address = (m_transfer_address + sizeof(u16)) & RAM_MASK;
ticks -= TRANSFER_TICKS_PER_HALFWORD;
if (IsRAMIRQTriggerable() && CheckRAMIRQ(m_transfer_address))
{
Log_DebugPrintf("Trigger IRQ @ %08X %04X from transfer write", m_transfer_address, m_transfer_address / 8);
TriggerRAMIRQ();
}
}
// similar deal here, the FIFO can be written out in a long slice // similar deal here, the FIFO can be written out in a long slice
UpdateDMARequest(); UpdateDMARequest();
@ -841,10 +862,8 @@ void SPU::UpdateTransferEvent()
if (mode == RAMTransferMode::Stopped) if (mode == RAMTransferMode::Stopped)
{ {
m_transfer_event->Deactivate(); m_transfer_event->Deactivate();
return;
} }
else if (mode == RAMTransferMode::DMARead)
if (mode == RAMTransferMode::DMARead)
{ {
// transfer event fills the fifo // transfer event fills the fifo
if (m_transfer_fifo.IsFull()) if (m_transfer_fifo.IsFull())

View file

@ -363,6 +363,8 @@ private:
void Execute(TickCount ticks); void Execute(TickCount ticks);
void UpdateEventInterval(); void UpdateEventInterval();
void ExecuteFIFOWriteToRAM(TickCount& ticks);
void ExecuteFIFOReadFromRAM(TickCount& ticks);
void ExecuteTransfer(TickCount ticks); void ExecuteTransfer(TickCount ticks);
void ManualTransferWrite(u16 value); void ManualTransferWrite(u16 value);
void UpdateTransferEvent(); void UpdateTransferEvent();