mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
DMA: Convert to namespace
This commit is contained in:
parent
d096a09ed8
commit
3acf569d67
|
@ -1331,13 +1331,13 @@ ALWAYS_INLINE static TickCount DoDMAAccess(u32 offset, u32& value)
|
||||||
{
|
{
|
||||||
if constexpr (type == MemoryAccessType::Read)
|
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);
|
value = FIXUP_WORD_READ_VALUE(size, offset, value);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
else
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1172,7 +1172,7 @@ void CDROM::UpdateStatusRegister()
|
||||||
s_status.DRQSTS = !s_data_fifo.IsEmpty();
|
s_status.DRQSTS = !s_data_fifo.IsEmpty();
|
||||||
s_status.BUSYSTS = HasPendingCommand();
|
s_status.BUSYSTS = HasPendingCommand();
|
||||||
|
|
||||||
g_dma.SetRequest(DMA::Channel::CDROM, s_status.DRQSTS);
|
DMA::SetRequest(DMA::Channel::CDROM, s_status.DRQSTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::UpdateInterruptRequest()
|
void CDROM::UpdateInterruptRequest()
|
||||||
|
|
341
src/core/dma.cpp
341
src/core/dma.cpp
|
@ -4,6 +4,7 @@
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
#include "cdrom.h"
|
#include "cdrom.h"
|
||||||
|
#include "common/bitfield.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "cpu_code_cache.h"
|
#include "cpu_code_cache.h"
|
||||||
|
@ -17,84 +18,238 @@
|
||||||
#include "spu.h"
|
#include "spu.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "util/state_wrapper.h"
|
#include "util/state_wrapper.h"
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
Log_SetChannel(DMA);
|
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<u32> s_transfer_buffer;
|
||||||
|
static std::unique_ptr<TimingEvent> s_unhalt_event;
|
||||||
|
static TickCount s_halt_ticks_remaining = 0;
|
||||||
|
|
||||||
|
struct ChannelState
|
||||||
|
{
|
||||||
|
u32 base_address = 0;
|
||||||
|
|
||||||
|
union BlockControl
|
||||||
|
{
|
||||||
|
u32 bits;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
BitField<u32, u32, 0, 16> word_count;
|
||||||
|
|
||||||
|
u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; }
|
||||||
|
} manual;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
BitField<u32, u32, 0, 16> block_size;
|
||||||
|
BitField<u32, u32, 16, 16> 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<u32, bool, 0, 1> copy_to_device;
|
||||||
|
BitField<u32, bool, 1, 1> address_step_reverse;
|
||||||
|
BitField<u32, bool, 8, 1> chopping_enable;
|
||||||
|
BitField<u32, SyncMode, 9, 2> sync_mode;
|
||||||
|
BitField<u32, u32, 16, 3> chopping_dma_window_size;
|
||||||
|
BitField<u32, u32, 20, 3> chopping_cpu_window_size;
|
||||||
|
BitField<u32, bool, 24, 1> enable_busy;
|
||||||
|
BitField<u32, bool, 28, 1> start_trigger;
|
||||||
|
|
||||||
|
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
|
||||||
|
} channel_control = {};
|
||||||
|
|
||||||
|
bool request = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::array<ChannelState, NUM_CHANNELS> s_state;
|
||||||
|
|
||||||
|
union DPCR
|
||||||
|
{
|
||||||
|
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)); }
|
||||||
|
bool GetMasterEnable(Channel channel) const
|
||||||
|
{
|
||||||
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(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<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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static DICR s_DICR = {};
|
||||||
|
}; // namespace DMA
|
||||||
|
|
||||||
|
u32 DMA::GetAddressMask()
|
||||||
{
|
{
|
||||||
return Bus::g_ram_mask & 0xFFFFFFFCu;
|
return Bus::g_ram_mask & 0xFFFFFFFCu;
|
||||||
}
|
}
|
||||||
|
|
||||||
DMA g_dma;
|
|
||||||
|
|
||||||
DMA::DMA() = default;
|
|
||||||
|
|
||||||
DMA::~DMA() = default;
|
|
||||||
|
|
||||||
void DMA::Initialize()
|
void DMA::Initialize()
|
||||||
{
|
{
|
||||||
m_max_slice_ticks = g_settings.dma_max_slice_ticks;
|
s_max_slice_ticks = g_settings.dma_max_slice_ticks;
|
||||||
m_halt_ticks = g_settings.dma_halt_ticks;
|
s_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<DMA*>(param)->UnhaltTransfer(ticks); }, this,
|
|
||||||
false);
|
|
||||||
|
|
||||||
|
s_transfer_buffer.resize(32);
|
||||||
|
s_unhalt_event =
|
||||||
|
TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, s_max_slice_ticks, &DMA::UnhaltTransfer, nullptr, false);
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::Shutdown()
|
void DMA::Shutdown()
|
||||||
{
|
{
|
||||||
ClearState();
|
ClearState();
|
||||||
m_unhalt_event.reset();
|
s_unhalt_event.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::Reset()
|
void DMA::Reset()
|
||||||
{
|
{
|
||||||
ClearState();
|
ClearState();
|
||||||
m_unhalt_event->Deactivate();
|
s_unhalt_event->Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::ClearState()
|
void DMA::ClearState()
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||||
{
|
{
|
||||||
ChannelState& cs = m_state[i];
|
ChannelState& cs = s_state[i];
|
||||||
cs.base_address = 0;
|
cs.base_address = 0;
|
||||||
cs.block_control.bits = 0;
|
cs.block_control.bits = 0;
|
||||||
cs.channel_control.bits = 0;
|
cs.channel_control.bits = 0;
|
||||||
cs.request = false;
|
cs.request = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_DPCR.bits = 0x07654321;
|
s_DPCR.bits = 0x07654321;
|
||||||
m_DICR.bits = 0;
|
s_DICR.bits = 0;
|
||||||
|
|
||||||
m_halt_ticks_remaining = 0;
|
s_halt_ticks_remaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DMA::DoState(StateWrapper& sw)
|
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++)
|
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.base_address);
|
||||||
sw.Do(&cs.block_control.bits);
|
sw.Do(&cs.block_control.bits);
|
||||||
sw.Do(&cs.channel_control.bits);
|
sw.Do(&cs.channel_control.bits);
|
||||||
sw.Do(&cs.request);
|
sw.Do(&cs.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Do(&m_DPCR.bits);
|
sw.Do(&s_DPCR.bits);
|
||||||
sw.Do(&m_DICR.bits);
|
sw.Do(&s_DICR.bits);
|
||||||
|
|
||||||
if (sw.IsReading())
|
if (sw.IsReading())
|
||||||
{
|
{
|
||||||
if (m_halt_ticks_remaining > 0)
|
if (s_halt_ticks_remaining > 0)
|
||||||
m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining);
|
s_unhalt_event->SetIntervalAndSchedule(s_halt_ticks_remaining);
|
||||||
else
|
else
|
||||||
m_unhalt_event->Deactivate();
|
s_unhalt_event->Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
|
@ -109,18 +264,18 @@ u32 DMA::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00:
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DMA%u base address -> 0x%08X", channel_index, m_state[channel_index].base_address);
|
Log_TracePrintf("DMA%u base address -> 0x%08X", channel_index, s_state[channel_index].base_address);
|
||||||
return m_state[channel_index].base_address;
|
return s_state[channel_index].base_address;
|
||||||
}
|
}
|
||||||
case 0x04:
|
case 0x04:
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DMA%u block control -> 0x%08X", channel_index, 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 m_state[channel_index].block_control.bits;
|
return s_state[channel_index].block_control.bits;
|
||||||
}
|
}
|
||||||
case 0x08:
|
case 0x08:
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DMA%u channel control -> 0x%08X", channel_index, 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 m_state[channel_index].channel_control.bits;
|
return s_state[channel_index].channel_control.bits;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -130,13 +285,13 @@ u32 DMA::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
if (offset == 0x70)
|
if (offset == 0x70)
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DPCR -> 0x%08X", m_DPCR.bits);
|
Log_TracePrintf("DPCR -> 0x%08X", s_DPCR.bits);
|
||||||
return m_DPCR.bits;
|
return s_DPCR.bits;
|
||||||
}
|
}
|
||||||
else if (offset == 0x74)
|
else if (offset == 0x74)
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DPCR -> 0x%08X", m_DPCR.bits);
|
Log_TracePrintf("DPCR -> 0x%08X", s_DPCR.bits);
|
||||||
return m_DICR.bits;
|
return s_DICR.bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +304,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
const u32 channel_index = offset >> 4;
|
const u32 channel_index = offset >> 4;
|
||||||
if (channel_index < 7)
|
if (channel_index < 7)
|
||||||
{
|
{
|
||||||
ChannelState& state = m_state[channel_index];
|
ChannelState& state = s_state[channel_index];
|
||||||
switch (offset & UINT32_C(0x0F))
|
switch (offset & UINT32_C(0x0F))
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00:
|
||||||
|
@ -197,7 +352,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
case 0x70:
|
case 0x70:
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DPCR <- 0x%08X", value);
|
Log_TracePrintf("DPCR <- 0x%08X", value);
|
||||||
m_DPCR.bits = value;
|
s_DPCR.bits = value;
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
||||||
{
|
{
|
||||||
|
@ -214,9 +369,9 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
case 0x74:
|
case 0x74:
|
||||||
{
|
{
|
||||||
Log_TracePrintf("DCIR <- 0x%08X", value);
|
Log_TracePrintf("DCIR <- 0x%08X", value);
|
||||||
m_DICR.bits = (m_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK);
|
s_DICR.bits = (s_DICR.bits & ~DICR_WRITE_MASK) | (value & DICR_WRITE_MASK);
|
||||||
m_DICR.bits = m_DICR.bits & ~(value & DICR_RESET_MASK);
|
s_DICR.bits = s_DICR.bits & ~(value & DICR_RESET_MASK);
|
||||||
m_DICR.UpdateMasterFlag();
|
s_DICR.UpdateMasterFlag();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +385,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
|
|
||||||
void DMA::SetRequest(Channel channel, bool request)
|
void DMA::SetRequest(Channel channel, bool request)
|
||||||
{
|
{
|
||||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
ChannelState& cs = s_state[static_cast<u32>(channel)];
|
||||||
if (cs.request == request)
|
if (cs.request == request)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -239,12 +394,22 @@ void DMA::SetRequest(Channel channel, bool request)
|
||||||
TransferChannel(channel);
|
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;
|
return false;
|
||||||
|
|
||||||
const ChannelState& cs = m_state[static_cast<u32>(channel)];
|
const ChannelState& cs = s_state[static_cast<u32>(channel)];
|
||||||
if (!cs.channel_control.enable_busy)
|
if (!cs.channel_control.enable_busy)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -254,15 +419,15 @@ bool DMA::CanTransferChannel(Channel channel, bool ignore_halt) const
|
||||||
return cs.request;
|
return cs.request;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DMA::IsTransferHalted() const
|
bool DMA::IsTransferHalted()
|
||||||
{
|
{
|
||||||
return m_unhalt_event->IsActive();
|
return s_unhalt_event->IsActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::UpdateIRQ()
|
void DMA::UpdateIRQ()
|
||||||
{
|
{
|
||||||
m_DICR.UpdateMasterFlag();
|
s_DICR.UpdateMasterFlag();
|
||||||
if (m_DICR.master_flag)
|
if (s_DICR.master_flag)
|
||||||
{
|
{
|
||||||
Log_TracePrintf("Firing DMA master interrupt");
|
Log_TracePrintf("Firing DMA master interrupt");
|
||||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::DMA);
|
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::DMA);
|
||||||
|
@ -279,27 +444,27 @@ enum : u32
|
||||||
HALT_TICKS_WHEN_TRANSMITTING_PAD = 100
|
HALT_TICKS_WHEN_TRANSMITTING_PAD = 100
|
||||||
};
|
};
|
||||||
|
|
||||||
TickCount DMA::GetTransferSliceTicks() const
|
TickCount DMA::GetTransferSliceTicks()
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
if (g_pad.IsTransmitting())
|
if (g_pad.IsTransmitting())
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("DMA transfer while transmitting pad - using lower slice size of %u vs %u",
|
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
|
#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)
|
bool DMA::TransferChannel(Channel channel)
|
||||||
{
|
{
|
||||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
ChannelState& cs = s_state[static_cast<u32>(channel)];
|
||||||
const u32 mask = GetAddressMask();
|
const u32 mask = GetAddressMask();
|
||||||
|
|
||||||
const bool copy_to_device = cs.channel_control.copy_to_device;
|
const bool copy_to_device = cs.channel_control.copy_to_device;
|
||||||
|
@ -433,7 +598,7 @@ bool DMA::TransferChannel(Channel channel)
|
||||||
if (cs.request)
|
if (cs.request)
|
||||||
{
|
{
|
||||||
// we got halted
|
// we got halted
|
||||||
if (!m_unhalt_event->IsActive())
|
if (!s_unhalt_event->IsActive())
|
||||||
HaltTransfer(GetTransferHaltTicks());
|
HaltTransfer(GetTransferHaltTicks());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -451,10 +616,10 @@ bool DMA::TransferChannel(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))
|
if (s_DICR.IsIRQEnabled(channel))
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("Set DMA interrupt for channel %u", static_cast<u32>(channel));
|
Log_DebugPrintf("Set DMA interrupt for channel %u", static_cast<u32>(channel));
|
||||||
m_DICR.SetIRQFlag(channel);
|
s_DICR.SetIRQFlag(channel);
|
||||||
UpdateIRQ();
|
UpdateIRQ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,20 +628,20 @@ bool DMA::TransferChannel(Channel channel)
|
||||||
|
|
||||||
void DMA::HaltTransfer(TickCount duration)
|
void DMA::HaltTransfer(TickCount duration)
|
||||||
{
|
{
|
||||||
m_halt_ticks_remaining += duration;
|
s_halt_ticks_remaining += duration;
|
||||||
Log_DebugPrintf("Halting DMA for %d ticks", m_halt_ticks_remaining);
|
Log_DebugPrintf("Halting DMA for %d ticks", s_halt_ticks_remaining);
|
||||||
if (m_unhalt_event->IsActive())
|
if (s_unhalt_event->IsActive())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DebugAssert(!m_unhalt_event->IsActive());
|
DebugAssert(!s_unhalt_event->IsActive());
|
||||||
m_unhalt_event->SetIntervalAndSchedule(m_halt_ticks_remaining);
|
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));
|
Log_DebugPrintf("Resuming DMA after %d ticks, %d ticks late", ticks, -(s_halt_ticks_remaining - ticks));
|
||||||
m_halt_ticks_remaining -= ticks;
|
s_halt_ticks_remaining -= ticks;
|
||||||
m_unhalt_event->Deactivate();
|
s_unhalt_event->Deactivate();
|
||||||
|
|
||||||
// TODO: Use channel priority. But doing it in ascending order is probably good enough.
|
// 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.
|
// 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.
|
// 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)
|
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<s32>(increment) < 0 || ((address + (increment * word_count)) & mask) <= address))
|
(static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & mask) <= address))
|
||||||
{
|
{
|
||||||
// Use temp buffer if it's wrapping around
|
// Use temp buffer if it's wrapping around
|
||||||
if (m_transfer_buffer.size() < word_count)
|
if (s_transfer_buffer.size() < word_count)
|
||||||
m_transfer_buffer.resize(word_count);
|
s_transfer_buffer.resize(word_count);
|
||||||
src_pointer = m_transfer_buffer.data();
|
src_pointer = s_transfer_buffer.data();
|
||||||
|
|
||||||
u8* ram_pointer = Bus::g_ram;
|
u8* ram_pointer = Bus::g_ram;
|
||||||
for (u32 i = 0; i < word_count; i++)
|
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;
|
address = (address + increment) & mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -577,9 +742,9 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
||||||
if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & mask) <= address)
|
if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & mask) <= address)
|
||||||
{
|
{
|
||||||
// Use temp buffer if it's wrapping around
|
// Use temp buffer if it's wrapping around
|
||||||
if (m_transfer_buffer.size() < word_count)
|
if (s_transfer_buffer.size() < word_count)
|
||||||
m_transfer_buffer.resize(word_count);
|
s_transfer_buffer.resize(word_count);
|
||||||
dest_pointer = m_transfer_buffer.data();
|
dest_pointer = s_transfer_buffer.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from device.
|
// Read from device.
|
||||||
|
@ -607,12 +772,12 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dest_pointer == m_transfer_buffer.data())
|
if (dest_pointer == s_transfer_buffer.data())
|
||||||
{
|
{
|
||||||
u8* ram_pointer = Bus::g_ram;
|
u8* ram_pointer = Bus::g_ram;
|
||||||
for (u32 i = 0; i < word_count; i++)
|
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;
|
address = (address + increment) & mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,7 +827,7 @@ void DMA::DrawDebugStateWindow()
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CHANNELS; i++)
|
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::TextColored(cs.channel_control.enable_busy ? active : inactive, "%u[%s]", i, channel_names[i]);
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
|
@ -682,17 +847,17 @@ void DMA::DrawDebugStateWindow()
|
||||||
cs.channel_control.enable_busy ? "Busy" : "Idle",
|
cs.channel_control.enable_busy ? "Busy" : "Idle",
|
||||||
cs.channel_control.start_trigger ? " (Trigger)" : "");
|
cs.channel_control.start_trigger ? " (Trigger)" : "");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(m_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive,
|
ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive,
|
||||||
m_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
s_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(m_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive, "%u",
|
ImGui::TextColored(s_DPCR.GetMasterEnable(static_cast<Channel>(i)) ? active : inactive, "%u",
|
||||||
m_DPCR.GetPriority(static_cast<Channel>(i)));
|
s_DPCR.GetPriority(static_cast<Channel>(i)));
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(m_DICR.IsIRQEnabled(static_cast<Channel>(i)) ? active : inactive,
|
ImGui::TextColored(s_DICR.IsIRQEnabled(static_cast<Channel>(i)) ? active : inactive,
|
||||||
m_DICR.IsIRQEnabled(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
s_DICR.IsIRQEnabled(static_cast<Channel>(i)) ? "Enabled" : "Disabled");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
ImGui::TextColored(m_DICR.GetIRQFlag(static_cast<Channel>(i)) ? active : inactive,
|
ImGui::TextColored(s_DICR.GetIRQFlag(static_cast<Channel>(i)) ? active : inactive,
|
||||||
m_DICR.GetIRQFlag(static_cast<Channel>(i)) ? "IRQ" : "");
|
s_DICR.GetIRQFlag(static_cast<Channel>(i)) ? "IRQ" : "");
|
||||||
ImGui::NextColumn();
|
ImGui::NextColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
227
src/core/dma.h
227
src/core/dma.h
|
@ -2,207 +2,42 @@
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "common/bitfield.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class StateWrapper;
|
class StateWrapper;
|
||||||
|
|
||||||
class TimingEvent;
|
namespace DMA {
|
||||||
|
|
||||||
class DMA
|
enum : u32
|
||||||
{
|
{
|
||||||
public:
|
NUM_CHANNELS = 7
|
||||||
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<u32> m_transfer_buffer;
|
|
||||||
std::unique_ptr<TimingEvent> m_unhalt_event;
|
|
||||||
TickCount m_halt_ticks_remaining = 0;
|
|
||||||
|
|
||||||
struct ChannelState
|
|
||||||
{
|
|
||||||
u32 base_address = 0;
|
|
||||||
|
|
||||||
union BlockControl
|
|
||||||
{
|
|
||||||
u32 bits;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
BitField<u32, u32, 0, 16> word_count;
|
|
||||||
|
|
||||||
u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; }
|
|
||||||
} manual;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
BitField<u32, u32, 0, 16> block_size;
|
|
||||||
BitField<u32, u32, 16, 16> 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<u32, bool, 0, 1> copy_to_device;
|
|
||||||
BitField<u32, bool, 1, 1> address_step_reverse;
|
|
||||||
BitField<u32, bool, 8, 1> chopping_enable;
|
|
||||||
BitField<u32, SyncMode, 9, 2> sync_mode;
|
|
||||||
BitField<u32, u32, 16, 3> chopping_dma_window_size;
|
|
||||||
BitField<u32, u32, 20, 3> chopping_cpu_window_size;
|
|
||||||
BitField<u32, bool, 24, 1> enable_busy;
|
|
||||||
BitField<u32, bool, 28, 1> start_trigger;
|
|
||||||
|
|
||||||
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
|
|
||||||
} channel_control = {};
|
|
||||||
|
|
||||||
bool request = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<ChannelState, NUM_CHANNELS> m_state;
|
|
||||||
|
|
||||||
union DPCR
|
|
||||||
{
|
|
||||||
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)); }
|
|
||||||
bool GetMasterEnable(Channel channel) const
|
|
||||||
{
|
|
||||||
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(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<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 = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern DMA g_dma;
|
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
|
||||||
|
|
|
@ -341,7 +341,7 @@ void GPU::UpdateDMARequest()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_GPUSTAT.dma_data_request = dma_request;
|
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()
|
void GPU::UpdateGPUIdle()
|
||||||
|
|
|
@ -307,12 +307,12 @@ void MDEC::UpdateStatus()
|
||||||
// we always want data in if it's enabled
|
// 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);
|
const bool data_in_request = s_enable_dma_in && s_data_in_fifo.GetSpace() >= (32 * 2);
|
||||||
s_status.data_in_request = data_in_request;
|
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
|
// 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();
|
const bool data_out_request = s_enable_dma_out && !s_data_out_fifo.IsEmpty();
|
||||||
s_status.data_out_request = data_out_request;
|
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()
|
u32 MDEC::ReadDataRegister()
|
||||||
|
|
|
@ -1349,7 +1349,7 @@ void SPU::UpdateDMARequest()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This might call us back directly.
|
// 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)
|
void SPU::DMARead(u32* words, u32 word_count)
|
||||||
|
|
|
@ -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 code cache must happen after GPU, because it might steal our address space.
|
||||||
CPU::CodeCache::Initialize();
|
CPU::CodeCache::Initialize();
|
||||||
|
|
||||||
g_dma.Initialize();
|
DMA::Initialize();
|
||||||
g_interrupt_controller.Initialize();
|
g_interrupt_controller.Initialize();
|
||||||
|
|
||||||
CDROM::Initialize();
|
CDROM::Initialize();
|
||||||
|
@ -1458,7 +1458,7 @@ void System::DestroySystem()
|
||||||
CDROM::Shutdown();
|
CDROM::Shutdown();
|
||||||
g_gpu.reset();
|
g_gpu.reset();
|
||||||
g_interrupt_controller.Shutdown();
|
g_interrupt_controller.Shutdown();
|
||||||
g_dma.Shutdown();
|
DMA::Shutdown();
|
||||||
PGXP::Shutdown();
|
PGXP::Shutdown();
|
||||||
CPU::CodeCache::Shutdown();
|
CPU::CodeCache::Shutdown();
|
||||||
Bus::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))
|
if (!sw.DoMarker("Bus") || !Bus::DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!sw.DoMarker("DMA") || !g_dma.DoState(sw))
|
if (!sw.DoMarker("DMA") || !DMA::DoState(sw))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!sw.DoMarker("InterruptController") || !g_interrupt_controller.DoState(sw))
|
if (!sw.DoMarker("InterruptController") || !g_interrupt_controller.DoState(sw))
|
||||||
|
@ -1739,7 +1739,7 @@ void System::InternalReset()
|
||||||
PGXP::Initialize();
|
PGXP::Initialize();
|
||||||
|
|
||||||
Bus::Reset();
|
Bus::Reset();
|
||||||
g_dma.Reset();
|
DMA::Reset();
|
||||||
g_interrupt_controller.Reset();
|
g_interrupt_controller.Reset();
|
||||||
g_gpu->Reset(true);
|
g_gpu->Reset(true);
|
||||||
CDROM::Reset();
|
CDROM::Reset();
|
||||||
|
@ -3299,8 +3299,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
g_texture_replacements.Reload();
|
g_texture_replacements.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks);
|
DMA::SetMaxSliceTicks(g_settings.dma_max_slice_ticks);
|
||||||
g_dma.SetHaltTicks(g_settings.dma_halt_ticks);
|
DMA::SetHaltTicks(g_settings.dma_halt_ticks);
|
||||||
|
|
||||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||||
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
|
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
|
||||||
|
|
|
@ -490,7 +490,7 @@ void ImGuiManager::RenderDebugWindows()
|
||||||
if (g_settings.debugging.show_mdec_state)
|
if (g_settings.debugging.show_mdec_state)
|
||||||
MDEC::DrawDebugStateWindow();
|
MDEC::DrawDebugStateWindow();
|
||||||
if (g_settings.debugging.show_dma_state)
|
if (g_settings.debugging.show_dma_state)
|
||||||
g_dma.DrawDebugStateWindow();
|
DMA::DrawDebugStateWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue