From b4f2bf4d3c4c807e9f403178de6fc49ce538ebaa Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 26 Oct 2019 17:41:37 +1000 Subject: [PATCH] CDROM: Deliver data interrupt asynchronously Prevents FMVs locking up when a command is being executed during a sector read. --- src/core/cdrom.cpp | 51 +++++++++++++++++++++++++++++++++++----------- src/core/cdrom.h | 5 +++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 17c89f6cc..f3f5cfdf7 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -44,6 +44,7 @@ void CDROM::SoftReset() m_mode.bits = 0; m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; m_interrupt_flag_register = 0; + m_pending_async_interrupt = 0; m_pending_location = {}; m_location_pending = false; m_filter_file_number = 0; @@ -67,6 +68,7 @@ void CDROM::SoftReset() m_param_fifo.Clear(); m_response_fifo.Clear(); + m_async_response_fifo.Clear(); m_data_fifo.Clear(); m_sector_buffer.clear(); @@ -91,6 +93,7 @@ bool CDROM::DoState(StateWrapper& sw) sw.Do(&m_mode.bits); sw.Do(&m_interrupt_enable_register); sw.Do(&m_interrupt_flag_register); + sw.Do(&m_pending_async_interrupt); sw.DoPOD(&m_pending_location); sw.Do(&m_location_pending); sw.Do(&m_filter_file_number); @@ -105,6 +108,7 @@ bool CDROM::DoState(StateWrapper& sw) sw.Do(&m_xa_resample_sixstep); sw.Do(&m_param_fifo); sw.Do(&m_response_fifo); + sw.Do(&m_async_response_fifo); sw.Do(&m_data_fifo); sw.Do(&m_sector_buffer); @@ -342,11 +346,18 @@ void CDROM::WriteRegister(u32 offset, u8 value) { Log_DebugPrintf("Interrupt flag register <- 0x%02X", value); m_interrupt_flag_register &= ~(value & INTERRUPT_REGISTER_MASK); - if (m_interrupt_flag_register == 0 && m_command_state == CommandState::WaitForIRQClear) + if (m_interrupt_flag_register == 0) { - m_system->Synchronize(); - m_command_state = CommandState::WaitForExecute; - m_system->SetDowncount(m_command_remaining_ticks); + if (m_command_state == CommandState::WaitForIRQClear) + { + m_system->Synchronize(); + m_command_state = CommandState::WaitForExecute; + m_system->SetDowncount(m_command_remaining_ticks); + } + else if (HasPendingAsyncInterrupt()) + { + DeliverAsyncInterrupt(); + } } // Bit 6 clears the parameter FIFO. @@ -403,6 +414,26 @@ void CDROM::SetInterrupt(Interrupt interrupt) m_interrupt_controller->InterruptRequest(InterruptController::IRQ::CDROM); } +void CDROM::SetAsyncInterrupt(Interrupt interrupt) +{ + Assert(m_pending_async_interrupt == 0); + m_pending_async_interrupt = static_cast(interrupt); + if (!HasPendingInterrupt()) + DeliverAsyncInterrupt(); +} + +void CDROM::DeliverAsyncInterrupt() +{ + Assert(m_pending_async_interrupt != 0 && !HasPendingInterrupt()); + Log_DevPrintf("Delivering async interrupt %u", m_pending_async_interrupt); + + m_response_fifo.Clear(); + m_response_fifo.PushFromQueue(&m_async_response_fifo); + m_interrupt_flag_register = m_pending_async_interrupt; + m_pending_async_interrupt = 0; + m_interrupt_controller->InterruptRequest(InterruptController::IRQ::CDROM); +} + void CDROM::SendACKAndStat() { m_response_fifo.Push(m_secondary_status.bits); @@ -894,13 +925,9 @@ void CDROM::BeginReading(bool cdda) void CDROM::DoSectorRead() { - if (HasPendingInterrupt()) + if (HasPendingAsyncInterrupt()) { - // can't read with a pending interrupt? - Log_WarningPrintf("Missed sector read..."); - // m_sector_read_remaining_ticks += 10; - // m_system->SetDowncount(m_sector_read_remaining_ticks); - // return; + Log_WarningPrintf("Data interrupt was not delivered"); } if (!m_sector_buffer.empty()) { @@ -963,8 +990,8 @@ void CDROM::ProcessDataSector() if (pass_to_cpu) { - m_response_fifo.Push(m_secondary_status.bits); - SetInterrupt(Interrupt::INT1); + m_async_response_fifo.Push(m_secondary_status.bits); + SetAsyncInterrupt(Interrupt::INT1); UpdateStatusRegister(); } } diff --git a/src/core/cdrom.h b/src/core/cdrom.h index 0421fb10f..c55b71b36 100644 --- a/src/core/cdrom.h +++ b/src/core/cdrom.h @@ -160,7 +160,10 @@ private: void SoftReset(); bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; } + bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; } void SetInterrupt(Interrupt interrupt); + void SetAsyncInterrupt(Interrupt interrupt); + void DeliverAsyncInterrupt(); void SendACKAndStat(); void SendErrorResponse(u8 reason = 0x80); void UpdateStatusRegister(); @@ -201,6 +204,7 @@ private: u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; u8 m_interrupt_flag_register = 0; + u8 m_pending_async_interrupt = 0; CDImage::Position m_pending_location = {}; bool m_location_pending = false; @@ -221,6 +225,7 @@ private: InlineFIFOQueue m_param_fifo; InlineFIFOQueue m_response_fifo; + InlineFIFOQueue m_async_response_fifo; HeapFIFOQueue m_data_fifo; std::vector m_sector_buffer; };