diff --git a/src/pse/bus.cpp b/src/pse/bus.cpp index e07b1172d..7a9e1b6c0 100644 --- a/src/pse/bus.cpp +++ b/src/pse/bus.cpp @@ -3,6 +3,7 @@ #include "YBaseLib/Log.h" #include "YBaseLib/String.h" #include "dma.h" +#include "gpu.h" #include Log_SetChannel(Bus); @@ -38,7 +39,7 @@ bool Bus::ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_ return result; } -bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value) +bool Bus::ReadHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value) { u32 temp = 0; const bool result = @@ -47,7 +48,7 @@ bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_ return result; } -bool Bus::ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value) +bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value) { return DispatchAccess(cpu_address, bus_address, *value); } @@ -58,13 +59,13 @@ bool Bus::WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus return DispatchAccess(cpu_address, bus_address, temp); } -bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value) +bool Bus::WriteHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value) { u32 temp = ZeroExtend32(value); return DispatchAccess(cpu_address, bus_address, temp); } -bool Bus::WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value) +bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value) { return DispatchAccess(cpu_address, bus_address, value); } @@ -178,6 +179,20 @@ bool Bus::WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value) return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value); } +bool Bus::DoReadGPU(MemoryAccessSize size, u32 offset, u32& value) +{ + Assert(size == MemoryAccessSize::Word); + value = m_gpu->ReadRegister(offset); + return true; +} + +bool Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value) +{ + Assert(size == MemoryAccessSize::Word); + m_gpu->WriteRegister(offset, value); + return true; +} + bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value) { if (offset == 0x1AE) @@ -186,7 +201,7 @@ bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value) return true; } - //return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value); + // return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value); value = 0; return true; } @@ -201,7 +216,7 @@ bool Bus::WriteSPU(MemoryAccessSize size, u32 offset, u32 value) if (offset == 0x1AA) return true; - //return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value); + // return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value); return true; } @@ -212,7 +227,7 @@ bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value) return true; } -bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value) +bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value) { Assert(size == MemoryAccessSize::Word); m_dma->WriteRegister(offset, value); diff --git a/src/pse/bus.h b/src/pse/bus.h index ac3b8044a..084ea7a72 100644 --- a/src/pse/bus.h +++ b/src/pse/bus.h @@ -20,11 +20,11 @@ public: bool DoState(StateWrapper& sw); bool ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8* value); - bool ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value); - bool ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value); + bool ReadHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value); + bool ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value); bool WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8 value); - bool WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value); - bool WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value); + bool WriteHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value); + bool WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value); template bool DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32& value); @@ -33,6 +33,9 @@ private: static constexpr u32 DMA_BASE = 0x1F801080; static constexpr u32 DMA_SIZE = 0x80; static constexpr u32 DMA_MASK = DMA_SIZE - 1; + static constexpr u32 GPU_BASE = 0x1F801810; + static constexpr u32 GPU_SIZE = 0x10; + static constexpr u32 GPU_MASK = GPU_SIZE - 1; static constexpr u32 SPU_BASE = 0x1F801C00; static constexpr u32 SPU_SIZE = 0x300; static constexpr u32 SPU_MASK = 0x3FF; @@ -54,11 +57,14 @@ private: bool ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value); bool WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value); - bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value); - bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value); + bool DoReadGPU(MemoryAccessSize size, u32 offset, u32& value); + bool DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value); bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value); - bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value); + bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value); + + bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value); + bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value); DMA* m_dma = nullptr; GPU* m_gpu = nullptr; diff --git a/src/pse/bus.inl b/src/pse/bus.inl index a2bbaf0db..649290b0b 100644 --- a/src/pse/bus.inl +++ b/src/pse/bus.inl @@ -89,6 +89,15 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddres return (type == MemoryAccessType::Read) ? DoReadDMA(size, bus_address & DMA_MASK, value) : DoWriteDMA(size, bus_address & DMA_MASK, value); } + else if (bus_address < GPU_BASE) + { + return DoInvalidAccess(type, size, cpu_address, bus_address, value); + } + else if (bus_address < (GPU_BASE + GPU_SIZE)) + { + return (type == MemoryAccessType::Read) ? DoReadGPU(size, bus_address & GPU_MASK, value) : + DoWriteGPU(size, bus_address & GPU_MASK, value); + } else if (bus_address < SPU_BASE) { return DoInvalidAccess(type, size, cpu_address, bus_address, value); diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index 550ed116d..d9717cc68 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -526,6 +526,32 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) } break; + case InstructionOp::lwl: + case InstructionOp::lwr: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); + const u32 aligned_value = ReadMemoryWord(aligned_addr); + + // note: bypasses load delay on the read + const u32 existing_value = m_regs.r[static_cast(inst.i.rt.GetValue())]; + const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); + u32 new_value; + if (inst.op == InstructionOp::lwl) + { + const u32 mask = UINT32_C(0x00FFFFFF) >> shift; + new_value = (existing_value & mask) | (aligned_value << (24 - shift)); + } + else + { + const u32 mask = UINT32_C(0xFFFFFF00) << (24 - shift); + new_value = (existing_value & mask) | (aligned_value >> shift); + } + + WriteRegDelayed(inst.i.rt, new_value); + } + break; + case InstructionOp::sb: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); @@ -550,6 +576,31 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) } break; + case InstructionOp::swl: + case InstructionOp::swr: + { + const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); + const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); + const u32 mem_value = ReadMemoryWord(aligned_addr); + const u32 reg_value = ReadReg(inst.i.rt); + const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); + + u32 new_value; + if (inst.op == InstructionOp::swl) + { + const u32 mem_mask = UINT32_C(0xFFFFFF00) << shift; + new_value = (mem_value & mem_mask) | (reg_value >> (24 - shift)); + } + else + { + const u32 mem_mask = UINT32_C(0x00FFFFFF) >> (24 - shift); + new_value = (mem_value & mem_mask) | (reg_value << shift); + } + + WriteMemoryWord(aligned_addr, new_value); + } + break; + case InstructionOp::j: { Branch((m_regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2)); diff --git a/src/pse/cpu_disasm.cpp b/src/pse/cpu_disasm.cpp index 66a542a6c..ba545b4d1 100644 --- a/src/pse/cpu_disasm.cpp +++ b/src/pse/cpu_disasm.cpp @@ -61,19 +61,19 @@ static const std::array s_base_table = {{ "UNKNOWN", // 31 "lb $rt, $offsetrs", // 32 "lh $rt, $offsetrs", // 33 - "UNKNOWN", // 34 + "lwl $rt, $offsetrs", // 34 "lw $rt, $offsetrs", // 35 "lbu $rt, $offsetrs", // 36 "lhu $rt, $offsetrs", // 37 - "UNKNOWN", // 38 + "lwr $rt, $offsetrs", // 38 "UNKNOWN", // 39 "sb $rt, $offsetrs", // 40 "sh $rt, $offsetrs", // 41 - "UNKNOWN", // 42 + "swl $rt, $offsetrs", // 42 "sw $rt, $offsetrs", // 43 "UNKNOWN", // 44 "UNKNOWN", // 45 - "UNKNOWN", // 46 + "swr $rt, $offsetrs", // 46 "UNKNOWN", // 47 "UNKNOWN", // 48 "UNKNOWN", // 49 diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index e08dd138d..346a9b3b7 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -17,7 +17,7 @@ bool DMA::Initialize(Bus* bus, GPU* gpu) void DMA::Reset() { m_state = {}; - m_DPCR.bits = 0; + m_DPCR.bits = 0x07654321; m_DCIR = 0; } @@ -52,5 +52,160 @@ u32 DMA::ReadRegister(u32 offset) void DMA::WriteRegister(u32 offset, u32 value) { + const u32 channel_index = offset >> 4; + if (channel_index < 7) + { + ChannelState& state = m_state[channel_index]; + switch (offset & UINT32_C(0x0F)) + { + case 0x00: + { + state.base_address = value & BASE_ADDRESS_MASK; + Log_DebugPrintf("DMA channel %u base address <- 0x%08X", channel_index, state.base_address); + return; + } + case 0x04: + { + Log_DebugPrintf("DMA channel %u block control <- 0x%08X", channel_index, value); + state.block_control.bits = value; + return; + } + + case 0x08: + { + state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) | + (value & ChannelState::ChannelControl::WRITE_MASK); + Log_DebugPrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits); + if (CanRunChannel(static_cast(channel_index))) + RunDMA(static_cast(channel_index)); + + return; + } + + default: + break; + } + } + else + { + switch (offset) + { + case 0x70: + { + Log_DebugPrintf("DPCR <- 0x%08X", value); + m_DPCR.bits = value; + return; + } + + case 0x74: + { + m_DCIR = (m_DCIR & ~DCIR_WRITE_MASK) | (value & DCIR_WRITE_MASK); + Log_DebugPrintf("DCIR <- 0x%08X", m_DCIR); + return; + } + + default: + break; + } + } + Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value); } + +void DMA::SetRequest(Channel channel, bool request) +{ + ChannelState& cs = m_state[static_cast(channel)]; + cs.request = request; + + if (CanRunChannel(channel)) + RunDMA(channel); +} + +bool DMA::CanRunChannel(Channel channel) const +{ + if (!m_DPCR.GetMasterEnable(channel)) + return false; + + const ChannelState& cs = m_state[static_cast(channel)]; + if (cs.channel_control.start_trigger) + return true; + + return (cs.channel_control.enable_busy && cs.request); +} + +void DMA::RunDMA(Channel channel) +{ + ChannelState& cs = m_state[static_cast(channel)]; + const PhysicalMemoryAddress memory_address = cs.base_address; + const bool copy_to_device = cs.channel_control.copy_to_device; + Log_DebugPrintf("Running DMA for channel %u", static_cast(channel)); + Assert(Common::IsAlignedPow2(memory_address, 4)); + + // start/trigger bit is cleared on beginning of transfer + cs.channel_control.start_trigger = false; + + switch (cs.channel_control.sync_mode) + { + case SyncMode::Manual: + { + 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); + if (copy_to_device) + { + for (u32 i = 0; i < word_count; i++) + { + u32 memory_value = 0; + m_bus->DispatchAccess(memory_address, memory_address, + memory_value); + DMAWrite(channel, memory_value); + } + } + else + { + for (u32 i = 0; i < word_count; i++) + { + u32 memory_value = DMARead(channel); + m_bus->DispatchAccess(memory_address, memory_address, + memory_value); + } + } + } + break; + + case SyncMode::Request: + case SyncMode::LinkedList: + default: + Panic("Unimplemented sync mode"); + break; + } + + // start/busy bit is cleared on end of transfer + cs.channel_control.enable_busy = false; +} + +u32 DMA::DMARead(Channel channel) +{ + switch (channel) + { + case Channel::OTC: + { + // we just return zeros here.. guessing it's pulled low? + return 0; + } + + case Channel::MDECin: + case Channel::MDECout: + case Channel::GPU: + case Channel::CDROM: + case Channel::SPU: + case Channel::PIO: + default: + Panic("Unhandled DMA channel write"); + return UINT32_C(0xFFFFFFFF); + } +} + +void DMA::DMAWrite(Channel channel, u32 value) +{ + Panic("Unhandled DMA channel write"); +} diff --git a/src/pse/dma.h b/src/pse/dma.h index 31cd7556d..661a09466 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -34,18 +34,33 @@ public: u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); + void SetRequest(Channel channel, bool request); + private: - Bus* m_bus = nullptr; - GPU* m_gpu = nullptr; + static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); enum class SyncMode : u32 { - Word = 0, - Block = 1, + Manual = 0, + Request = 1, LinkedList = 2, Reserved = 3 }; + // is everything enabled for a channel to operate? + bool CanRunChannel(Channel channel) const; + + void RunDMA(Channel channel); + + // from device -> memory + u32 DMARead(Channel channel); + + // from memory -> device + void DMAWrite(Channel channel, u32 value); + + Bus* m_bus = nullptr; + GPU* m_gpu = nullptr; + struct ChannelState { u32 base_address; @@ -56,25 +71,35 @@ private: struct { BitField word_count; - } word_mode; + + u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; } + } manual; struct { BitField block_size; BitField block_count; - } block_mode; + + 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 direction_to_ram; + BitField copy_to_device; BitField address_step_forward; BitField chopping_enable; BitField sync_mode; BitField chopping_dma_window_size; BitField chopping_cpu_window_size; + BitField enable_busy; BitField start_trigger; + + static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011; } channel_control; + + bool request = false; }; std::array m_state = {}; @@ -83,8 +108,8 @@ private: { u32 bits; - u8 GetPriority(Channel channel) { return ((bits >> (static_cast(channel) * 4)) & u32(3)); } - bool GetMasterEnable(Channel channel) + u8 GetPriority(Channel channel) const { return ((bits >> (static_cast(channel) * 4)) & u32(3)); } + bool GetMasterEnable(Channel channel) const { return ConvertToBoolUnchecked((bits >> (static_cast(channel) * 4 + 3)) & u32(1)); } diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp new file mode 100644 index 000000000..7c7549b90 --- /dev/null +++ b/src/pse/gpu.cpp @@ -0,0 +1,65 @@ +#include "gpu.h" +#include "YBaseLib/Log.h" +#include "bus.h" +Log_SetChannel(GPU); + +GPU::GPU() = default; + +GPU::~GPU() = default; + +bool GPU::Initialize(Bus* bus, DMA* dma) +{ + m_bus = bus; + m_dma = dma; + return true; +} + +void GPU::Reset() +{ + SoftReset(); +} + +void GPU::SoftReset() +{ + m_GPUSTAT.bits = 0x14802000; +} + +u32 GPU::ReadRegister(u32 offset) +{ + if (offset == 0x00) + { + // GPUREAD + Log_ErrorPrintf("GPUREAD"); + return 0; + } + else if (offset == 0x04) + { + // GPUSTAT + return m_GPUSTAT.bits; + } + + Log_ErrorPrintf("Unhandled register read: %02X", offset); + return UINT32_C(0xFFFFFFFF); +} + +void GPU::WriteRegister(u32 offset, u32 value) +{ + if (offset == 0x00) + WriteGP0(value); + else if (offset == 0x04) + WriteGP1(value); + else + Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value); +} + +void GPU::WriteGP0(u32 value) +{ + const u8 command = Truncate8(value >> 24); + Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command); +} + +void GPU::WriteGP1(u32 value) +{ + const u8 command = Truncate8(value >> 24); + Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command); +} diff --git a/src/pse/gpu.h b/src/pse/gpu.h new file mode 100644 index 000000000..ff8d94ecb --- /dev/null +++ b/src/pse/gpu.h @@ -0,0 +1,58 @@ +#pragma once +#include "common/bitfield.h" +#include "types.h" +#include + +class Bus; +class DMA; + +class GPU +{ +public: + GPU(); + ~GPU(); + + bool Initialize(Bus* bus, DMA* dma); + void Reset(); + + u32 ReadRegister(u32 offset); + void WriteRegister(u32 offset, u32 value); + +private: + void SoftReset(); + void WriteGP0(u32 value); + void WriteGP1(u32 value); + + Bus* m_bus = nullptr; + DMA* m_dma = nullptr; + + union GPUSTAT + { + u32 bits; + BitField texture_page_x_base; + BitField texture_page_y_base; + BitField semi_transparency; + BitField texture_page_colors; + BitField dither_enable; + BitField draw_to_display_area; + BitField draw_set_mask_bit; + BitField draw_to_masked_pixels; + BitField interlaced_field; + BitField reverse_flag; + BitField texture_disable; + BitField horizontal_resolution_2; + BitField horizontal_resolution_1; + BitField vetical_resolution; + BitField pal_mode; + BitField display_area_color_depth_24; + BitField vertical_interlace; + BitField display_enable; + BitField interrupt_request; + BitField dma_data_request; + BitField ready_to_recieve_cmd; + BitField ready_to_send_vram; + BitField ready_to_recieve_dma; + BitField dma_direction; + BitField drawing_even_line; + } m_GPUSTAT = {}; +}; diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 37a474a77..ac31a0ee2 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -39,6 +39,7 @@ + @@ -47,6 +48,7 @@ + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index d3c82f4a3..71fa7c85d 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -6,6 +6,7 @@ + @@ -16,6 +17,7 @@ + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 7ae996095..3a7842190 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -9,10 +9,13 @@ bool System::Initialize() if (!m_cpu.Initialize(&m_bus)) return false; - if (!m_bus.Initialize(this, &m_dma, nullptr)) + if (!m_bus.Initialize(this, &m_dma, &m_gpu)) return false; - if (!m_dma.Initialize(&m_bus, nullptr)) + if (!m_dma.Initialize(&m_bus, &m_gpu)) + return false; + + if (!m_gpu.Initialize(&m_bus, &m_dma)) return false; return true; @@ -22,6 +25,8 @@ void System::Reset() { m_cpu.Reset(); m_bus.Reset(); + m_dma.Reset(); + m_gpu.Reset(); } void System::RunFrame() diff --git a/src/pse/system.h b/src/pse/system.h index bc94af776..c81e23e08 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -1,6 +1,7 @@ #pragma once #include "bus.h" #include "dma.h" +#include "gpu.h" #include "cpu_core.h" #include "types.h" @@ -19,4 +20,5 @@ private: CPU::Core m_cpu; Bus m_bus; DMA m_dma; + GPU m_gpu; };