diff --git a/src/pse/bus.cpp b/src/pse/bus.cpp index f8d3947d5..f2523476b 100644 --- a/src/pse/bus.cpp +++ b/src/pse/bus.cpp @@ -9,6 +9,7 @@ #include "dma.h" #include "gpu.h" #include "interrupt_controller.h" +#include "mdec.h" #include "pad.h" #include "spu.h" #include "timers.h" @@ -28,7 +29,7 @@ Bus::Bus() = default; Bus::~Bus() = default; bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, - Pad* pad, Timers* timers, SPU* spu) + Pad* pad, Timers* timers, SPU* spu, MDEC* mdec) { if (!LoadBIOS()) return false; @@ -41,6 +42,7 @@ bool Bus::Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_co m_pad = pad; m_timers = timers; m_spu = spu; + m_mdec = mdec; return true; } @@ -80,8 +82,7 @@ bool Bus::ReadByte(PhysicalMemoryAddress address, u8* value) bool Bus::ReadHalfWord(PhysicalMemoryAddress address, u16* value) { u32 temp = 0; - const bool result = - DispatchAccess(address, temp); + const bool result = DispatchAccess(address, temp); *value = Truncate16(temp); return result; } @@ -375,6 +376,20 @@ bool Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value) return true; } +bool Bus::DoReadMDEC(MemoryAccessSize size, u32 offset, u32& value) +{ + Assert(size == MemoryAccessSize::Word); + value = m_mdec->ReadRegister(offset); + return true; +} + +bool Bus::DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value) +{ + Assert(size == MemoryAccessSize::Word); + m_mdec->WriteRegister(offset, value); + return true; +} + bool Bus::DoReadInterruptController(MemoryAccessSize size, u32 offset, u32& value) { FixupUnalignedWordAccessW32(offset, value); diff --git a/src/pse/bus.h b/src/pse/bus.h index 1a9c3788f..328288840 100644 --- a/src/pse/bus.h +++ b/src/pse/bus.h @@ -17,6 +17,7 @@ class CDROM; class Pad; class Timers; class SPU; +class MDEC; class System; class Bus @@ -26,7 +27,7 @@ public: ~Bus(); bool Initialize(CPU::Core* cpu, DMA* dma, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, Pad* pad, - Timers* timers, SPU* spu); + Timers* timers, SPU* spu, MDEC* mdec); void Reset(); bool DoState(StateWrapper& sw); @@ -71,11 +72,14 @@ private: TIMERS_SIZE = 0x40, TIMERS_MASK = TIMERS_SIZE - 1, CDROM_BASE = 0x1F801800, - CDROM_SIZE = 0x04, + CDROM_SIZE = 0x10, CDROM_MASK = CDROM_SIZE - 1, GPU_BASE = 0x1F801810, GPU_SIZE = 0x10, GPU_MASK = GPU_SIZE - 1, + MDEC_BASE = 0x1F801820, + MDEC_SIZE = 0x10, + MDEC_MASK = MDEC_SIZE - 1, SPU_BASE = 0x1F801C00, SPU_SIZE = 0x300, SPU_MASK = 0x3FF, @@ -143,6 +147,9 @@ private: bool DoReadGPU(MemoryAccessSize size, u32 offset, u32& value); bool DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value); + bool DoReadMDEC(MemoryAccessSize size, u32 offset, u32& value); + bool DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value); + bool DoReadInterruptController(MemoryAccessSize size, u32 offset, u32& value); bool DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value); @@ -163,6 +170,7 @@ private: Pad* m_pad = nullptr; Timers* m_timers = nullptr; SPU* m_spu = nullptr; + MDEC* m_mdec = nullptr; std::array m_ram{}; // 2MB RAM std::array m_bios{}; // 512K BIOS ROM diff --git a/src/pse/bus.inl b/src/pse/bus.inl index f9b41767f..ede34c44a 100644 --- a/src/pse/bus.inl +++ b/src/pse/bus.inl @@ -138,15 +138,16 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value) return (type == MemoryAccessType::Read) ? DoReadCDROM(size, address & CDROM_MASK, value) : DoWriteCDROM(size, address & CDROM_MASK, value); } - else if (address < GPU_BASE) - { - return DoInvalidAccess(type, size, address, value); - } else if (address < (GPU_BASE + GPU_SIZE)) { return (type == MemoryAccessType::Read) ? DoReadGPU(size, address & GPU_MASK, value) : DoWriteGPU(size, address & GPU_MASK, value); } + else if (address < (MDEC_BASE + MDEC_SIZE)) + { + return (type == MemoryAccessType::Read) ? DoReadMDEC(size, address & MDEC_MASK, value) : + DoWriteMDEC(size, address & MDEC_MASK, value); + } else if (address < SPU_BASE) { return DoInvalidAccess(type, size, address, value); diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index 5005a12c3..d6c81c66d 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -5,6 +5,7 @@ #include "common/state_wrapper.h" #include "gpu.h" #include "interrupt_controller.h" +#include "mdec.h" #include "spu.h" #include "system.h" Log_SetChannel(DMA); @@ -14,7 +15,7 @@ DMA::DMA() = default; DMA::~DMA() = default; bool DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, - SPU* spu) + SPU* spu, MDEC* mdec) { m_system = system; m_bus = bus; @@ -22,6 +23,7 @@ bool DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_co m_gpu = gpu; m_cdrom = cdrom; m_spu = spu; + m_mdec = mdec; return true; } @@ -371,8 +373,10 @@ u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaini case Channel::SPU: return m_spu->DMARead(); - case Channel::MDECin: case Channel::MDECout: + return m_mdec->DMARead(); + + case Channel::MDECin: case Channel::PIO: default: Panic("Unhandled DMA channel read"); @@ -393,6 +397,9 @@ void DMA::DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address break; case Channel::MDECin: + m_mdec->DMAWrite(value); + break; + case Channel::MDECout: case Channel::CDROM: case Channel::PIO: diff --git a/src/pse/dma.h b/src/pse/dma.h index 69ba6c08a..1ac75ed4d 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -11,6 +11,7 @@ class InterruptController; class GPU; class CDROM; class SPU; +class MDEC; class DMA { @@ -35,7 +36,7 @@ public: ~DMA(); bool Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, - SPU* spu); + SPU* spu, MDEC* mdec); void Reset(); bool DoState(StateWrapper& sw); @@ -78,6 +79,7 @@ private: GPU* m_gpu = nullptr; CDROM* m_cdrom = nullptr; SPU* m_spu = nullptr; + MDEC* m_mdec = nullptr; TickCount m_transfer_ticks = 0; bool m_transfer_pending = false; diff --git a/src/pse/mdec.cpp b/src/pse/mdec.cpp new file mode 100644 index 000000000..6fd252ba0 --- /dev/null +++ b/src/pse/mdec.cpp @@ -0,0 +1,139 @@ +#include "mdec.h" +#include "YBaseLib/Log.h" +#include "common/state_wrapper.h" +#include "dma.h" +#include "interrupt_controller.h" +#include "system.h" +Log_SetChannel(MDEC); + +MDEC::MDEC() = default; + +MDEC::~MDEC() = default; + +bool MDEC::Initialize(System* system, DMA* dma) +{ + m_system = system; + m_dma = dma; + return true; +} + +void MDEC::Reset() +{ + SoftReset(); +} + +bool MDEC::DoState(StateWrapper& sw) +{ + sw.Do(&m_status_register.bits); + sw.Do(&m_data_in_fifo); + sw.Do(&m_data_out_fifo); + + return !sw.HasError(); +} + +u32 MDEC::ReadRegister(u32 offset) +{ + switch (offset) + { + case 0: + return ReadDataRegister(); + + case 4: + { + Log_DebugPrintf("MDEC status register -> 0x%08X", m_status_register.bits); + return m_status_register.bits; + } + + default: + { + Log_ErrorPrintf("Unknown MDEC register read: 0x%08X", offset); + return UINT32_C(0xFFFFFFFF); + } + } +} + +void MDEC::WriteRegister(u32 offset, u32 value) +{ + switch (offset) + { + case 0: + { + WriteCommandRegister(value); + return; + } + + case 4: + { + Log_DebugPrintf("MDEC control register <- 0x%08X", value); + + const ControlRegister cr{value}; + if (cr.reset) + SoftReset(); + + m_status_register.data_in_request = cr.enable_dma_in; + m_status_register.data_out_request = cr.enable_dma_out; + m_dma->SetRequest(DMA::Channel::MDECin, cr.enable_dma_in); + m_dma->SetRequest(DMA::Channel::MDECout, cr.enable_dma_out); + + return; + } + + default: + { + Log_ErrorPrintf("Unknown MDEC register write: 0x%08X <- 0x%08X", offset, value); + return; + } + } +} + +u32 MDEC::DMARead() +{ + return ReadDataRegister(); +} + +void MDEC::DMAWrite(u32 value) +{ + WriteCommandRegister(value); +} + +void MDEC::SoftReset() +{ + m_status_register = {}; + m_data_in_fifo.Clear(); + m_data_out_fifo.Clear(); + + UpdateStatusRegister(); +} + +void MDEC::UpdateStatusRegister() +{ + m_status_register.data_out_fifo_empty = m_data_out_fifo.IsEmpty(); + m_status_register.data_in_fifo_full = m_data_in_fifo.IsFull(); +} + +void MDEC::WriteCommandRegister(u32 value) +{ + Log_DebugPrintf("MDEC command/data register <- 0x%08X", value); + + m_data_in_fifo.Push(value); + HandleCommand(); + UpdateStatusRegister(); +} + +u32 MDEC::ReadDataRegister() +{ + if (m_data_out_fifo.IsEmpty()) + { + Log_WarningPrintf("MDEC data out FIFO empty on read"); + return UINT32_C(0xFFFFFFFF); + } + + const u32 value = m_data_out_fifo.Pop(); + UpdateStatusRegister(); + return value; +} + +void MDEC::HandleCommand() +{ + Log_DebugPrintf("MDEC command: 0x%08X", m_data_in_fifo.Peek(0)); +} diff --git a/src/pse/mdec.h b/src/pse/mdec.h new file mode 100644 index 000000000..eac24c42e --- /dev/null +++ b/src/pse/mdec.h @@ -0,0 +1,81 @@ +#pragma once +#include "common/bitfield.h" +#include "common/fifo_queue.h" +#include "types.h" +#include +#include + +class StateWrapper; + +class System; +class DMA; + +class MDEC +{ +public: + MDEC(); + ~MDEC(); + + bool Initialize(System* system, DMA* dma); + void Reset(); + bool DoState(StateWrapper& sw); + + // I/O + u32 ReadRegister(u32 offset); + void WriteRegister(u32 offset, u32 value); + + u32 DMARead(); + void DMAWrite(u32 value); + +private: + static constexpr u32 DATA_IN_FIFO_SIZE = 256; + static constexpr u32 DATA_OUT_FIFO_SIZE = 256; + + enum DataOutputDepth : u8 + { + DataOutputDepth_4Bit = 0, + DataOutputDepth_8Bit = 1, + DataOutputDepth_24Bit = 2, + DataOutputDepth_15Bit = 3 + }; + + union StatusRegister + { + u32 bits; + + BitField data_out_fifo_empty; + BitField data_in_fifo_full; + BitField command_busy; + BitField data_in_request; + BitField data_out_request; + BitField data_output_depth; + BitField data_output_signed; + BitField data_output_bit15; + BitField current_block; + BitField parameter_words_remaining; + }; + + union ControlRegister + { + u32 bits; + BitField reset; + BitField enable_dma_in; + BitField enable_dma_out; + }; + + void SoftReset(); + void UpdateStatusRegister(); + + void WriteCommandRegister(u32 value); + u32 ReadDataRegister(); + + void HandleCommand(); + + System* m_system = nullptr; + DMA* m_dma = nullptr; + + StatusRegister m_status_register = {}; + + InlineFIFOQueue m_data_in_fifo; + InlineFIFOQueue m_data_out_fifo; +}; diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 49142f50d..444accb8e 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -47,6 +47,7 @@ + @@ -68,6 +69,7 @@ + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 58b3f16b3..0f99affda 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -18,6 +18,7 @@ + @@ -41,6 +42,7 @@ + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index c5fc11412..0b9eee98f 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -7,6 +7,7 @@ #include "dma.h" #include "gpu.h" #include "interrupt_controller.h" +#include "mdec.h" #include "pad.h" #include "pad_device.h" #include "spu.h" @@ -26,6 +27,7 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface) m_pad = std::make_unique(); m_timers = std::make_unique(); m_spu = std::make_unique(); + m_mdec = std::make_unique(); } System::~System() = default; @@ -36,12 +38,13 @@ bool System::Initialize() return false; if (!m_bus->Initialize(m_cpu.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), - m_pad.get(), m_timers.get(), m_spu.get())) + m_pad.get(), m_timers.get(), m_spu.get(), m_mdec.get())) { return false; } - if (!m_dma->Initialize(this, m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_spu.get())) + if (!m_dma->Initialize(this, m_bus.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_spu.get(), + m_mdec.get())) { return false; } @@ -64,6 +67,9 @@ bool System::Initialize() if (!m_spu->Initialize(this, m_dma.get(), m_interrupt_controller.get())) return false; + if (!m_mdec->Initialize(this, m_dma.get())) + return false; + return true; } @@ -96,6 +102,9 @@ bool System::DoState(StateWrapper& sw) if (!sw.DoMarker("SPU") || !m_timers->DoState(sw)) return false; + if (!sw.DoMarker("MDEC") || !m_mdec->DoState(sw)) + return false; + return !sw.HasError(); } @@ -110,6 +119,7 @@ void System::Reset() m_pad->Reset(); m_timers->Reset(); m_spu->Reset(); + m_mdec->Reset(); m_frame_number = 1; } diff --git a/src/pse/system.h b/src/pse/system.h index cab27ac4b..ab065b3d9 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -21,6 +21,7 @@ class Pad; class PadDevice; class Timers; class SPU; +class MDEC; class System { @@ -69,6 +70,7 @@ private: std::unique_ptr m_pad; std::unique_ptr m_timers; std::unique_ptr m_spu; + std::unique_ptr m_mdec; u32 m_frame_number = 1; u32 m_internal_frame_number = 1; };