diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index 80fc49c49..887f9b070 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -4,15 +4,17 @@ #include "cdrom.h" #include "common/state_wrapper.h" #include "gpu.h" +#include "interrupt_controller.h" Log_SetChannel(DMA); DMA::DMA() = default; DMA::~DMA() = default; -bool DMA::Initialize(Bus* bus, GPU* gpu, CDROM* cdrom) +bool DMA::Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom) { m_bus = bus; + m_interrupt_controller = interrupt_controller; m_gpu = gpu; m_cdrom = cdrom; return true; @@ -22,7 +24,7 @@ void DMA::Reset() { m_state = {}; m_DPCR.bits = 0x07654321; - m_DCIR = 0; + m_DICR.bits = 0; } bool DMA::DoState(StateWrapper& sw) @@ -37,7 +39,7 @@ bool DMA::DoState(StateWrapper& sw) } sw.Do(&m_DPCR.bits); - sw.Do(&m_DCIR); + sw.Do(&m_DICR.bits); return !sw.HasError(); } @@ -63,7 +65,7 @@ u32 DMA::ReadRegister(u32 offset) if (offset == 0x70) return m_DPCR.bits; else if (offset == 0x74) - return m_DCIR; + return m_DICR.bits; } Log_ErrorPrintf("Unhandled register read: %02X", offset); @@ -119,8 +121,10 @@ void DMA::WriteRegister(u32 offset, u32 value) case 0x74: { - m_DCIR = (m_DCIR & ~DCIR_WRITE_MASK) | (value & DCIR_WRITE_MASK); - Log_DebugPrintf("DCIR <- 0x%08X", m_DCIR); + Log_DebugPrintf("DCIR <- 0x%08X", value); + m_DICR.bits = (m_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK); + m_DICR.bits = (m_DICR.bits & ~DICR_RESET_MASK) & (value ^ DICR_RESET_MASK); + m_DICR.UpdateMasterFlag(); return; } @@ -293,6 +297,17 @@ void DMA::RunDMA(Channel channel) // start/busy bit is cleared on end of transfer cs.channel_control.enable_busy = false; + if (m_DICR.IsIRQEnabled(channel)) + { + Log_DebugPrintf("Set DMA interrupt for channel %u", static_cast(channel)); + m_DICR.SetIRQFlag(channel); + m_DICR.UpdateMasterFlag(); + if (m_DICR.master_flag) + { + Log_DebugPrintf("Firing DMA interrupt"); + m_interrupt_controller->InterruptRequest(InterruptController::IRQ::DMA); + } + } } u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words) diff --git a/src/pse/dma.h b/src/pse/dma.h index 0bc08c6b4..6958c761b 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -6,6 +6,7 @@ class StateWrapper; class Bus; +class InterruptController; class GPU; class CDROM; @@ -31,7 +32,7 @@ public: DMA(); ~DMA(); - bool Initialize(Bus* bus, GPU* gpu, CDROM* cdrom); + bool Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom); void Reset(); bool DoState(StateWrapper& sw); @@ -63,6 +64,7 @@ private: void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words); Bus* m_bus = nullptr; + InterruptController* m_interrupt_controller = nullptr; GPU* m_gpu = nullptr; CDROM* m_cdrom = nullptr; @@ -105,14 +107,32 @@ private: } channel_control; bool request = false; + bool irq = false; }; std::array m_state = {}; - struct DPCR + 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 { @@ -120,6 +140,46 @@ private: } } m_DPCR; - static constexpr u32 DCIR_WRITE_MASK = 0b11111111'11111111'10000000'00111111; - u32 m_DCIR = 0; + 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 = {}; }; diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 36323e071..59f7f6648 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -39,7 +39,7 @@ bool System::Initialize() return false; } - if (!m_dma->Initialize(m_bus.get(), m_gpu.get(), m_cdrom.get())) + if (!m_dma->Initialize(m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get())) return false; if (!m_interrupt_controller->Initialize(m_cpu.get()))