mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-02-06 14:25:39 +00:00
DMA: Implement interrupts
This commit is contained in:
parent
db777fdabb
commit
4cc83e2228
|
@ -4,15 +4,17 @@
|
||||||
#include "cdrom.h"
|
#include "cdrom.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
|
#include "interrupt_controller.h"
|
||||||
Log_SetChannel(DMA);
|
Log_SetChannel(DMA);
|
||||||
|
|
||||||
DMA::DMA() = default;
|
DMA::DMA() = default;
|
||||||
|
|
||||||
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_bus = bus;
|
||||||
|
m_interrupt_controller = interrupt_controller;
|
||||||
m_gpu = gpu;
|
m_gpu = gpu;
|
||||||
m_cdrom = cdrom;
|
m_cdrom = cdrom;
|
||||||
return true;
|
return true;
|
||||||
|
@ -22,7 +24,7 @@ void DMA::Reset()
|
||||||
{
|
{
|
||||||
m_state = {};
|
m_state = {};
|
||||||
m_DPCR.bits = 0x07654321;
|
m_DPCR.bits = 0x07654321;
|
||||||
m_DCIR = 0;
|
m_DICR.bits = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DMA::DoState(StateWrapper& sw)
|
bool DMA::DoState(StateWrapper& sw)
|
||||||
|
@ -37,7 +39,7 @@ bool DMA::DoState(StateWrapper& sw)
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Do(&m_DPCR.bits);
|
sw.Do(&m_DPCR.bits);
|
||||||
sw.Do(&m_DCIR);
|
sw.Do(&m_DICR.bits);
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +65,7 @@ u32 DMA::ReadRegister(u32 offset)
|
||||||
if (offset == 0x70)
|
if (offset == 0x70)
|
||||||
return m_DPCR.bits;
|
return m_DPCR.bits;
|
||||||
else if (offset == 0x74)
|
else if (offset == 0x74)
|
||||||
return m_DCIR;
|
return m_DICR.bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log_ErrorPrintf("Unhandled register read: %02X", offset);
|
Log_ErrorPrintf("Unhandled register read: %02X", offset);
|
||||||
|
@ -119,8 +121,10 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
|
|
||||||
case 0x74:
|
case 0x74:
|
||||||
{
|
{
|
||||||
m_DCIR = (m_DCIR & ~DCIR_WRITE_MASK) | (value & DCIR_WRITE_MASK);
|
Log_DebugPrintf("DCIR <- 0x%08X", value);
|
||||||
Log_DebugPrintf("DCIR <- 0x%08X", m_DCIR);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,6 +297,17 @@ void DMA::RunDMA(Channel channel)
|
||||||
|
|
||||||
// start/busy bit is cleared on end of transfer
|
// start/busy bit is cleared on end of transfer
|
||||||
cs.channel_control.enable_busy = false;
|
cs.channel_control.enable_busy = false;
|
||||||
|
if (m_DICR.IsIRQEnabled(channel))
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Set DMA interrupt for channel %u", static_cast<u32>(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)
|
u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
class Bus;
|
class Bus;
|
||||||
|
class InterruptController;
|
||||||
class GPU;
|
class GPU;
|
||||||
class CDROM;
|
class CDROM;
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ public:
|
||||||
DMA();
|
DMA();
|
||||||
~DMA();
|
~DMA();
|
||||||
|
|
||||||
bool Initialize(Bus* bus, GPU* gpu, CDROM* cdrom);
|
bool Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom);
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ private:
|
||||||
void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words);
|
void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words);
|
||||||
|
|
||||||
Bus* m_bus = nullptr;
|
Bus* m_bus = nullptr;
|
||||||
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
GPU* m_gpu = nullptr;
|
GPU* m_gpu = nullptr;
|
||||||
CDROM* m_cdrom = nullptr;
|
CDROM* m_cdrom = nullptr;
|
||||||
|
|
||||||
|
@ -105,14 +107,32 @@ private:
|
||||||
} channel_control;
|
} channel_control;
|
||||||
|
|
||||||
bool request = false;
|
bool request = false;
|
||||||
|
bool irq = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<ChannelState, NUM_CHANNELS> m_state = {};
|
std::array<ChannelState, NUM_CHANNELS> m_state = {};
|
||||||
|
|
||||||
struct DPCR
|
union DPCR
|
||||||
{
|
{
|
||||||
u32 bits;
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u32, u8, 0, 3> MDECin_priority;
|
||||||
|
BitField<u32, bool, 3, 1> MDECin_master_enable;
|
||||||
|
BitField<u32, u8, 4, 3> MDECout_priority;
|
||||||
|
BitField<u32, bool, 7, 1> MDECout_master_enable;
|
||||||
|
BitField<u32, u8, 8, 3> GPU_priority;
|
||||||
|
BitField<u32, bool, 10, 1> GPU_master_enable;
|
||||||
|
BitField<u32, u8, 12, 3> CDROM_priority;
|
||||||
|
BitField<u32, bool, 15, 1> CDROM_master_enable;
|
||||||
|
BitField<u32, u8, 16, 3> SPU_priority;
|
||||||
|
BitField<u32, bool, 19, 1> SPU_master_enable;
|
||||||
|
BitField<u32, u8, 20, 3> PIO_priority;
|
||||||
|
BitField<u32, bool, 23, 1> PIO_master_enable;
|
||||||
|
BitField<u32, u8, 24, 3> OTC_priority;
|
||||||
|
BitField<u32, bool, 27, 1> OTC_master_enable;
|
||||||
|
BitField<u32, u8, 28, 3> priority_offset;
|
||||||
|
BitField<u32, bool, 31, 1> unused;
|
||||||
|
|
||||||
u8 GetPriority(Channel channel) const { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
|
u8 GetPriority(Channel channel) const { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
|
||||||
bool GetMasterEnable(Channel channel) const
|
bool GetMasterEnable(Channel channel) const
|
||||||
{
|
{
|
||||||
|
@ -120,6 +140,46 @@ private:
|
||||||
}
|
}
|
||||||
} m_DPCR;
|
} m_DPCR;
|
||||||
|
|
||||||
static constexpr u32 DCIR_WRITE_MASK = 0b11111111'11111111'10000000'00111111;
|
static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'00111111;
|
||||||
u32 m_DCIR = 0;
|
static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000;
|
||||||
|
union DICR
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
|
||||||
|
BitField<u32, bool, 15, 1> force_irq;
|
||||||
|
BitField<u32, bool, 16, 1> MDECin_irq_enable;
|
||||||
|
BitField<u32, bool, 17, 1> MDECout_irq_enable;
|
||||||
|
BitField<u32, bool, 18, 1> GPU_irq_enable;
|
||||||
|
BitField<u32, bool, 19, 1> CDROM_irq_enable;
|
||||||
|
BitField<u32, bool, 20, 1> SPU_irq_enable;
|
||||||
|
BitField<u32, bool, 21, 1> PIO_irq_enable;
|
||||||
|
BitField<u32, bool, 22, 1> OTC_irq_enable;
|
||||||
|
BitField<u32, bool, 23, 1> master_enable;
|
||||||
|
BitField<u32, bool, 24, 1> MDECin_irq_flag;
|
||||||
|
BitField<u32, bool, 25, 1> MDECout_irq_flag;
|
||||||
|
BitField<u32, bool, 26, 1> GPU_irq_flag;
|
||||||
|
BitField<u32, bool, 27, 1> CDROM_irq_flag;
|
||||||
|
BitField<u32, bool, 28, 1> SPU_irq_flag;
|
||||||
|
BitField<u32, bool, 29, 1> PIO_irq_flag;
|
||||||
|
BitField<u32, bool, 30, 1> OTC_irq_flag;
|
||||||
|
BitField<u32, bool, 31, 1> master_flag;
|
||||||
|
|
||||||
|
bool IsIRQEnabled(Channel channel) const
|
||||||
|
{
|
||||||
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) + 16)) & u32(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetIRQFlag(Channel channel) const
|
||||||
|
{
|
||||||
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) + 24)) & u32(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIRQFlag(Channel channel) { bits |= (u32(1) << (static_cast<u8>(channel) + 24)); }
|
||||||
|
void ClearIRQFlag(Channel channel) { bits &= ~(u32(1) << (static_cast<u8>(channel) + 24)); }
|
||||||
|
|
||||||
|
void UpdateMasterFlag()
|
||||||
|
{
|
||||||
|
master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0);
|
||||||
|
}
|
||||||
|
} m_DICR = {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,7 +39,7 @@ bool System::Initialize()
|
||||||
return false;
|
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;
|
return false;
|
||||||
|
|
||||||
if (!m_interrupt_controller->Initialize(m_cpu.get()))
|
if (!m_interrupt_controller->Initialize(m_cpu.get()))
|
||||||
|
|
Loading…
Reference in a new issue