CDROM: Implement get version and getstat commands

This commit is contained in:
Connor McLaughlin 2019-09-17 22:18:58 +10:00
parent b951f27381
commit e3c6035152
8 changed files with 159 additions and 12 deletions

View file

@ -88,7 +88,7 @@ struct BitField
BitField& operator|=(DataType rhs)
{
SetValue(GetValue() & rhs);
SetValue(GetValue() | rhs);
return *this;
}

View file

@ -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()

View file

@ -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<Command>(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<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::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;
}
}
}

View file

@ -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<u8, bool, 7, 1> BUSYSTS;
} 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_flag_register = 0;

View file

@ -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;

View file

@ -289,7 +289,7 @@ struct Cop0Registers
BitField<u32, u8, 0, 6> mode_bits;
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;
union CAUSE

View file

@ -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;

View file

@ -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);
}