mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-03-06 14:27:44 +00:00
CDROM: Implement get version and getstat commands
This commit is contained in:
parent
b951f27381
commit
e3c6035152
|
@ -88,7 +88,7 @@ struct BitField
|
||||||
|
|
||||||
BitField& operator|=(DataType rhs)
|
BitField& operator|=(DataType rhs)
|
||||||
{
|
{
|
||||||
SetValue(GetValue() & rhs);
|
SetValue(GetValue() | rhs);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
T* GetFrontPointer() { return m_ptr[m_head]; }
|
T* GetFrontPointer() { return m_ptr[m_head]; }
|
||||||
constexpr u32 GetCapacity() const { return CAPACITY; }
|
constexpr u32 GetCapacity() const { return CAPACITY; }
|
||||||
u32 GetSize() const { return m_size; }
|
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; }
|
bool IsFull() const { return m_size == CAPACITY; }
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "cdrom.h"
|
#include "cdrom.h"
|
||||||
#include "YBaseLib/Log.h"
|
#include "YBaseLib/Log.h"
|
||||||
#include "common/state_wrapper.h"
|
#include "common/state_wrapper.h"
|
||||||
|
#include "interrupt_controller.h"
|
||||||
Log_SetChannel(CDROM);
|
Log_SetChannel(CDROM);
|
||||||
|
|
||||||
CDROM::CDROM() = default;
|
CDROM::CDROM() = default;
|
||||||
|
@ -16,9 +17,17 @@ bool CDROM::Initialize(DMA* dma, InterruptController* interrupt_controller)
|
||||||
|
|
||||||
void CDROM::Reset()
|
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_param_fifo.Clear();
|
||||||
m_response_fifo.Clear();
|
m_response_fifo.Clear();
|
||||||
m_data_fifo.Clear();
|
m_data_fifo.Clear();
|
||||||
|
UpdateStatusRegister();
|
||||||
|
|
||||||
|
m_secondary_status.shell_open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDROM::DoState(StateWrapper& sw)
|
bool CDROM::DoState(StateWrapper& sw)
|
||||||
|
@ -39,10 +48,18 @@ u8 CDROM::ReadRegister(u32 offset)
|
||||||
return m_status.bits;
|
return m_status.bits;
|
||||||
|
|
||||||
case 1: // always response FIFO
|
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
|
case 2: // always data FIFO
|
||||||
return m_data_fifo.Pop();
|
{
|
||||||
|
const u8 value = m_data_fifo.Pop();
|
||||||
|
UpdateStatusRegister();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
|
@ -88,7 +105,7 @@ void CDROM::WriteRegister(u32 offset, u8 value)
|
||||||
if (m_state != State::Idle)
|
if (m_state != State::Idle)
|
||||||
Log_ErrorPrintf("Ignoring write (0x%02X) to command register in non-idle state", ZeroExtend32(value));
|
Log_ErrorPrintf("Ignoring write (0x%02X) to command register in non-idle state", ZeroExtend32(value));
|
||||||
else
|
else
|
||||||
WriteCommand(value);
|
ExecuteCommand(static_cast<Command>(value));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,6 +144,7 @@ void CDROM::WriteRegister(u32 offset, u8 value)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_param_fifo.Push(value);
|
m_param_fifo.Push(value);
|
||||||
|
UpdateStatusRegister();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,9 +208,74 @@ void CDROM::WriteRegister(u32 offset, u8 value)
|
||||||
ZeroExtend32(m_status.index.GetValue()), ZeroExtend32(value));
|
ZeroExtend32(m_status.index.GetValue()), ZeroExtend32(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDROM::SetInterrupt(Interrupt interrupt)
|
||||||
|
{
|
||||||
|
m_interrupt_flag_register = static_cast<u8>(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::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<u8>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,56 @@ private:
|
||||||
static constexpr u32 NUM_INTERRUPTS = 32;
|
static constexpr u32 NUM_INTERRUPTS = 32;
|
||||||
static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F;
|
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; }
|
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;
|
DMA* m_dma;
|
||||||
InterruptController* m_interrupt_controller;
|
InterruptController* m_interrupt_controller;
|
||||||
|
@ -56,6 +104,19 @@ private:
|
||||||
BitField<u8, bool, 7, 1> BUSYSTS;
|
BitField<u8, bool, 7, 1> BUSYSTS;
|
||||||
} m_status = {};
|
} m_status = {};
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
u8 bits;
|
||||||
|
BitField<u8, bool, 0, 1> error;
|
||||||
|
BitField<u8, bool, 1, 1> motor_on;
|
||||||
|
BitField<u8, bool, 2, 1> seek_error;
|
||||||
|
BitField<u8, bool, 3, 1> id_error;
|
||||||
|
BitField<u8, bool, 4, 1> shell_open;
|
||||||
|
BitField<u8, bool, 5, 1> reading;
|
||||||
|
BitField<u8, bool, 6, 1> seeking;
|
||||||
|
BitField<u8, bool, 7, 1> playing_cdda;
|
||||||
|
} m_secondary_status = {};
|
||||||
|
|
||||||
u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
|
u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
|
||||||
u8 m_interrupt_flag_register = 0;
|
u8 m_interrupt_flag_register = 0;
|
||||||
|
|
||||||
|
|
|
@ -606,6 +606,7 @@ void Core::ExecuteInstruction(Instruction inst)
|
||||||
|
|
||||||
case InstructionFunct::syscall:
|
case InstructionFunct::syscall:
|
||||||
{
|
{
|
||||||
|
Log_DebugPrintf("Syscall 0x%X(0x%X)", m_regs.s0, m_regs.a0);
|
||||||
RaiseException(Exception::Syscall);
|
RaiseException(Exception::Syscall);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -289,7 +289,7 @@ struct Cop0Registers
|
||||||
BitField<u32, u8, 0, 6> mode_bits;
|
BitField<u32, u8, 0, 6> mode_bits;
|
||||||
BitField<u32, u8, 28, 2> coprocessor_enable_mask;
|
BitField<u32, u8, 28, 2> 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;
|
} sr;
|
||||||
|
|
||||||
union CAUSE
|
union CAUSE
|
||||||
|
|
|
@ -292,6 +292,8 @@ void GPU::Execute(TickCount ticks)
|
||||||
// start the new frame
|
// start the new frame
|
||||||
m_system->IncrementFrameNumber();
|
m_system->IncrementFrameNumber();
|
||||||
m_crtc_state.current_scanline = 0;
|
m_crtc_state.current_scanline = 0;
|
||||||
|
m_crtc_state.in_hblank = false;
|
||||||
|
m_crtc_state.in_vblank = false;
|
||||||
|
|
||||||
if (m_GPUSTAT.vertical_resolution)
|
if (m_GPUSTAT.vertical_resolution)
|
||||||
m_GPUSTAT.drawing_even_line ^= true;
|
m_GPUSTAT.drawing_even_line ^= true;
|
||||||
|
|
|
@ -58,7 +58,7 @@ void InterruptController::WriteRegister(u32 offset, u32 value)
|
||||||
case 0x00: // I_STATUS
|
case 0x00: // I_STATUS
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("Clearing bits 0x%08X", value);
|
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();
|
UpdateCPUInterruptRequest();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -80,7 +80,7 @@ void InterruptController::UpdateCPUInterruptRequest()
|
||||||
{
|
{
|
||||||
// external interrupts set bit 10 only?
|
// external interrupts set bit 10 only?
|
||||||
if (m_interrupt_status_register != 0)
|
if (m_interrupt_status_register != 0)
|
||||||
m_cpu->SetExternalInterrupt(3);
|
m_cpu->SetExternalInterrupt(2);
|
||||||
else
|
else
|
||||||
m_cpu->ClearExternalInterrupt(3);
|
m_cpu->ClearExternalInterrupt(2);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue