diff --git a/src/core/dma.cpp b/src/core/dma.cpp index 869c4b83d..ecc336945 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -90,7 +90,7 @@ struct ChannelState bool request = false; }; -union DPCR +union DPCRRegister { u32 bits; @@ -120,7 +120,7 @@ union DPCR static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'00111111; static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000; -union DICR +union DICRRegister { u32 bits; @@ -197,16 +197,23 @@ static TickCount TransferMemoryToDevice(u32 address, u32 increment, u32 word_cou static TickCount GetMaxSliceTicks(); // configuration -static TickCount s_max_slice_ticks = 1000; -static TickCount s_halt_ticks = 100; +namespace { +struct DMAState +{ + TickCount max_slice_ticks = 1000; + TickCount halt_ticks = 100; -static std::vector s_transfer_buffer; -static std::unique_ptr s_unhalt_event; -static TickCount s_halt_ticks_remaining = 0; + std::vector transfer_buffer; + std::unique_ptr unhalt_event; + TickCount halt_ticks_remaining = 0; -static std::array s_state; -static DPCR s_DPCR = {}; -static DICR s_DICR = {}; + std::array channels; + DPCRRegister DPCR = {}; + DICRRegister DICR = {}; +}; +} // namespace + +ALIGN_TO_CACHE_LINE static DMAState s_state; static constexpr std::array s_channel_transfer_functions = {{ &TransferChannel, @@ -234,65 +241,65 @@ struct fmt::formatter : fmt::formatter void DMA::Initialize() { - s_max_slice_ticks = g_settings.dma_max_slice_ticks; - s_halt_ticks = g_settings.dma_halt_ticks; + s_state.max_slice_ticks = g_settings.dma_max_slice_ticks; + s_state.halt_ticks = g_settings.dma_halt_ticks; - s_unhalt_event = - TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_max_slice_ticks, &DMA::UnhaltTransfer, nullptr, false); + s_state.unhalt_event = TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_state.max_slice_ticks, + &DMA::UnhaltTransfer, nullptr, false); Reset(); } void DMA::Shutdown() { ClearState(); - s_unhalt_event.reset(); + s_state.unhalt_event.reset(); } void DMA::Reset() { ClearState(); - s_unhalt_event->Deactivate(); + s_state.unhalt_event->Deactivate(); } void DMA::ClearState() { for (u32 i = 0; i < NUM_CHANNELS; i++) { - ChannelState& cs = s_state[i]; + ChannelState& cs = s_state.channels[i]; cs.base_address = 0; cs.block_control.bits = 0; cs.channel_control.bits = 0; cs.request = false; } - s_DPCR.bits = 0x07654321; - s_DICR.bits = 0; + s_state.DPCR.bits = 0x07654321; + s_state.DICR.bits = 0; - s_halt_ticks_remaining = 0; + s_state.halt_ticks_remaining = 0; } bool DMA::DoState(StateWrapper& sw) { - sw.Do(&s_halt_ticks_remaining); + sw.Do(&s_state.halt_ticks_remaining); for (u32 i = 0; i < NUM_CHANNELS; i++) { - ChannelState& cs = s_state[i]; + ChannelState& cs = s_state.channels[i]; sw.Do(&cs.base_address); sw.Do(&cs.block_control.bits); sw.Do(&cs.channel_control.bits); sw.Do(&cs.request); } - sw.Do(&s_DPCR.bits); - sw.Do(&s_DICR.bits); + sw.Do(&s_state.DPCR.bits); + sw.Do(&s_state.DICR.bits); if (sw.IsReading()) { - if (s_halt_ticks_remaining > 0) - s_unhalt_event->SetIntervalAndSchedule(s_halt_ticks_remaining); + if (s_state.halt_ticks_remaining > 0) + s_state.unhalt_event->SetIntervalAndSchedule(s_state.halt_ticks_remaining); else - s_unhalt_event->Deactivate(); + s_state.unhalt_event->Deactivate(); } return !sw.HasError(); @@ -308,20 +315,20 @@ u32 DMA::ReadRegister(u32 offset) case 0x00: { TRACE_LOG("DMA[{}] base address -> 0x{:08X}", static_cast(channel_index), - s_state[channel_index].base_address); - return s_state[channel_index].base_address; + s_state.channels[channel_index].base_address); + return s_state.channels[channel_index].base_address; } case 0x04: { TRACE_LOG("DMA[{}] block control -> 0x{:08X}", static_cast(channel_index), - s_state[channel_index].block_control.bits); - return s_state[channel_index].block_control.bits; + s_state.channels[channel_index].block_control.bits); + return s_state.channels[channel_index].block_control.bits; } case 0x08: { TRACE_LOG("DMA[{}] channel control -> 0x{:08X}", static_cast(channel_index), - s_state[channel_index].channel_control.bits); - return s_state[channel_index].channel_control.bits; + s_state.channels[channel_index].channel_control.bits); + return s_state.channels[channel_index].channel_control.bits; } default: break; @@ -331,13 +338,13 @@ u32 DMA::ReadRegister(u32 offset) { if (offset == 0x70) { - TRACE_LOG("DPCR -> 0x{:08X}", s_DPCR.bits); - return s_DPCR.bits; + TRACE_LOG("DPCR -> 0x{:08X}", s_state.DPCR.bits); + return s_state.DPCR.bits; } else if (offset == 0x74) { - TRACE_LOG("DICR -> 0x{:08X}", s_DICR.bits); - return s_DICR.bits; + TRACE_LOG("DICR -> 0x{:08X}", s_state.DICR.bits); + return s_state.DICR.bits; } } @@ -350,7 +357,7 @@ void DMA::WriteRegister(u32 offset, u32 value) const u32 channel_index = offset >> 4; if (channel_index < 7) { - ChannelState& state = s_state[channel_index]; + ChannelState& state = s_state.channels[channel_index]; switch (offset & UINT32_C(0x0F)) { case 0x00: @@ -426,7 +433,7 @@ void DMA::WriteRegister(u32 offset, u32 value) case 0x70: { TRACE_LOG("DPCR <- 0x{:08X}", value); - s_DPCR.bits = value; + s_state.DPCR.bits = value; for (u32 i = 0; i < NUM_CHANNELS; i++) { @@ -443,8 +450,8 @@ void DMA::WriteRegister(u32 offset, u32 value) case 0x74: { TRACE_LOG("DICR <- 0x{:08X}", value); - s_DICR.bits = (s_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK); - s_DICR.bits = s_DICR.bits & ~(value & DICR_RESET_MASK); + s_state.DICR.bits = (s_state.DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK); + s_state.DICR.bits = s_state.DICR.bits & ~(value & DICR_RESET_MASK); UpdateIRQ(); return; } @@ -459,7 +466,7 @@ void DMA::WriteRegister(u32 offset, u32 value) void DMA::SetRequest(Channel channel, bool request) { - ChannelState& cs = s_state[static_cast(channel)]; + ChannelState& cs = s_state.channels[static_cast(channel)]; if (cs.request == request) return; @@ -470,20 +477,20 @@ void DMA::SetRequest(Channel channel, bool request) void DMA::SetMaxSliceTicks(TickCount ticks) { - s_max_slice_ticks = ticks; + s_state.max_slice_ticks = ticks; } void DMA::SetHaltTicks(TickCount ticks) { - s_halt_ticks = ticks; + s_state.halt_ticks = ticks; } ALWAYS_INLINE_RELEASE bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) { - if (!s_DPCR.GetMasterEnable(channel)) + if (!s_state.DPCR.GetMasterEnable(channel)) return false; - const ChannelState& cs = s_state[static_cast(channel)]; + const ChannelState& cs = s_state.channels[static_cast(channel)]; if (!cs.channel_control.enable_busy) return false; @@ -495,16 +502,16 @@ ALWAYS_INLINE_RELEASE bool DMA::CanTransferChannel(Channel channel, bool ignore_ bool DMA::IsTransferHalted() { - return s_unhalt_event->IsActive(); + return s_state.unhalt_event->IsActive(); } void DMA::UpdateIRQ() { - [[maybe_unused]] const auto old_dicr = s_DICR; - s_DICR.UpdateMasterFlag(); - if (!old_dicr.master_flag && s_DICR.master_flag) + [[maybe_unused]] const auto old_dicr = s_state.DICR; + s_state.DICR.UpdateMasterFlag(); + if (!old_dicr.master_flag && s_state.DICR.master_flag) TRACE_LOG("Firing DMA master interrupt"); - InterruptController::SetLineState(InterruptController::IRQ::DMA, s_DICR.master_flag); + InterruptController::SetLineState(InterruptController::IRQ::DMA, s_state.DICR.master_flag); } ALWAYS_INLINE_RELEASE bool DMA::IsLinkedListTerminator(PhysicalMemoryAddress address) @@ -520,8 +527,8 @@ ALWAYS_INLINE_RELEASE bool DMA::CheckForBusError(Channel channel, ChannelState& { DEBUG_LOG("DMA bus error on channel {} at address 0x{:08X} size {}", channel, address, size); cs.channel_control.enable_busy = false; - s_DICR.bus_error = true; - s_DICR.SetIRQFlag(channel); + s_state.DICR.bus_error = true; + s_state.DICR.SetIRQFlag(channel); UpdateIRQ(); return true; } @@ -534,17 +541,17 @@ ALWAYS_INLINE_RELEASE void DMA::CompleteTransfer(Channel channel, ChannelState& // start/busy bit is cleared on end of transfer DEBUG_LOG("DMA transfer for channel {} complete", channel); cs.channel_control.enable_busy = false; - if (s_DICR.ShouldSetIRQFlag(channel)) + if (s_state.DICR.ShouldSetIRQFlag(channel)) { DEBUG_LOG("Setting DMA interrupt for channel {}", channel); - s_DICR.SetIRQFlag(channel); + s_state.DICR.SetIRQFlag(channel); UpdateIRQ(); } } TickCount DMA::GetMaxSliceTicks() { - const TickCount max = Pad::IsTransmitting() ? SLICE_SIZE_WHEN_TRANSMITTING_PAD : s_max_slice_ticks; + const TickCount max = Pad::IsTransmitting() ? SLICE_SIZE_WHEN_TRANSMITTING_PAD : s_state.max_slice_ticks; if (!TimingEvents::IsRunningEvents()) return max; @@ -556,7 +563,7 @@ TickCount DMA::GetMaxSliceTicks() template bool DMA::TransferChannel() { - ChannelState& cs = s_state[static_cast(channel)]; + ChannelState& cs = s_state.channels[static_cast(channel)]; const bool copy_to_device = cs.channel_control.copy_to_device; @@ -653,7 +660,7 @@ bool DMA::TransferChannel() if (cs.request) { // stall the transfer for a bit if we ran for too long - HaltTransfer(s_halt_ticks); + HaltTransfer(s_state.halt_ticks); return false; } else @@ -726,8 +733,8 @@ bool DMA::TransferChannel() if (cs.request) { // we got halted - if (!s_unhalt_event->IsActive()) - HaltTransfer(s_halt_ticks); + if (!s_state.unhalt_event->IsActive()) + HaltTransfer(s_state.halt_ticks); return false; } @@ -748,20 +755,20 @@ bool DMA::TransferChannel() void DMA::HaltTransfer(TickCount duration) { - s_halt_ticks_remaining += duration; - DEBUG_LOG("Halting DMA for {} ticks", s_halt_ticks_remaining); - if (s_unhalt_event->IsActive()) + s_state.halt_ticks_remaining += duration; + DEBUG_LOG("Halting DMA for {} ticks", s_state.halt_ticks_remaining); + if (s_state.unhalt_event->IsActive()) return; - DebugAssert(!s_unhalt_event->IsActive()); - s_unhalt_event->SetIntervalAndSchedule(s_halt_ticks_remaining); + DebugAssert(!s_state.unhalt_event->IsActive()); + s_state.unhalt_event->SetIntervalAndSchedule(s_state.halt_ticks_remaining); } void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late) { - DEBUG_LOG("Resuming DMA after {} ticks, {} ticks late", ticks, -(s_halt_ticks_remaining - ticks)); - s_halt_ticks_remaining -= ticks; - s_unhalt_event->Deactivate(); + DEBUG_LOG("Resuming DMA after {} ticks, {} ticks late", ticks, -(s_state.halt_ticks_remaining - ticks)); + s_state.halt_ticks_remaining -= ticks; + s_state.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. @@ -775,7 +782,7 @@ void DMA::UnhaltTransfer(void*, TickCount ticks, TickCount ticks_late) } // We didn't run too long, so reset timer. - s_halt_ticks_remaining = 0; + s_state.halt_ticks_remaining = 0; } template @@ -795,14 +802,14 @@ TickCount DMA::TransferMemoryToDevice(u32 address, u32 increment, u32 word_count if (static_cast(increment) < 0 || ((address + (increment * word_count)) & mask) <= address) [[unlikely]] { // Use temp buffer if it's wrapping around - if (s_transfer_buffer.size() < word_count) - s_transfer_buffer.resize(word_count); - src_pointer = s_transfer_buffer.data(); + if (s_state.transfer_buffer.size() < word_count) + s_state.transfer_buffer.resize(word_count); + src_pointer = s_state.transfer_buffer.data(); u8* ram_pointer = Bus::g_ram; for (u32 i = 0; i < word_count; i++) { - std::memcpy(&s_transfer_buffer[i], &ram_pointer[address], sizeof(u32)); + std::memcpy(&s_state.transfer_buffer[i], &ram_pointer[address], sizeof(u32)); address = (address + increment) & mask; } } @@ -879,9 +886,9 @@ TickCount DMA::TransferDeviceToMemory(u32 address, u32 increment, u32 word_count if (static_cast(increment) < 0 || ((address + (increment * word_count)) & mask) <= address) [[unlikely]] { // Use temp buffer if it's wrapping around - if (s_transfer_buffer.size() < word_count) - s_transfer_buffer.resize(word_count); - dest_pointer = s_transfer_buffer.data(); + if (s_state.transfer_buffer.size() < word_count) + s_state.transfer_buffer.resize(word_count); + dest_pointer = s_state.transfer_buffer.data(); } // Read from device. @@ -909,12 +916,12 @@ TickCount DMA::TransferDeviceToMemory(u32 address, u32 increment, u32 word_count break; } - if (dest_pointer == s_transfer_buffer.data()) [[unlikely]] + if (dest_pointer == s_state.transfer_buffer.data()) [[unlikely]] { u8* ram_pointer = Bus::g_ram; for (u32 i = 0; i < word_count; i++) { - std::memcpy(&ram_pointer[address], &s_transfer_buffer[i], sizeof(u32)); + std::memcpy(&ram_pointer[address], &s_state.transfer_buffer[i], sizeof(u32)); address = (address + increment) & mask; } } @@ -961,7 +968,7 @@ void DMA::DrawDebugStateWindow() for (u32 i = 0; i < NUM_CHANNELS; i++) { - const ChannelState& cs = s_state[i]; + const ChannelState& cs = s_state.channels[i]; ImGui::TextColored(cs.channel_control.enable_busy ? active : inactive, "%u[%s]", i, s_channel_names[i]); ImGui::NextColumn(); @@ -981,17 +988,17 @@ void DMA::DrawDebugStateWindow() cs.channel_control.enable_busy ? "Busy" : "Idle", cs.channel_control.start_trigger ? " (Trigger)" : ""); ImGui::NextColumn(); - ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, - s_DPCR.GetMasterEnable(static_cast(i)) ? "Enabled" : "Disabled"); + ImGui::TextColored(s_state.DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, + s_state.DPCR.GetMasterEnable(static_cast(i)) ? "Enabled" : "Disabled"); ImGui::NextColumn(); - ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, "%u", - s_DPCR.GetPriority(static_cast(i))); + ImGui::TextColored(s_state.DPCR.GetMasterEnable(static_cast(i)) ? active : inactive, "%u", + s_state.DPCR.GetPriority(static_cast(i))); ImGui::NextColumn(); - ImGui::TextColored(s_DICR.GetIRQEnabled(static_cast(i)) ? active : inactive, - s_DICR.GetIRQEnabled(static_cast(i)) ? "Enabled" : "Disabled"); + ImGui::TextColored(s_state.DICR.GetIRQEnabled(static_cast(i)) ? active : inactive, + s_state.DICR.GetIRQEnabled(static_cast(i)) ? "Enabled" : "Disabled"); ImGui::NextColumn(); - ImGui::TextColored(s_DICR.GetIRQFlag(static_cast(i)) ? active : inactive, - s_DICR.GetIRQFlag(static_cast(i)) ? "IRQ" : ""); + ImGui::TextColored(s_state.DICR.GetIRQFlag(static_cast(i)) ? active : inactive, + s_state.DICR.GetIRQFlag(static_cast(i)) ? "IRQ" : ""); ImGui::NextColumn(); }