From 845cd37835853299d1375e02553bd200a873ee8e Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 6 Jan 2021 00:00:56 +1000 Subject: [PATCH] DMA: Determine slice size based on whether pad is transmitting Plenty of games seem to suffer from this issue where they have a linked list DMA going while polling the controller. Using a too-large slice size will result in the serial timing being off, and the game thinking the controller is disconnected. So we don't hurt performance too much for the general case, we reduce this to equal CPU and DMA time when the controller is transferring, but otherwise leave it at the higher size. --- src/core/dma.cpp | 37 +++++++++++++++++++++++++++++++++---- src/core/dma.h | 3 ++- src/core/pad.h | 7 ++++--- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/core/dma.cpp b/src/core/dma.cpp index 1145d5fe0..2c65599b8 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -9,6 +9,7 @@ #include "gpu.h" #include "interrupt_controller.h" #include "mdec.h" +#include "pad.h" #include "spu.h" #include "system.h" #ifdef WITH_IMGUI @@ -253,6 +254,34 @@ void DMA::UpdateIRQ() } } +// Plenty of games seem to suffer from this issue where they have a linked list DMA going while polling the +// controller. Using a too-large slice size will result in the serial timing being off, and the game thinking +// the controller is disconnected. So we don't hurt performance too much for the general case, we reduce this +// to equal CPU and DMA time when the controller is transferring, but otherwise leave it at the higher size. +enum : u32 +{ + SLICE_SIZE_WHEN_TRANSMITTING_PAD = 100, + HALT_TICKS_WHEN_TRANSMITTING_PAD = 100 +}; + +TickCount DMA::GetTransferSliceTicks() const +{ +#ifdef _DEBUG + if (g_pad.IsTransmitting()) + { + Log_DebugPrintf("DMA transfer while transmitting pad - using lower slice size of %u vs %u", + SLICE_SIZE_WHEN_TRANSMITTING_PAD, m_max_slice_ticks); + } +#endif + + return g_pad.IsTransmitting() ? SLICE_SIZE_WHEN_TRANSMITTING_PAD : m_max_slice_ticks; +} + +TickCount DMA::GetTransferHaltTicks() const +{ + return g_pad.IsTransmitting() ? HALT_TICKS_WHEN_TRANSMITTING_PAD : m_halt_ticks; +} + bool DMA::TransferChannel(Channel channel) { ChannelState& cs = m_state[static_cast(channel)]; @@ -294,7 +323,7 @@ bool DMA::TransferChannel(Channel channel) current_address & ADDRESS_MASK); u8* ram_pointer = Bus::g_ram; - TickCount remaining_ticks = m_max_slice_ticks; + TickCount remaining_ticks = GetTransferSliceTicks(); while (cs.request && remaining_ticks > 0) { u32 header; @@ -330,7 +359,7 @@ bool DMA::TransferChannel(Channel channel) if (cs.request) { // stall the transfer for a bit if we ran for too long - HaltTransfer(m_halt_ticks); + HaltTransfer(GetTransferHaltTicks()); return false; } else @@ -350,7 +379,7 @@ bool DMA::TransferChannel(Channel channel) const u32 block_size = cs.block_control.request.GetBlockSize(); u32 blocks_remaining = cs.block_control.request.GetBlockCount(); - TickCount ticks_remaining = m_max_slice_ticks; + TickCount ticks_remaining = GetTransferSliceTicks(); if (copy_to_device) { @@ -391,7 +420,7 @@ bool DMA::TransferChannel(Channel channel) { // we got halted if (!m_unhalt_event->IsActive()) - HaltTransfer(m_halt_ticks); + HaltTransfer(GetTransferHaltTicks()); return false; } diff --git a/src/core/dma.h b/src/core/dma.h index b4f76a774..04f56f3f6 100644 --- a/src/core/dma.h +++ b/src/core/dma.h @@ -50,7 +50,6 @@ public: private: static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC); - static constexpr u32 TRANSFER_TICKS = 10; enum class SyncMode : u32 { @@ -68,6 +67,8 @@ private: void UpdateIRQ(); // returns false if the DMA should now be halted + TickCount GetTransferSliceTicks() const; + TickCount GetTransferHaltTicks() const; bool TransferChannel(Channel channel); void HaltTransfer(TickCount duration); void UnhaltTransfer(TickCount ticks); diff --git a/src/core/pad.h b/src/core/pad.h index 1d16ce884..f8debd2c1 100644 --- a/src/core/pad.h +++ b/src/core/pad.h @@ -31,6 +31,8 @@ public: u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); + ALWAYS_INLINE bool IsTransmitting() const { return m_state != State::Idle; } + private: static constexpr u32 NUM_SLOTS = 2; @@ -87,10 +89,9 @@ private: BitField clk_polarity; }; - bool IsTransmitting() const { return m_state != State::Idle; } - bool CanTransfer() const { return m_transmit_buffer_full && m_JOY_CTRL.SELECT && m_JOY_CTRL.TXEN; } + ALWAYS_INLINE bool CanTransfer() const { return m_transmit_buffer_full && m_JOY_CTRL.SELECT && m_JOY_CTRL.TXEN; } - TickCount GetTransferTicks() const { return static_cast(ZeroExtend32(m_JOY_BAUD) * 8); } + ALWAYS_INLINE TickCount GetTransferTicks() const { return static_cast(ZeroExtend32(m_JOY_BAUD) * 8); } // From @JaCzekanski // ACK lasts ~96 ticks or approximately 2.84us at master clock (not implemented).