From e3c6035152d16e696ad45e5f1214c98033e5052c Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 17 Sep 2019 22:18:58 +1000 Subject: [PATCH] CDROM: Implement get version and getstat commands --- src/common/bitfield.h | 2 +- src/common/fifo_queue.h | 2 +- src/pse/cdrom.cpp | 93 ++++++++++++++++++++++++++++++-- src/pse/cdrom.h | 63 +++++++++++++++++++++- src/pse/cpu_core.cpp | 1 + src/pse/cpu_types.h | 2 +- src/pse/gpu.cpp | 2 + src/pse/interrupt_controller.cpp | 6 +-- 8 files changed, 159 insertions(+), 12 deletions(-) diff --git a/src/common/bitfield.h b/src/common/bitfield.h index b331d38f2..44f95a264 100644 --- a/src/common/bitfield.h +++ b/src/common/bitfield.h @@ -88,7 +88,7 @@ struct BitField BitField& operator|=(DataType rhs) { - SetValue(GetValue() & rhs); + SetValue(GetValue() | rhs); return *this; } diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h index 77fd64924..b2eaecaa7 100644 --- a/src/common/fifo_queue.h +++ b/src/common/fifo_queue.h @@ -20,7 +20,7 @@ public: T* GetFrontPointer() { return m_ptr[m_head]; } constexpr u32 GetCapacity() const { return CAPACITY; } u32 GetSize() const { return m_size; } - bool IsEmpty() const { return m_size > 0; } + bool IsEmpty() const { return m_size == 0; } bool IsFull() const { return m_size == CAPACITY; } void Clear() diff --git a/src/pse/cdrom.cpp b/src/pse/cdrom.cpp index 18920f423..9eda855e2 100644 --- a/src/pse/cdrom.cpp +++ b/src/pse/cdrom.cpp @@ -1,6 +1,7 @@ #include "cdrom.h" #include "YBaseLib/Log.h" #include "common/state_wrapper.h" +#include "interrupt_controller.h" Log_SetChannel(CDROM); CDROM::CDROM() = default; @@ -16,9 +17,17 @@ bool CDROM::Initialize(DMA* dma, InterruptController* interrupt_controller) void CDROM::Reset() { + m_state = State::Idle; + m_status.bits = 0; + m_secondary_status.bits = 0; + m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; + m_interrupt_flag_register = 0; m_param_fifo.Clear(); m_response_fifo.Clear(); m_data_fifo.Clear(); + UpdateStatusRegister(); + + m_secondary_status.shell_open = true; } bool CDROM::DoState(StateWrapper& sw) @@ -39,10 +48,18 @@ u8 CDROM::ReadRegister(u32 offset) return m_status.bits; case 1: // always response FIFO - return m_response_fifo.Pop(); + { + const u8 value = m_response_fifo.Pop(); + UpdateStatusRegister(); + return value; + } case 2: // always data FIFO - return m_data_fifo.Pop(); + { + const u8 value = m_data_fifo.Pop(); + UpdateStatusRegister(); + return value; + } case 3: { @@ -88,7 +105,7 @@ void CDROM::WriteRegister(u32 offset, u8 value) if (m_state != State::Idle) Log_ErrorPrintf("Ignoring write (0x%02X) to command register in non-idle state", ZeroExtend32(value)); else - WriteCommand(value); + ExecuteCommand(static_cast(value)); return; } @@ -127,6 +144,7 @@ void CDROM::WriteRegister(u32 offset, u8 value) } m_param_fifo.Push(value); + UpdateStatusRegister(); return; } @@ -190,9 +208,74 @@ void CDROM::WriteRegister(u32 offset, u8 value) ZeroExtend32(m_status.index.GetValue()), ZeroExtend32(value)); } +void CDROM::SetInterrupt(Interrupt interrupt) +{ + m_interrupt_flag_register = static_cast(interrupt); + if (HasPendingInterrupt()) + m_interrupt_controller->InterruptRequest(InterruptController::IRQ::CDROM); +} + +void CDROM::UpdateStatusRegister() +{ + m_status.ADPBUSY = false; + m_status.PRMEMPTY = m_param_fifo.IsEmpty(); + m_status.PRMWRDY = m_param_fifo.IsFull(); + m_status.RSLRRDY = !m_response_fifo.IsEmpty(); + m_status.DRQSTS = !m_data_fifo.IsEmpty(); + m_status.BUSYSTS = m_state != State::Idle; +} + void CDROM::Execute() {} -void CDROM::WriteCommand(u8 command) +void CDROM::ExecuteCommand(Command command) { - Log_ErrorPrintf("CDROM write command 0x%02X", ZeroExtend32(command)); + Log_ErrorPrintf("CDROM write command 0x%02X", ZeroExtend32(static_cast(command))); + + switch (command) + { + case Command::Getstat: + { + Log_DebugPrintf("CDROM Getstat command"); + + // if bit 0 or 2 is set, send an additional byte + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + UpdateStatusRegister(); + } + break; + + case Command::Test: + { + const u8 subcommand = m_param_fifo.Pop(); + ExecuteTestCommand(subcommand); + } + break; + + default: + Panic("Unknown command"); + break; + } +} + +void CDROM::ExecuteTestCommand(u8 subcommand) +{ + switch (subcommand) + { + case 0x20: // Get CDROM BIOS Date/Version + { + Log_DebugPrintf("Get CDROM BIOS Date/Version"); + static constexpr u8 response[] = {0x94, 0x09, 0x19, 0xC0}; + m_response_fifo.PushRange(response, countof(response)); + m_param_fifo.Clear(); + SetInterrupt(Interrupt::INT3); + UpdateStatusRegister(); + return; + } + + default: + { + Log_ErrorPrintf("Unknown test command 0x%02X", subcommand); + return; + } + } } diff --git a/src/pse/cdrom.h b/src/pse/cdrom.h index adc30d1b6..af72d2c62 100644 --- a/src/pse/cdrom.h +++ b/src/pse/cdrom.h @@ -31,8 +31,56 @@ private: static constexpr u32 NUM_INTERRUPTS = 32; static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F; + enum class Interrupt : u8 + { + INT1 = 0x01, + INT2 = 0x02, + INT3 = 0x03, + INT4 = 0x04, + INT5 = 0x05 + }; + + enum class Command : u8 + { + Sync = 0x00, + Getstat = 0x01, + Setloc = 0x02, + Play = 0x03, + Forward = 0x04, + Backward = 0x05, + ReadN = 0x06, + MotorOn = 0x07, + Stop = 0x08, + Pause = 0x09, + Init = 0x0A, + Mute = 0x0B, + Demute = 0x0C, + Setfilter = 0x0D, + Setmode = 0x0E, + Getparam = 0x0F, + GetlocL = 0x10, + GetlocP = 0x11, + SetSession = 0x12, + GetTN = 0x13, + GetTD = 0x14, + SeekL = 0x15, + SeekP = 0x16, + SetClock = 0x17, + GetClock = 0x18, + Test = 0x19, + GetID = 0x1A, + ReadS = 0x1B, + Reset = 0x1C, + GetQ = 0x1D, + ReadTOC = 0x1E, + VideoCD = 0x1F, + }; + bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; } - void WriteCommand(u8 command); + void SetInterrupt(Interrupt interrupt); + void UpdateStatusRegister(); + void ExecuteCommand(Command command); + void ExecuteTestCommand(u8 subcommand); DMA* m_dma; InterruptController* m_interrupt_controller; @@ -56,6 +104,19 @@ private: BitField BUSYSTS; } m_status = {}; + union + { + u8 bits; + BitField error; + BitField motor_on; + BitField seek_error; + BitField id_error; + BitField shell_open; + BitField reading; + BitField seeking; + BitField playing_cdda; + } m_secondary_status = {}; + u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; u8 m_interrupt_flag_register = 0; diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index e685ecefd..bc049e6f3 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -606,6 +606,7 @@ void Core::ExecuteInstruction(Instruction inst) case InstructionFunct::syscall: { + Log_DebugPrintf("Syscall 0x%X(0x%X)", m_regs.s0, m_regs.a0); RaiseException(Exception::Syscall); } break; diff --git a/src/pse/cpu_types.h b/src/pse/cpu_types.h index ec5a6e2bf..1eaca0bf0 100644 --- a/src/pse/cpu_types.h +++ b/src/pse/cpu_types.h @@ -289,7 +289,7 @@ struct Cop0Registers BitField mode_bits; BitField coprocessor_enable_mask; - static constexpr u32 WRITE_MASK = 0b1111'0010'0111'1111'1111'0011'0011'1111; + static constexpr u32 WRITE_MASK = 0b1111'0010'0111'1111'1111'1111'0011'1111; } sr; union CAUSE diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 8181dad7a..688ba2586 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -292,6 +292,8 @@ void GPU::Execute(TickCount ticks) // start the new frame m_system->IncrementFrameNumber(); m_crtc_state.current_scanline = 0; + m_crtc_state.in_hblank = false; + m_crtc_state.in_vblank = false; if (m_GPUSTAT.vertical_resolution) m_GPUSTAT.drawing_even_line ^= true; diff --git a/src/pse/interrupt_controller.cpp b/src/pse/interrupt_controller.cpp index e40b7443b..070f7ccc4 100644 --- a/src/pse/interrupt_controller.cpp +++ b/src/pse/interrupt_controller.cpp @@ -58,7 +58,7 @@ void InterruptController::WriteRegister(u32 offset, u32 value) case 0x00: // I_STATUS { Log_DebugPrintf("Clearing bits 0x%08X", value); - m_interrupt_status_register = m_interrupt_status_register & (~(value & REGISTER_WRITE_MASK)); + m_interrupt_status_register = m_interrupt_status_register & (value & REGISTER_WRITE_MASK); UpdateCPUInterruptRequest(); } break; @@ -80,7 +80,7 @@ void InterruptController::UpdateCPUInterruptRequest() { // external interrupts set bit 10 only? if (m_interrupt_status_register != 0) - m_cpu->SetExternalInterrupt(3); + m_cpu->SetExternalInterrupt(2); else - m_cpu->ClearExternalInterrupt(3); + m_cpu->ClearExternalInterrupt(2); }