DMA: Implement linked list mode
This commit is contained in:
parent
27913cd20a
commit
162f94337e
113
src/pse/dma.cpp
113
src/pse/dma.cpp
|
@ -1,6 +1,7 @@
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
|
#include "gpu.h"
|
||||||
Log_SetChannel(DMA);
|
Log_SetChannel(DMA);
|
||||||
|
|
||||||
DMA::DMA() = default;
|
DMA::DMA() = default;
|
||||||
|
@ -60,7 +61,7 @@ void DMA::WriteRegister(u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00:
|
||||||
{
|
{
|
||||||
state.base_address = value & BASE_ADDRESS_MASK;
|
state.base_address = value & ADDRESS_MASK;
|
||||||
Log_DebugPrintf("DMA channel %u base address <- 0x%08X", channel_index, state.base_address);
|
Log_DebugPrintf("DMA channel %u base address <- 0x%08X", channel_index, state.base_address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -136,44 +137,97 @@ bool DMA::CanRunChannel(Channel channel) const
|
||||||
void DMA::RunDMA(Channel channel)
|
void DMA::RunDMA(Channel channel)
|
||||||
{
|
{
|
||||||
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
ChannelState& cs = m_state[static_cast<u32>(channel)];
|
||||||
const PhysicalMemoryAddress memory_address = cs.base_address;
|
|
||||||
const bool copy_to_device = cs.channel_control.copy_to_device;
|
const bool copy_to_device = cs.channel_control.copy_to_device;
|
||||||
Log_DebugPrintf("Running DMA for channel %u", static_cast<u32>(channel));
|
Log_DebugPrintf("Running DMA for channel %u", static_cast<u32>(channel));
|
||||||
Assert(Common::IsAlignedPow2(memory_address, 4));
|
|
||||||
|
|
||||||
// start/trigger bit is cleared on beginning of transfer
|
// start/trigger bit is cleared on beginning of transfer
|
||||||
cs.channel_control.start_trigger = false;
|
cs.channel_control.start_trigger = false;
|
||||||
|
|
||||||
|
PhysicalMemoryAddress current_address = cs.base_address & ~UINT32_C(3);
|
||||||
|
const PhysicalMemoryAddress increment = cs.channel_control.address_step_reverse ? static_cast<u32>(-4) : UINT32_C(4);
|
||||||
switch (cs.channel_control.sync_mode)
|
switch (cs.channel_control.sync_mode)
|
||||||
{
|
{
|
||||||
case SyncMode::Manual:
|
case SyncMode::Manual:
|
||||||
{
|
{
|
||||||
const u32 word_count = cs.block_control.manual.GetWordCount();
|
const u32 word_count = cs.block_control.manual.GetWordCount();
|
||||||
Log_DebugPrintf(" ... copying %u words %s 0x%08X", word_count, copy_to_device ? "from" : "to", memory_address);
|
Log_DebugPrintf(" ... copying %u words %s 0x%08X", word_count, copy_to_device ? "from" : "to", current_address);
|
||||||
if (copy_to_device)
|
if (copy_to_device)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < word_count; i++)
|
u32 words_remaining = word_count;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
u32 memory_value = 0;
|
words_remaining--;
|
||||||
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(memory_address, memory_address,
|
|
||||||
memory_value);
|
u32 value = 0;
|
||||||
DMAWrite(channel, memory_value);
|
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(current_address, current_address,
|
||||||
}
|
value);
|
||||||
|
DMAWrite(channel, value, current_address, words_remaining);
|
||||||
|
|
||||||
|
current_address = (current_address + increment) & ADDRESS_MASK;
|
||||||
|
} while (words_remaining > 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < word_count; i++)
|
u32 words_remaining = word_count;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
u32 memory_value = DMARead(channel);
|
words_remaining--;
|
||||||
m_bus->DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(memory_address, memory_address,
|
|
||||||
|
u32 value = DMARead(channel, current_address, words_remaining);
|
||||||
|
m_bus->DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(current_address, current_address,
|
||||||
|
value);
|
||||||
|
|
||||||
|
current_address = (current_address + increment) & ADDRESS_MASK;
|
||||||
|
} while (words_remaining > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SyncMode::LinkedList:
|
||||||
|
{
|
||||||
|
if (!copy_to_device)
|
||||||
|
{
|
||||||
|
Panic("Linked list not implemented for DMA reads");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
u32 header;
|
||||||
|
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(current_address, current_address,
|
||||||
|
header);
|
||||||
|
|
||||||
|
const u32 word_count = header >> 24;
|
||||||
|
const u32 next_address = header & UINT32_C(0xFFFFFF);
|
||||||
|
Log_DebugPrintf(" .. linked list entry at 0x%08X size=%u(%u words) next=0x%08X", current_address,
|
||||||
|
word_count * UINT32_C(4), word_count, next_address);
|
||||||
|
current_address += sizeof(header);
|
||||||
|
|
||||||
|
if (word_count > 0)
|
||||||
|
{
|
||||||
|
u32 words_remaining = word_count;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
words_remaining--;
|
||||||
|
|
||||||
|
u32 memory_value = 0;
|
||||||
|
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(current_address, current_address,
|
||||||
memory_value);
|
memory_value);
|
||||||
|
DMAWrite(channel, memory_value, current_address, words_remaining);
|
||||||
|
current_address = (current_address + UINT32_C(4)) & ADDRESS_MASK;
|
||||||
|
} while (words_remaining > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_address & UINT32_C(0x800000))
|
||||||
|
break;
|
||||||
|
|
||||||
|
current_address = next_address & ADDRESS_MASK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SyncMode::Request:
|
case SyncMode::Request:
|
||||||
case SyncMode::LinkedList:
|
|
||||||
default:
|
default:
|
||||||
Panic("Unimplemented sync mode");
|
Panic("Unimplemented sync mode");
|
||||||
break;
|
break;
|
||||||
|
@ -183,29 +237,44 @@ void DMA::RunDMA(Channel channel)
|
||||||
cs.channel_control.enable_busy = false;
|
cs.channel_control.enable_busy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 DMA::DMARead(Channel channel)
|
u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words)
|
||||||
{
|
{
|
||||||
switch (channel)
|
switch (channel)
|
||||||
{
|
{
|
||||||
case Channel::OTC:
|
case Channel::OTC:
|
||||||
{
|
// clear ordering table
|
||||||
// we just return zeros here.. guessing it's pulled low?
|
return (remaining_words == 0) ? UINT32_C(0xFFFFFF) : ((dst_address - UINT32_C(4)) & ADDRESS_MASK);
|
||||||
return 0;
|
|
||||||
}
|
case Channel::GPU:
|
||||||
|
return m_gpu->DMARead();
|
||||||
|
|
||||||
case Channel::MDECin:
|
case Channel::MDECin:
|
||||||
case Channel::MDECout:
|
case Channel::MDECout:
|
||||||
case Channel::GPU:
|
|
||||||
case Channel::CDROM:
|
case Channel::CDROM:
|
||||||
case Channel::SPU:
|
case Channel::SPU:
|
||||||
case Channel::PIO:
|
case Channel::PIO:
|
||||||
default:
|
default:
|
||||||
Panic("Unhandled DMA channel write");
|
Panic("Unhandled DMA channel read");
|
||||||
return UINT32_C(0xFFFFFFFF);
|
return UINT32_C(0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA::DMAWrite(Channel channel, u32 value)
|
void DMA::DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words)
|
||||||
{
|
{
|
||||||
|
switch (channel)
|
||||||
|
{
|
||||||
|
case Channel::GPU:
|
||||||
|
m_gpu->DMAWrite(value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Channel::MDECin:
|
||||||
|
case Channel::MDECout:
|
||||||
|
case Channel::CDROM:
|
||||||
|
case Channel::SPU:
|
||||||
|
case Channel::PIO:
|
||||||
|
case Channel::OTC:
|
||||||
|
default:
|
||||||
Panic("Unhandled DMA channel write");
|
Panic("Unhandled DMA channel write");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
void SetRequest(Channel channel, bool request);
|
void SetRequest(Channel channel, bool request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF);
|
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF);
|
||||||
|
|
||||||
enum class SyncMode : u32
|
enum class SyncMode : u32
|
||||||
{
|
{
|
||||||
|
@ -53,10 +53,10 @@ private:
|
||||||
void RunDMA(Channel channel);
|
void RunDMA(Channel channel);
|
||||||
|
|
||||||
// from device -> memory
|
// from device -> memory
|
||||||
u32 DMARead(Channel channel);
|
u32 DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words);
|
||||||
|
|
||||||
// from memory -> device
|
// from memory -> device
|
||||||
void DMAWrite(Channel channel, u32 value);
|
void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words);
|
||||||
|
|
||||||
Bus* m_bus = nullptr;
|
Bus* m_bus = nullptr;
|
||||||
GPU* m_gpu = nullptr;
|
GPU* m_gpu = nullptr;
|
||||||
|
@ -88,7 +88,7 @@ private:
|
||||||
{
|
{
|
||||||
u32 bits;
|
u32 bits;
|
||||||
BitField<u32, bool, 0, 1> copy_to_device;
|
BitField<u32, bool, 0, 1> copy_to_device;
|
||||||
BitField<u32, bool, 1, 1> address_step_forward;
|
BitField<u32, bool, 1, 1> address_step_reverse;
|
||||||
BitField<u32, bool, 8, 1> chopping_enable;
|
BitField<u32, bool, 8, 1> chopping_enable;
|
||||||
BitField<u32, SyncMode, 9, 2> sync_mode;
|
BitField<u32, SyncMode, 9, 2> sync_mode;
|
||||||
BitField<u32, u32, 16, 3> chopping_dma_window_size;
|
BitField<u32, u32, 16, 3> chopping_dma_window_size;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "bus.h"
|
#include "bus.h"
|
||||||
|
#include "dma.h"
|
||||||
Log_SetChannel(GPU);
|
Log_SetChannel(GPU);
|
||||||
|
|
||||||
GPU::GPU() = default;
|
GPU::GPU() = default;
|
||||||
|
@ -22,34 +23,70 @@ void GPU::Reset()
|
||||||
void GPU::SoftReset()
|
void GPU::SoftReset()
|
||||||
{
|
{
|
||||||
m_GPUSTAT.bits = 0x14802000;
|
m_GPUSTAT.bits = 0x14802000;
|
||||||
|
UpdateDMARequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::UpdateDMARequest()
|
||||||
|
{
|
||||||
|
const bool request = m_GPUSTAT.dma_direction != DMADirection::Off;
|
||||||
|
m_GPUSTAT.dma_data_request = request;
|
||||||
|
m_dma->SetRequest(DMA::Channel::GPU, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GPU::ReadRegister(u32 offset)
|
u32 GPU::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
if (offset == 0x00)
|
switch (offset)
|
||||||
{
|
{
|
||||||
// GPUREAD
|
case 0x00:
|
||||||
Log_ErrorPrintf("GPUREAD");
|
return ReadGPUREAD();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (offset == 0x04)
|
|
||||||
{
|
|
||||||
// GPUSTAT
|
|
||||||
return m_GPUSTAT.bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
case 0x04:
|
||||||
|
return m_GPUSTAT.bits;
|
||||||
|
|
||||||
|
default:
|
||||||
Log_ErrorPrintf("Unhandled register read: %02X", offset);
|
Log_ErrorPrintf("Unhandled register read: %02X", offset);
|
||||||
return UINT32_C(0xFFFFFFFF);
|
return UINT32_C(0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GPU::WriteRegister(u32 offset, u32 value)
|
void GPU::WriteRegister(u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
if (offset == 0x00)
|
switch (offset)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
WriteGP0(value);
|
WriteGP0(value);
|
||||||
else if (offset == 0x04)
|
return;
|
||||||
|
|
||||||
|
case 0x04:
|
||||||
WriteGP1(value);
|
WriteGP1(value);
|
||||||
else
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value);
|
Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GPU::DMARead()
|
||||||
|
{
|
||||||
|
if (m_GPUSTAT.dma_direction != DMADirection::GPUREADtoCPU)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("Invalid DMA direction from GPU DMA read");
|
||||||
|
return UINT32_C(0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadGPUREAD();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPU::DMAWrite(u32 value)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("GPU DMA Write %08X", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GPU::ReadGPUREAD()
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("GPUREAD not implemented");
|
||||||
|
return UINT32_C(0xFFFFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::WriteGP0(u32 value)
|
void GPU::WriteGP0(u32 value)
|
||||||
|
@ -61,5 +98,19 @@ void GPU::WriteGP0(u32 value)
|
||||||
void GPU::WriteGP1(u32 value)
|
void GPU::WriteGP1(u32 value)
|
||||||
{
|
{
|
||||||
const u8 command = Truncate8(value >> 24);
|
const u8 command = Truncate8(value >> 24);
|
||||||
Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command);
|
const u32 param = value & UINT32_C(0x00FFFFFF);
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case 0x04: // DMA Direction
|
||||||
|
{
|
||||||
|
m_GPUSTAT.dma_direction = static_cast<DMADirection>(param);
|
||||||
|
Log_DebugPrintf("DMA direction <- 0x%02X", static_cast<u32>(m_GPUSTAT.dma_direction.GetValue()));
|
||||||
|
UpdateDMARequest();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,22 @@ public:
|
||||||
u32 ReadRegister(u32 offset);
|
u32 ReadRegister(u32 offset);
|
||||||
void WriteRegister(u32 offset, u32 value);
|
void WriteRegister(u32 offset, u32 value);
|
||||||
|
|
||||||
|
// DMA access
|
||||||
|
u32 DMARead();
|
||||||
|
void DMAWrite(u32 value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class DMADirection : u32
|
||||||
|
{
|
||||||
|
Off = 0,
|
||||||
|
FIFO = 1,
|
||||||
|
CPUtoGP0 = 2,
|
||||||
|
GPUREADtoCPU = 3
|
||||||
|
};
|
||||||
|
|
||||||
void SoftReset();
|
void SoftReset();
|
||||||
|
void UpdateDMARequest();
|
||||||
|
u32 ReadGPUREAD();
|
||||||
void WriteGP0(u32 value);
|
void WriteGP0(u32 value);
|
||||||
void WriteGP1(u32 value);
|
void WriteGP1(u32 value);
|
||||||
|
|
||||||
|
@ -52,7 +66,7 @@ private:
|
||||||
BitField<u32, bool, 26, 1> ready_to_recieve_cmd;
|
BitField<u32, bool, 26, 1> ready_to_recieve_cmd;
|
||||||
BitField<u32, bool, 27, 1> ready_to_send_vram;
|
BitField<u32, bool, 27, 1> ready_to_send_vram;
|
||||||
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
|
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
|
||||||
BitField<u32, u8, 29, 2> dma_direction;
|
BitField<u32, DMADirection, 29, 2> dma_direction;
|
||||||
BitField<u32, bool, 31, 1> drawing_even_line;
|
BitField<u32, bool, 31, 1> drawing_even_line;
|
||||||
} m_GPUSTAT = {};
|
} m_GPUSTAT = {};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue