diff --git a/src/core/bus.cpp b/src/core/bus.cpp index e9200aa10..f9c01610d 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -1331,13 +1331,13 @@ ALWAYS_INLINE static TickCount DoDMAAccess(u32 offset, u32& value) { if constexpr (type == MemoryAccessType::Read) { - value = g_dma.ReadRegister(FIXUP_WORD_OFFSET(size, offset)); + value = DMA::ReadRegister(FIXUP_WORD_OFFSET(size, offset)); value = FIXUP_WORD_READ_VALUE(size, offset, value); return 2; } else { - g_dma.WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value)); + DMA::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value)); return 0; } } diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 30f1e739c..7e3a1b5d5 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -1172,7 +1172,7 @@ void CDROM::UpdateStatusRegister() s_status.DRQSTS = !s_data_fifo.IsEmpty(); s_status.BUSYSTS = HasPendingCommand(); - g_dma.SetRequest(DMA::Channel::CDROM, s_status.DRQSTS); + DMA::SetRequest(DMA::Channel::CDROM, s_status.DRQSTS); } void CDROM::UpdateInterruptRequest() diff --git a/src/core/dma.cpp b/src/core/dma.cpp index 2050d10ca..05e12424c 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -4,6 +4,7 @@ #include "dma.h" #include "bus.h" #include "cdrom.h" +#include "common/bitfield.h" #include "common/log.h" #include "common/string_util.h" #include "cpu_code_cache.h" @@ -17,84 +18,238 @@ #include "spu.h" #include "system.h" #include "util/state_wrapper.h" +#include +#include +#include Log_SetChannel(DMA); -static u32 GetAddressMask() +namespace DMA { +enum class SyncMode : u32 +{ + Manual = 0, + Request = 1, + LinkedList = 2, + Reserved = 3 +}; + +static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); +static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC); + +static u32 GetAddressMask(); +static void ClearState(); + +// is everything enabled for a channel to operate? +static bool CanTransferChannel(Channel channel, bool ignore_halt); +static bool IsTransferHalted(); +static void UpdateIRQ(); + +// returns false if the DMA should now be halted +static TickCount GetTransferSliceTicks(); +static TickCount GetTransferHaltTicks(); +static bool TransferChannel(Channel channel); +static void HaltTransfer(TickCount duration); +static void UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late); + +// from device -> memory +static TickCount TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 word_count); + +// from memory -> device +static TickCount TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count); + +// configuration +static TickCount s_max_slice_ticks = 1000; +static TickCount s_halt_ticks = 100; + +static std::vector s_transfer_buffer; +static std::unique_ptr s_unhalt_event; +static TickCount s_halt_ticks_remaining = 0; + +struct ChannelState +{ + u32 base_address = 0; + + union BlockControl + { + u32 bits; + union + { + BitField word_count; + + u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; } + } manual; + union + { + BitField block_size; + BitField block_count; + + u32 GetBlockSize() const { return (block_size == 0) ? 0x10000 : block_size; } + u32 GetBlockCount() const { return (block_count == 0) ? 0x10000 : block_count; } + } request; + } block_control = {}; + + union ChannelControl + { + u32 bits; + BitField copy_to_device; + BitField address_step_reverse; + BitField chopping_enable; + BitField sync_mode; + BitField chopping_dma_window_size; + BitField chopping_cpu_window_size; + BitField enable_busy; + BitField start_trigger; + + static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011; + } channel_control = {}; + + bool request = false; +}; + +static std::array s_state; + +union DPCR +{ + u32 bits; + + BitField MDECin_priority; + BitField MDECin_master_enable; + BitField MDECout_priority; + BitField MDECout_master_enable; + BitField GPU_priority; + BitField GPU_master_enable; + BitField CDROM_priority; + BitField CDROM_master_enable; + BitField SPU_priority; + BitField SPU_master_enable; + BitField PIO_priority; + BitField PIO_master_enable; + BitField OTC_priority; + BitField OTC_master_enable; + BitField priority_offset; + BitField unused; + + u8 GetPriority(Channel channel) const { return ((bits >> (static_cast(channel) * 4)) & u32(3)); } + bool GetMasterEnable(Channel channel) const + { + return ConvertToBoolUnchecked((bits >> (static_cast(channel) * 4 + 3)) & u32(1)); + } +}; + +static DPCR s_DPCR = {}; + +static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'00111111; +static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000; +union DICR +{ + u32 bits; + + BitField force_irq; + BitField MDECin_irq_enable; + BitField MDECout_irq_enable; + BitField GPU_irq_enable; + BitField CDROM_irq_enable; + BitField SPU_irq_enable; + BitField PIO_irq_enable; + BitField OTC_irq_enable; + BitField master_enable; + BitField MDECin_irq_flag; + BitField MDECout_irq_flag; + BitField GPU_irq_flag; + BitField CDROM_irq_flag; + BitField SPU_irq_flag; + BitField PIO_irq_flag; + BitField OTC_irq_flag; + BitField master_flag; + + bool IsIRQEnabled(Channel channel) const + { + return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 16)) & u32(1)); + } + + bool GetIRQFlag(Channel channel) const + { + return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 24)) & u32(1)); + } + + void SetIRQFlag(Channel channel) { bits |= (u32(1) << (static_cast(channel) + 24)); } + void ClearIRQFlag(Channel channel) { bits &= ~(u32(1) << (static_cast(channel) + 24)); } + + void UpdateMasterFlag() + { + master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0); + } +}; + +static DICR s_DICR = {}; +}; // namespace DMA + +u32 DMA::GetAddressMask() { return Bus::g_ram_mask & 0xFFFFFFFCu; } -DMA g_dma; - -DMA::DMA() = default; - -DMA::~DMA() = default; - void DMA::Initialize() { - m_max_slice_ticks = g_settings.dma_max_slice_ticks; - m_halt_ticks = g_settings.dma_halt_ticks; - - m_transfer_buffer.resize(32); - m_unhalt_event = TimingEvents::CreateTimingEvent( - "DMA Transfer Unhalt", 1, m_max_slice_ticks, - [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->UnhaltTransfer(ticks); }, this, - false); + s_max_slice_ticks = g_settings.dma_max_slice_ticks; + s_halt_ticks = g_settings.dma_halt_ticks; + s_transfer_buffer.resize(32); + s_unhalt_event = + TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_max_slice_ticks, &DMA::UnhaltTransfer, nullptr, false); Reset(); } void DMA::Shutdown() { ClearState(); - m_unhalt_event.reset(); + s_unhalt_event.reset(); } void DMA::Reset() { ClearState(); - m_unhalt_event->Deactivate(); + s_unhalt_event->Deactivate(); } void DMA::ClearState() { for (u32 i = 0; i < NUM_CHANNELS; i++) { - ChannelState& cs = m_state[i]; + ChannelState& cs = s_state[i]; cs.base_address = 0; cs.block_control.bits = 0; cs.channel_control.bits = 0; cs.request = false; } - m_DPCR.bits = 0x07654321; - m_DICR.bits = 0; + s_DPCR.bits = 0x07654321; + s_DICR.bits = 0; - m_halt_ticks_remaining = 0; + s_halt_ticks_remaining = 0; } bool DMA::DoState(StateWrapper& sw) { - sw.Do(&m_halt_ticks_remaining); + sw.Do(&s_halt_ticks_remaining); for (u32 i = 0; i < NUM_CHANNELS; i++) { - ChannelState& cs = m_state[i]; + ChannelState& cs = s_state[i]; sw.Do(&cs.base_address); sw.Do(&cs.block_control.bits); sw.Do(&cs.channel_control.bits); sw.Do(&cs.request); } - sw.Do(&m_DPCR.bits); - sw.Do(&m_DICR.bits); + sw.Do(&s_DPCR.bits); + sw.Do(&s_DICR.bits); if (sw.IsReading()) { - if (m_halt_ticks_remaining > 0) - m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining); + if (s_halt_ticks_remaining > 0) + s_unhalt_event->SetIntervalAndSchedule(s_halt_ticks_remaining); else - m_unhalt_event->Deactivate(); + s_unhalt_event->Deactivate(); } return !sw.HasError(); @@ -109,18 +264,18 @@ u32 DMA::ReadRegister(u32 offset) { case 0x00: { - Log_TracePrintf("DMA%u base address -> 0x%08X", channel_index, m_state[channel_index].base_address); - return m_state[channel_index].base_address; + Log_TracePrintf("DMA%u base address -> 0x%08X", channel_index, s_state[channel_index].base_address); + return s_state[channel_index].base_address; } case 0x04: { - Log_TracePrintf("DMA%u block control -> 0x%08X", channel_index, m_state[channel_index].block_control.bits); - return m_state[channel_index].block_control.bits; + Log_TracePrintf("DMA%u block control -> 0x%08X", channel_index, s_state[channel_index].block_control.bits); + return s_state[channel_index].block_control.bits; } case 0x08: { - Log_TracePrintf("DMA%u channel control -> 0x%08X", channel_index, m_state[channel_index].channel_control.bits); - return m_state[channel_index].channel_control.bits; + Log_TracePrintf("DMA%u channel control -> 0x%08X", channel_index, s_state[channel_index].channel_control.bits); + return s_state[channel_index].channel_control.bits; } default: break; @@ -130,13 +285,13 @@ u32 DMA::ReadRegister(u32 offset) { if (offset == 0x70) { - Log_TracePrintf("DPCR -> 0x%08X", m_DPCR.bits); - return m_DPCR.bits; + Log_TracePrintf("DPCR -> 0x%08X", s_DPCR.bits); + return s_DPCR.bits; } else if (offset == 0x74) { - Log_TracePrintf("DPCR -> 0x%08X", m_DPCR.bits); - return m_DICR.bits; + Log_TracePrintf("DPCR -> 0x%08X", s_DPCR.bits); + return s_DICR.bits; } } @@ -149,7 +304,7 @@ void DMA::WriteRegister(u32 offset, u32 value) const u32 channel_index = offset >> 4; if (channel_index < 7) { - ChannelState& state = m_state[channel_index]; + ChannelState& state = s_state[channel_index]; switch (offset & UINT32_C(0x0F)) { case 0x00: @@ -197,7 +352,7 @@ void DMA::WriteRegister(u32 offset, u32 value) case 0x70: { Log_TracePrintf("DPCR <- 0x%08X", value); - m_DPCR.bits = value; + s_DPCR.bits = value; for (u32 i = 0; i < NUM_CHANNELS; i++) { @@ -214,9 +369,9 @@ void DMA::WriteRegister(u32 offset, u32 value) case 0x74: { Log_TracePrintf("DCIR <- 0x%08X", value); - m_DICR.bits = (m_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK); - m_DICR.bits = m_DICR.bits & ~(value & DICR_RESET_MASK); - m_DICR.UpdateMasterFlag(); + s_DICR.bits = (s_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK); + s_DICR.bits = s_DICR.bits & ~(value & DICR_RESET_MASK); + s_DICR.UpdateMasterFlag(); return; } @@ -230,7 +385,7 @@ void DMA::WriteRegister(u32 offset, u32 value) void DMA::SetRequest(Channel channel, bool request) { - ChannelState& cs = m_state[static_cast(channel)]; + ChannelState& cs = s_state[static_cast(channel)]; if (cs.request == request) return; @@ -239,12 +394,22 @@ void DMA::SetRequest(Channel channel, bool request) TransferChannel(channel); } -bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) const +void DMA::SetMaxSliceTicks(TickCount ticks) { - if (!m_DPCR.GetMasterEnable(channel)) + s_max_slice_ticks = ticks; +} + +void DMA::SetHaltTicks(TickCount ticks) +{ + s_halt_ticks = ticks; +} + +bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) +{ + if (!s_DPCR.GetMasterEnable(channel)) return false; - const ChannelState& cs = m_state[static_cast(channel)]; + const ChannelState& cs = s_state[static_cast(channel)]; if (!cs.channel_control.enable_busy) return false; @@ -254,15 +419,15 @@ bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) const return cs.request; } -bool DMA::IsTransferHalted() const +bool DMA::IsTransferHalted() { - return m_unhalt_event->IsActive(); + return s_unhalt_event->IsActive(); } void DMA::UpdateIRQ() { - m_DICR.UpdateMasterFlag(); - if (m_DICR.master_flag) + s_DICR.UpdateMasterFlag(); + if (s_DICR.master_flag) { Log_TracePrintf("Firing DMA master interrupt"); g_interrupt_controller.InterruptRequest(InterruptController::IRQ::DMA); @@ -279,27 +444,27 @@ enum : u32 HALT_TICKS_WHEN_TRANSMITTING_PAD = 100 }; -TickCount DMA::GetTransferSliceTicks() const +TickCount DMA::GetTransferSliceTicks() { #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); + SLICE_SIZE_WHEN_TRANSMITTING_PAD, s_max_slice_ticks); } #endif - return g_pad.IsTransmitting() ? SLICE_SIZE_WHEN_TRANSMITTING_PAD : m_max_slice_ticks; + return g_pad.IsTransmitting() ? SLICE_SIZE_WHEN_TRANSMITTING_PAD : s_max_slice_ticks; } -TickCount DMA::GetTransferHaltTicks() const +TickCount DMA::GetTransferHaltTicks() { - return g_pad.IsTransmitting() ? HALT_TICKS_WHEN_TRANSMITTING_PAD : m_halt_ticks; + return g_pad.IsTransmitting() ? HALT_TICKS_WHEN_TRANSMITTING_PAD : s_halt_ticks; } bool DMA::TransferChannel(Channel channel) { - ChannelState& cs = m_state[static_cast(channel)]; + ChannelState& cs = s_state[static_cast(channel)]; const u32 mask = GetAddressMask(); const bool copy_to_device = cs.channel_control.copy_to_device; @@ -433,7 +598,7 @@ bool DMA::TransferChannel(Channel channel) if (cs.request) { // we got halted - if (!m_unhalt_event->IsActive()) + if (!s_unhalt_event->IsActive()) HaltTransfer(GetTransferHaltTicks()); return false; @@ -451,10 +616,10 @@ bool DMA::TransferChannel(Channel channel) // start/busy bit is cleared on end of transfer cs.channel_control.enable_busy = false; - if (m_DICR.IsIRQEnabled(channel)) + if (s_DICR.IsIRQEnabled(channel)) { Log_DebugPrintf("Set DMA interrupt for channel %u", static_cast(channel)); - m_DICR.SetIRQFlag(channel); + s_DICR.SetIRQFlag(channel); UpdateIRQ(); } @@ -463,20 +628,20 @@ bool DMA::TransferChannel(Channel channel) void DMA::HaltTransfer(TickCount duration) { - m_halt_ticks_remaining += duration; - Log_DebugPrintf("Halting DMA for %d ticks", m_halt_ticks_remaining); - if (m_unhalt_event->IsActive()) + s_halt_ticks_remaining += duration; + Log_DebugPrintf("Halting DMA for %d ticks", s_halt_ticks_remaining); + if (s_unhalt_event->IsActive()) return; - DebugAssert(!m_unhalt_event->IsActive()); - m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining); + DebugAssert(!s_unhalt_event->IsActive()); + s_unhalt_event->SetIntervalAndSchedule(s_halt_ticks_remaining); } -void DMA::UnhaltTransfer(TickCount ticks) +void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late) { - Log_DebugPrintf("Resuming DMA after %d ticks, %d ticks late", ticks, -(m_halt_ticks_remaining - ticks)); - m_halt_ticks_remaining -= ticks; - m_unhalt_event->Deactivate(); + Log_DebugPrintf("Resuming DMA after %d ticks, %d ticks late", ticks, -(s_halt_ticks_remaining - ticks)); + s_halt_ticks_remaining -= ticks; + s_unhalt_event->Deactivate(); // TODO: Use channel priority. But doing it in ascending order is probably good enough. // Main thing is that OTC happens after GPU, because otherwise it'll wipe out the LL. @@ -490,7 +655,7 @@ void DMA::UnhaltTransfer(TickCount ticks) } // We didn't run too long, so reset timer. - m_halt_ticks_remaining = 0; + s_halt_ticks_remaining = 0; } TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count) @@ -501,14 +666,14 @@ TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 incremen (static_cast(increment) < 0 || ((address + (increment * word_count)) & mask) <= address)) { // Use temp buffer if it's wrapping around - if (m_transfer_buffer.size() < word_count) - m_transfer_buffer.resize(word_count); - src_pointer = m_transfer_buffer.data(); + if (s_transfer_buffer.size() < word_count) + s_transfer_buffer.resize(word_count); + src_pointer = s_transfer_buffer.data(); u8* ram_pointer = Bus::g_ram; for (u32 i = 0; i < word_count; i++) { - std::memcpy(&m_transfer_buffer[i], &ram_pointer[address], sizeof(u32)); + std::memcpy(&s_transfer_buffer[i], &ram_pointer[address], sizeof(u32)); address = (address + increment) & mask; } } @@ -577,9 +742,9 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen if (static_cast(increment) < 0 || ((address + (increment * word_count)) & mask) <= address) { // Use temp buffer if it's wrapping around - if (m_transfer_buffer.size() < word_count) - m_transfer_buffer.resize(word_count); - dest_pointer = m_transfer_buffer.data(); + if (s_transfer_buffer.size() < word_count) + s_transfer_buffer.resize(word_count); + dest_pointer = s_transfer_buffer.data(); } // Read from device. @@ -607,12 +772,12 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen break; } - if (dest_pointer == m_transfer_buffer.data()) + if (dest_pointer == s_transfer_buffer.data()) { u8* ram_pointer = Bus::g_ram; for (u32 i = 0; i < word_count; i++) { - std::memcpy(&ram_pointer[address], &m_transfer_buffer[i], sizeof(u32)); + std::memcpy(&ram_pointer[address], &s_transfer_buffer[i], sizeof(u32)); address = (address + increment) & mask; } } @@ -662,7 +827,7 @@ void DMA::DrawDebugStateWindow() for (u32 i = 0; i < NUM_CHANNELS; i++) { - const ChannelState& cs = m_state[i]; + const ChannelState& cs = s_state[i]; ImGui::TextColored(cs.channel_control.enable_busy ? active : inactive, "%u[%s]", i, channel_names[i]); ImGui::NextColumn(); @@ -682,17 +847,17 @@ void DMA::DrawDebugStateWindow() cs.channel_control.enable_busy ? "Busy" : "Idle", cs.channel_control.start_trigger ? " (Trigger)" : ""); ImGui::NextColumn(); - ImGui::TextColored(m_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, - m_DPCR.GetMasterEnable(static_cast(i)) ? "Enabled" : "Disabled"); + ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, + s_DPCR.GetMasterEnable(static_cast(i)) ? "Enabled" : "Disabled"); ImGui::NextColumn(); - ImGui::TextColored(m_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, "%u", - m_DPCR.GetPriority(static_cast(i))); + ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, "%u", + s_DPCR.GetPriority(static_cast(i))); ImGui::NextColumn(); - ImGui::TextColored(m_DICR.IsIRQEnabled(static_cast(i)) ? active : inactive, - m_DICR.IsIRQEnabled(static_cast(i)) ? "Enabled" : "Disabled"); + ImGui::TextColored(s_DICR.IsIRQEnabled(static_cast(i)) ? active : inactive, + s_DICR.IsIRQEnabled(static_cast(i)) ? "Enabled" : "Disabled"); ImGui::NextColumn(); - ImGui::TextColored(m_DICR.GetIRQFlag(static_cast(i)) ? active : inactive, - m_DICR.GetIRQFlag(static_cast(i)) ? "IRQ" : ""); + ImGui::TextColored(s_DICR.GetIRQFlag(static_cast(i)) ? active : inactive, + s_DICR.GetIRQFlag(static_cast(i)) ? "IRQ" : ""); ImGui::NextColumn(); } diff --git a/src/core/dma.h b/src/core/dma.h index b16150b30..8b575f7fa 100644 --- a/src/core/dma.h +++ b/src/core/dma.h @@ -2,207 +2,42 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once -#include "common/bitfield.h" #include "types.h" -#include -#include -#include class StateWrapper; -class TimingEvent; +namespace DMA { -class DMA +enum : u32 { -public: - enum : u32 - { - NUM_CHANNELS = 7 - }; - - enum class Channel : u32 - { - MDECin = 0, - MDECout = 1, - GPU = 2, - CDROM = 3, - SPU = 4, - PIO = 5, - OTC = 6 - }; - - DMA(); - ~DMA(); - - void Initialize(); - void Shutdown(); - void Reset(); - bool DoState(StateWrapper& sw); - - u32 ReadRegister(u32 offset); - void WriteRegister(u32 offset, u32 value); - - void SetRequest(Channel channel, bool request); - - // changing interfaces - void SetMaxSliceTicks(TickCount ticks) { m_max_slice_ticks = ticks; } - void SetHaltTicks(TickCount ticks) { m_halt_ticks = ticks; } - - void DrawDebugStateWindow(); - -private: - static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); - static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC); - - enum class SyncMode : u32 - { - Manual = 0, - Request = 1, - LinkedList = 2, - Reserved = 3 - }; - - void ClearState(); - - // is everything enabled for a channel to operate? - bool CanTransferChannel(Channel channel, bool ignore_halt) const; - bool IsTransferHalted() const; - 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); - - // from device -> memory - TickCount TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 word_count); - - // from memory -> device - TickCount TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count); - - // configuration - TickCount m_max_slice_ticks = 1000; - TickCount m_halt_ticks = 100; - - std::vector m_transfer_buffer; - std::unique_ptr m_unhalt_event; - TickCount m_halt_ticks_remaining = 0; - - struct ChannelState - { - u32 base_address = 0; - - union BlockControl - { - u32 bits; - union - { - BitField word_count; - - u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; } - } manual; - union - { - BitField block_size; - BitField block_count; - - u32 GetBlockSize() const { return (block_size == 0) ? 0x10000 : block_size; } - u32 GetBlockCount() const { return (block_count == 0) ? 0x10000 : block_count; } - } request; - } block_control = {}; - - union ChannelControl - { - u32 bits; - BitField copy_to_device; - BitField address_step_reverse; - BitField chopping_enable; - BitField sync_mode; - BitField chopping_dma_window_size; - BitField chopping_cpu_window_size; - BitField enable_busy; - BitField start_trigger; - - static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011; - } channel_control = {}; - - bool request = false; - }; - - std::array m_state; - - union DPCR - { - u32 bits; - - BitField MDECin_priority; - BitField MDECin_master_enable; - BitField MDECout_priority; - BitField MDECout_master_enable; - BitField GPU_priority; - BitField GPU_master_enable; - BitField CDROM_priority; - BitField CDROM_master_enable; - BitField SPU_priority; - BitField SPU_master_enable; - BitField PIO_priority; - BitField PIO_master_enable; - BitField OTC_priority; - BitField OTC_master_enable; - BitField priority_offset; - BitField unused; - - u8 GetPriority(Channel channel) const { return ((bits >> (static_cast(channel) * 4)) & u32(3)); } - bool GetMasterEnable(Channel channel) const - { - return ConvertToBoolUnchecked((bits >> (static_cast(channel) * 4 + 3)) & u32(1)); - } - } m_DPCR = {}; - - static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'00111111; - static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000; - union DICR - { - u32 bits; - - BitField force_irq; - BitField MDECin_irq_enable; - BitField MDECout_irq_enable; - BitField GPU_irq_enable; - BitField CDROM_irq_enable; - BitField SPU_irq_enable; - BitField PIO_irq_enable; - BitField OTC_irq_enable; - BitField master_enable; - BitField MDECin_irq_flag; - BitField MDECout_irq_flag; - BitField GPU_irq_flag; - BitField CDROM_irq_flag; - BitField SPU_irq_flag; - BitField PIO_irq_flag; - BitField OTC_irq_flag; - BitField master_flag; - - bool IsIRQEnabled(Channel channel) const - { - return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 16)) & u32(1)); - } - - bool GetIRQFlag(Channel channel) const - { - return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 24)) & u32(1)); - } - - void SetIRQFlag(Channel channel) { bits |= (u32(1) << (static_cast(channel) + 24)); } - void ClearIRQFlag(Channel channel) { bits &= ~(u32(1) << (static_cast(channel) + 24)); } - - void UpdateMasterFlag() - { - master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0); - } - } m_DICR = {}; + NUM_CHANNELS = 7 }; -extern DMA g_dma; \ No newline at end of file +enum class Channel : u32 +{ + MDECin = 0, + MDECout = 1, + GPU = 2, + CDROM = 3, + SPU = 4, + PIO = 5, + OTC = 6 +}; + +void Initialize(); +void Shutdown(); +void Reset(); +bool DoState(StateWrapper& sw); + +u32 ReadRegister(u32 offset); +void WriteRegister(u32 offset, u32 value); + +void SetRequest(Channel channel, bool request); + +// changing interfaces +void SetMaxSliceTicks(TickCount ticks); +void SetHaltTicks(TickCount ticks); + +void DrawDebugStateWindow(); + +} // namespace DMA diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 6669909e0..3b1277737 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -341,7 +341,7 @@ void GPU::UpdateDMARequest() break; } m_GPUSTAT.dma_data_request = dma_request; - g_dma.SetRequest(DMA::Channel::GPU, dma_request); + DMA::SetRequest(DMA::Channel::GPU, dma_request); } void GPU::UpdateGPUIdle() diff --git a/src/core/mdec.cpp b/src/core/mdec.cpp index 38b398225..96891ed25 100644 --- a/src/core/mdec.cpp +++ b/src/core/mdec.cpp @@ -307,12 +307,12 @@ void MDEC::UpdateStatus() // we always want data in if it's enabled const bool data_in_request = s_enable_dma_in && s_data_in_fifo.GetSpace() >= (32 * 2); s_status.data_in_request = data_in_request; - g_dma.SetRequest(DMA::Channel::MDECin, data_in_request); + DMA::SetRequest(DMA::Channel::MDECin, data_in_request); // we only want to send data out if we have some in the fifo const bool data_out_request = s_enable_dma_out && !s_data_out_fifo.IsEmpty(); s_status.data_out_request = data_out_request; - g_dma.SetRequest(DMA::Channel::MDECout, data_out_request); + DMA::SetRequest(DMA::Channel::MDECout, data_out_request); } u32 MDEC::ReadDataRegister() diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 5a22c67b3..685e30dd8 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -1349,7 +1349,7 @@ void SPU::UpdateDMARequest() } // This might call us back directly. - g_dma.SetRequest(DMA::Channel::SPU, s_SPUSTAT.dma_request); + DMA::SetRequest(DMA::Channel::SPU, s_SPUSTAT.dma_request); } void SPU::DMARead(u32* words, u32 word_count) diff --git a/src/core/system.cpp b/src/core/system.cpp index 7e8d01fc0..3c6bd2d67 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1386,7 +1386,7 @@ bool System::Initialize(bool force_software_renderer) // CPU code cache must happen after GPU, because it might steal our address space. CPU::CodeCache::Initialize(); - g_dma.Initialize(); + DMA::Initialize(); g_interrupt_controller.Initialize(); CDROM::Initialize(); @@ -1458,7 +1458,7 @@ void System::DestroySystem() CDROM::Shutdown(); g_gpu.reset(); g_interrupt_controller.Shutdown(); - g_dma.Shutdown(); + DMA::Shutdown(); PGXP::Shutdown(); CPU::CodeCache::Shutdown(); Bus::Shutdown(); @@ -1640,7 +1640,7 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di if (!sw.DoMarker("Bus") || !Bus::DoState(sw)) return false; - if (!sw.DoMarker("DMA") || !g_dma.DoState(sw)) + if (!sw.DoMarker("DMA") || !DMA::DoState(sw)) return false; if (!sw.DoMarker("InterruptController") || !g_interrupt_controller.DoState(sw)) @@ -1739,7 +1739,7 @@ void System::InternalReset() PGXP::Initialize(); Bus::Reset(); - g_dma.Reset(); + DMA::Reset(); g_interrupt_controller.Reset(); g_gpu->Reset(true); CDROM::Reset(); @@ -3299,8 +3299,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_texture_replacements.Reload(); } - g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks); - g_dma.SetHaltTicks(g_settings.dma_halt_ticks); + DMA::SetMaxSliceTicks(g_settings.dma_max_slice_ticks); + DMA::SetHaltTicks(g_settings.dma_halt_ticks); if (g_settings.audio_backend != old_settings.audio_backend || g_settings.video_sync_enabled != old_settings.video_sync_enabled || diff --git a/src/frontend-common/common_host.cpp b/src/frontend-common/common_host.cpp index 387e790f7..cf22c580f 100644 --- a/src/frontend-common/common_host.cpp +++ b/src/frontend-common/common_host.cpp @@ -490,7 +490,7 @@ void ImGuiManager::RenderDebugWindows() if (g_settings.debugging.show_mdec_state) MDEC::DrawDebugStateWindow(); if (g_settings.debugging.show_dma_state) - g_dma.DrawDebugStateWindow(); + DMA::DrawDebugStateWindow(); } }