From 162f94337e491ddd6f848e341127dc0a79128e64 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 11 Sep 2019 14:59:41 +1000 Subject: [PATCH] DMA: Implement linked list mode --- src/pse/dma.cpp | 117 ++++++++++++++++++++++++++++++++++++++---------- src/pse/dma.h | 8 ++-- src/pse/gpu.cpp | 85 ++++++++++++++++++++++++++++------- src/pse/gpu.h | 16 ++++++- 4 files changed, 180 insertions(+), 46 deletions(-) diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index 346a9b3b7..053eff6bd 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -1,6 +1,7 @@ #include "dma.h" #include "YBaseLib/Log.h" #include "bus.h" +#include "gpu.h" Log_SetChannel(DMA); DMA::DMA() = default; @@ -60,7 +61,7 @@ void DMA::WriteRegister(u32 offset, u32 value) { case 0x00: { - state.base_address = value & BASE_ADDRESS_MASK; + state.base_address = value & ADDRESS_MASK; Log_DebugPrintf("DMA channel %u base address <- 0x%08X", channel_index, state.base_address); return; } @@ -136,44 +137,97 @@ bool DMA::CanRunChannel(Channel channel) const void DMA::RunDMA(Channel channel) { ChannelState& cs = m_state[static_cast(channel)]; - const PhysicalMemoryAddress memory_address = cs.base_address; const bool copy_to_device = cs.channel_control.copy_to_device; Log_DebugPrintf("Running DMA for channel %u", static_cast(channel)); - Assert(Common::IsAlignedPow2(memory_address, 4)); // start/trigger bit is cleared on beginning of transfer cs.channel_control.start_trigger = false; + PhysicalMemoryAddress current_address = cs.base_address & ~UINT32_C(3); + const PhysicalMemoryAddress increment = cs.channel_control.address_step_reverse ? static_cast(-4) : UINT32_C(4); switch (cs.channel_control.sync_mode) { case SyncMode::Manual: { const u32 word_count = cs.block_control.manual.GetWordCount(); - Log_DebugPrintf(" ... copying %u words %s 0x%08X", word_count, copy_to_device ? "from" : "to", memory_address); + Log_DebugPrintf(" ... copying %u words %s 0x%08X", word_count, copy_to_device ? "from" : "to", current_address); if (copy_to_device) { - for (u32 i = 0; i < word_count; i++) + u32 words_remaining = word_count; + do { - u32 memory_value = 0; - m_bus->DispatchAccess(memory_address, memory_address, - memory_value); - DMAWrite(channel, memory_value); - } + words_remaining--; + + u32 value = 0; + m_bus->DispatchAccess(current_address, current_address, + value); + DMAWrite(channel, value, current_address, words_remaining); + + current_address = (current_address + increment) & ADDRESS_MASK; + } while (words_remaining > 0); } else { - for (u32 i = 0; i < word_count; i++) + u32 words_remaining = word_count; + do { - u32 memory_value = DMARead(channel); - m_bus->DispatchAccess(memory_address, memory_address, - memory_value); + words_remaining--; + + u32 value = DMARead(channel, current_address, words_remaining); + m_bus->DispatchAccess(current_address, current_address, + value); + + current_address = (current_address + increment) & ADDRESS_MASK; + } while (words_remaining > 0); + } + } + break; + + case SyncMode::LinkedList: + { + if (!copy_to_device) + { + Panic("Linked list not implemented for DMA reads"); + } + else + { + for (;;) + { + u32 header; + m_bus->DispatchAccess(current_address, current_address, + header); + + const u32 word_count = header >> 24; + const u32 next_address = header & UINT32_C(0xFFFFFF); + Log_DebugPrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", current_address, + word_count * UINT32_C(4), word_count, next_address); + current_address += sizeof(header); + + if (word_count > 0) + { + u32 words_remaining = word_count; + do + { + words_remaining--; + + u32 memory_value = 0; + m_bus->DispatchAccess(current_address, current_address, + memory_value); + DMAWrite(channel, memory_value, current_address, words_remaining); + current_address = (current_address + UINT32_C(4)) & ADDRESS_MASK; + } while (words_remaining > 0); + } + + if (next_address & UINT32_C(0x800000)) + break; + + current_address = next_address & ADDRESS_MASK; } } } break; case SyncMode::Request: - case SyncMode::LinkedList: default: Panic("Unimplemented sync mode"); break; @@ -183,29 +237,44 @@ void DMA::RunDMA(Channel channel) cs.channel_control.enable_busy = false; } -u32 DMA::DMARead(Channel channel) +u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words) { switch (channel) { case Channel::OTC: - { - // we just return zeros here.. guessing it's pulled low? - return 0; - } + // clear ordering table + return (remaining_words == 0) ? UINT32_C(0xFFFFFF) : ((dst_address - UINT32_C(4)) & ADDRESS_MASK); + + case Channel::GPU: + return m_gpu->DMARead(); case Channel::MDECin: case Channel::MDECout: - case Channel::GPU: case Channel::CDROM: case Channel::SPU: case Channel::PIO: default: - Panic("Unhandled DMA channel write"); + Panic("Unhandled DMA channel read"); return UINT32_C(0xFFFFFFFF); } } -void DMA::DMAWrite(Channel channel, u32 value) +void DMA::DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words) { - Panic("Unhandled DMA channel write"); + switch (channel) + { + case Channel::GPU: + m_gpu->DMAWrite(value); + return; + + case Channel::MDECin: + case Channel::MDECout: + case Channel::CDROM: + case Channel::SPU: + case Channel::PIO: + case Channel::OTC: + default: + Panic("Unhandled DMA channel write"); + break; + } } diff --git a/src/pse/dma.h b/src/pse/dma.h index 661a09466..987fc9624 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -37,7 +37,7 @@ public: void SetRequest(Channel channel, bool request); private: - static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); + static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF); enum class SyncMode : u32 { @@ -53,10 +53,10 @@ private: void RunDMA(Channel channel); // from device -> memory - u32 DMARead(Channel channel); + u32 DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words); // from memory -> device - void DMAWrite(Channel channel, u32 value); + void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words); Bus* m_bus = nullptr; GPU* m_gpu = nullptr; @@ -88,7 +88,7 @@ private: { u32 bits; BitField copy_to_device; - BitField address_step_forward; + BitField address_step_reverse; BitField chopping_enable; BitField sync_mode; BitField chopping_dma_window_size; diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 7c7549b90..08362abeb 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -1,6 +1,7 @@ #include "gpu.h" #include "YBaseLib/Log.h" #include "bus.h" +#include "dma.h" Log_SetChannel(GPU); GPU::GPU() = default; @@ -22,34 +23,70 @@ void GPU::Reset() void GPU::SoftReset() { m_GPUSTAT.bits = 0x14802000; + UpdateDMARequest(); +} + +void GPU::UpdateDMARequest() +{ + const bool request = m_GPUSTAT.dma_direction != DMADirection::Off; + m_GPUSTAT.dma_data_request = request; + m_dma->SetRequest(DMA::Channel::GPU, request); } u32 GPU::ReadRegister(u32 offset) { - if (offset == 0x00) + switch (offset) { - // GPUREAD - Log_ErrorPrintf("GPUREAD"); - return 0; + case 0x00: + return ReadGPUREAD(); + + case 0x04: + return m_GPUSTAT.bits; + + default: + Log_ErrorPrintf("Unhandled register read: %02X", offset); + return UINT32_C(0xFFFFFFFF); } - else if (offset == 0x04) +} + +void GPU::WriteRegister(u32 offset, u32 value) +{ + switch (offset) { - // GPUSTAT - return m_GPUSTAT.bits; + case 0x00: + WriteGP0(value); + return; + + case 0x04: + WriteGP1(value); + return; + + default: + Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value); + return; } +} - Log_ErrorPrintf("Unhandled register read: %02X", offset); - return UINT32_C(0xFFFFFFFF); +u32 GPU::DMARead() +{ + if (m_GPUSTAT.dma_direction != DMADirection::GPUREADtoCPU) + { + Log_ErrorPrintf("Invalid DMA direction from GPU DMA read"); + return UINT32_C(0xFFFFFFFF); + } + + return ReadGPUREAD(); } -void GPU::WriteRegister(u32 offset, u32 value) +void GPU::DMAWrite(u32 value) +{ + Log_ErrorPrintf("GPU DMA Write %08X", value); +} + +u32 GPU::ReadGPUREAD() { - if (offset == 0x00) - WriteGP0(value); - else if (offset == 0x04) - WriteGP1(value); - else - Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value); + Log_ErrorPrintf("GPUREAD not implemented"); + return UINT32_C(0xFFFFFFFF); } void GPU::WriteGP0(u32 value) @@ -61,5 +98,19 @@ void GPU::WriteGP0(u32 value) void GPU::WriteGP1(u32 value) { const u8 command = Truncate8(value >> 24); - Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command); + const u32 param = value & UINT32_C(0x00FFFFFF); + switch (command) + { + case 0x04: // DMA Direction + { + m_GPUSTAT.dma_direction = static_cast(param); + Log_DebugPrintf("DMA direction <- 0x%02X", static_cast(m_GPUSTAT.dma_direction.GetValue())); + UpdateDMARequest(); + } + break; + + default: + Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command); + break; + } } diff --git a/src/pse/gpu.h b/src/pse/gpu.h index ff8d94ecb..857a534da 100644 --- a/src/pse/gpu.h +++ b/src/pse/gpu.h @@ -18,8 +18,22 @@ public: u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); + // DMA access + u32 DMARead(); + void DMAWrite(u32 value); + private: + enum class DMADirection : u32 + { + Off = 0, + FIFO = 1, + CPUtoGP0 = 2, + GPUREADtoCPU = 3 + }; + void SoftReset(); + void UpdateDMARequest(); + u32 ReadGPUREAD(); void WriteGP0(u32 value); void WriteGP1(u32 value); @@ -52,7 +66,7 @@ private: BitField ready_to_recieve_cmd; BitField ready_to_send_vram; BitField ready_to_recieve_dma; - BitField dma_direction; + BitField dma_direction; BitField drawing_even_line; } m_GPUSTAT = {}; };