diff --git a/src/common/cd_image.h b/src/common/cd_image.h index a0ef098a1..3bcfe1ae1 100644 --- a/src/common/cd_image.h +++ b/src/common/cd_image.h @@ -9,6 +9,16 @@ public: CDImage(); ~CDImage(); + enum : u32 + { + RAW_SECTOR_SIZE = 2352, + DATA_SECTOR_SIZE = 2048, + SECTOR_SYNC_SIZE = 12, + FRAMES_PER_SECOND = 75, // "sectors" + SECONDS_PER_MINUTE = 60, + FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE, + }; + enum class ReadMode : u32 { DataOnly, // 2048 bytes per sector. @@ -40,16 +50,6 @@ public: u32 Read(ReadMode read_mode, u32 sector_count, void* buffer); private: - enum : u32 - { - RAW_SECTOR_SIZE = 2352, - DATA_SECTOR_SIZE = 2048, - SECTOR_SYNC_SIZE = 12, - FRAMES_PER_SECOND = 75, // "sectors" - SECONDS_PER_MINUTE = 60, - FRAMES_PER_MINUTE = FRAMES_PER_SECOND * SECONDS_PER_MINUTE, - }; - // TODO: Multiple data files from cue sheet ByteStream* m_data_file = nullptr; diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h index b2eaecaa7..3436e2d48 100644 --- a/src/common/fifo_queue.h +++ b/src/common/fifo_queue.h @@ -16,8 +16,8 @@ class FIFOQueue public: const T* GetDataPointer() const { return m_ptr; } T* GetDataPointer() { return m_ptr; } - const T* GetFrontPointer() const { return m_ptr[m_head]; } - T* GetFrontPointer() { return m_ptr[m_head]; } + const T* GetFrontPointer() const { return &m_ptr[m_head]; } + 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; } @@ -91,6 +91,17 @@ public: const T& Peek() const { return m_ptr[m_head]; } const T& Peek(u32 offset) { return m_ptr[(m_head + offset) % CAPACITY]; } + void Remove(u32 count) + { + Assert(m_size >= count); + for (u32 i = 0; i < count; i++) + { + m_ptr[m_head].~T(); + m_head = (m_head + 1) % CAPACITY; + m_size--; + } + } + void RemoveOne() { Assert(m_size > 0); diff --git a/src/pse-sdl/main.cpp b/src/pse-sdl/main.cpp index 31c54f51d..758ec22e6 100644 --- a/src/pse-sdl/main.cpp +++ b/src/pse-sdl/main.cpp @@ -80,7 +80,7 @@ int main(int argc, char* argv[]) { // set log flags // g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); - g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL DMA", LOGLEVEL_DEBUG); + g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController", LOGLEVEL_DEBUG); #ifdef Y_BUILD_CONFIG_RELEASE g_pLog->SetFilterLevel(LOGLEVEL_INFO); diff --git a/src/pse/cdrom.cpp b/src/pse/cdrom.cpp index c45f733a2..01a260ea1 100644 --- a/src/pse/cdrom.cpp +++ b/src/pse/cdrom.cpp @@ -3,14 +3,16 @@ #include "common/cd_image.h" #include "common/state_wrapper.h" #include "interrupt_controller.h" +#include "system.h" Log_SetChannel(CDROM); CDROM::CDROM() = default; CDROM::~CDROM() = default; -bool CDROM::Initialize(DMA* dma, InterruptController* interrupt_controller) +bool CDROM::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller) { + m_system = system; m_dma = dma; m_interrupt_controller = interrupt_controller; return true; @@ -18,7 +20,7 @@ bool CDROM::Initialize(DMA* dma, InterruptController* interrupt_controller) void CDROM::Reset() { - m_state = State::Idle; + m_command_state = CommandState::Idle; m_status.bits = 0; m_secondary_status.bits = 0; m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; @@ -27,13 +29,11 @@ void CDROM::Reset() m_response_fifo.Clear(); m_data_fifo.Clear(); UpdateStatusRegister(); - - m_secondary_status.shell_open = true; } bool CDROM::DoState(StateWrapper& sw) { - sw.Do(&m_state); + sw.Do(&m_command_state); sw.Do(&m_status.bits); sw.Do(&m_secondary_status.bits); sw.Do(&m_interrupt_enable_register); @@ -57,7 +57,7 @@ bool CDROM::InsertMedia(const char* filename) RemoveMedia(); m_media = std::move(media); - m_secondary_status.shell_open = false; + // m_secondary_status.shell_open = false; return true; } @@ -69,7 +69,7 @@ void CDROM::RemoveMedia() // TODO: Error while reading? Log_InfoPrintf("Removing CD..."); m_media.reset(); - m_secondary_status.shell_open = true; + // m_secondary_status.shell_open = true; } u8 CDROM::ReadRegister(u32 offset) @@ -77,12 +77,14 @@ u8 CDROM::ReadRegister(u32 offset) switch (offset) { case 0: // status register + Log_DebugPrintf("CDROM read status register <- 0x%08X", m_status.bits); return m_status.bits; case 1: // always response FIFO { const u8 value = m_response_fifo.Pop(); UpdateStatusRegister(); + Log_DebugPrintf("CDROM read response FIFO <- 0x%08X", ZeroExtend32(value)); return value; } @@ -90,6 +92,7 @@ u8 CDROM::ReadRegister(u32 offset) { const u8 value = m_data_fifo.Pop(); UpdateStatusRegister(); + Log_DebugPrintf("CDROM read data FIFO <- 0x%08X", ZeroExtend32(value)); return value; } @@ -99,10 +102,13 @@ u8 CDROM::ReadRegister(u32 offset) { case 0: case 2: + Log_DebugPrintf("CDROM read interrupt enable register <- 0x%02X", + ZeroExtend32(m_interrupt_enable_register | ~INTERRUPT_REGISTER_MASK)); return m_interrupt_enable_register | ~INTERRUPT_REGISTER_MASK; case 1: case 3: + Log_DebugPrintf("CDROM read interrupt flag register <- 0x%02X", ZeroExtend32(m_interrupt_flag_register)); return m_interrupt_flag_register; } } @@ -134,10 +140,10 @@ void CDROM::WriteRegister(u32 offset, u8 value) case 0: { Log_DebugPrintf("CDROM command register <- 0x%02X", ZeroExtend32(value)); - if (m_state != State::Idle) - Log_ErrorPrintf("Ignoring write (0x%02X) to command register in non-idle state", ZeroExtend32(value)); + if (m_command_state == CommandState::Idle) + BeginCommand(static_cast(value)); else - ExecuteCommand(static_cast(value)); + Log_ErrorPrintf("Ignoring write (0x%02X) to command register in non-idle state", ZeroExtend32(value)); return; } @@ -208,7 +214,11 @@ void CDROM::WriteRegister(u32 offset, u8 value) { case 0: { - Log_ErrorPrintf("Request register <- 0x%02X", value); + Log_DebugPrintf("Request register <- 0x%02X", value); + const RequestRegister rr{value}; + // if (!rr.BFRD) + // m_data_fifo.Clear(); + return; } @@ -216,7 +226,13 @@ void CDROM::WriteRegister(u32 offset, u8 value) { Log_DebugPrintf("Interrupt flag register <- 0x%02X", value); m_interrupt_flag_register &= ~(value & INTERRUPT_REGISTER_MASK); - Execute(); + if (m_interrupt_flag_register == 0 && m_command_state == CommandState::WaitForIRQClear) + { + m_system->Synchronize(); + m_command_state = CommandState::WaitForExecute; + m_system->SetDowncount(m_command_remaining_ticks); + } + return; } @@ -240,6 +256,32 @@ void CDROM::WriteRegister(u32 offset, u8 value) ZeroExtend32(m_status.index.GetValue()), ZeroExtend32(value)); } +u32 CDROM::DMARead() +{ + if (m_data_fifo.IsEmpty()) + { + Log_ErrorPrintf("DMA read on empty data FIFO"); + return UINT32_C(0xFFFFFFFF); + } + + u32 data; + if (m_data_fifo.GetSize() >= sizeof(data)) + { + std::memcpy(&data, m_data_fifo.GetFrontPointer(), sizeof(data)); + m_data_fifo.Remove(sizeof(data)); + } + else + { + Log_WarningPrintf("Unaligned DMA read on FIFO(%u)", m_data_fifo.GetSize()); + data = 0; + std::memcpy(&data, m_data_fifo.GetFrontPointer(), m_data_fifo.GetSize()); + m_data_fifo.Clear(); + } + + Log_DebugPrintf("DMA Read -> 0x%08X", data); + return data; +} + void CDROM::SetInterrupt(Interrupt interrupt) { m_interrupt_flag_register = static_cast(interrupt); @@ -251,19 +293,90 @@ void CDROM::UpdateStatusRegister() { m_status.ADPBUSY = false; m_status.PRMEMPTY = m_param_fifo.IsEmpty(); - m_status.PRMWRDY = m_param_fifo.IsFull(); + 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; + m_status.BUSYSTS = m_command_state != CommandState::Idle; } -void CDROM::Execute() {} - -void CDROM::ExecuteCommand(Command command) +u32 CDROM::GetTicksForCommand() const { - Log_ErrorPrintf("CDROM write command 0x%02X", ZeroExtend32(static_cast(command))); + return 100; +} - switch (command) +void CDROM::Execute(TickCount ticks) +{ + switch (m_command_state) + { + case CommandState::Idle: + case CommandState::WaitForIRQClear: + break; + + case CommandState::WaitForExecute: + { + m_command_remaining_ticks -= ticks; + if (m_command_remaining_ticks <= 0) + ExecuteCommand(); + } + break; + + default: + UnreachableCode(); + break; + } + + if (m_reading) + { + m_sector_read_remaining_ticks -= ticks; + if (m_sector_read_remaining_ticks <= 0) + DoSectorRead(); + } +} + +void CDROM::BeginCommand(Command command) +{ + m_response_fifo.Clear(); + + m_system->Synchronize(); + m_command = command; + m_command_stage = 0; + m_command_remaining_ticks = GetTicksForCommand(); + m_command_state = CommandState::WaitForExecute; + m_system->SetDowncount(m_command_remaining_ticks); + UpdateStatusRegister(); +} + +void CDROM::NextCommandStage(bool wait_for_irq, u32 time) +{ + // prevent re-execution when synchronizing below + m_command_state = CommandState::WaitForIRQClear; + m_command_remaining_ticks = time; + m_command_stage++; + if (wait_for_irq) + return; + + m_system->Synchronize(); + m_command_state = CommandState::WaitForExecute; + m_system->SetDowncount(m_command_remaining_ticks); + UpdateStatusRegister(); +} + +void CDROM::EndCommand() +{ + m_param_fifo.Clear(); + + m_command_state = CommandState::Idle; + m_command = Command::Sync; + m_command_stage = 0; + m_command_remaining_ticks = 0; + UpdateStatusRegister(); +} + +void CDROM::ExecuteCommand() +{ + Log_DevPrintf("CDROM executing command 0x%02X stage %u", ZeroExtend32(static_cast(m_command)), m_command_stage); + + switch (m_command) { case Command::Getstat: { @@ -272,16 +385,137 @@ void CDROM::ExecuteCommand(Command command) // if bit 0 or 2 is set, send an additional byte m_response_fifo.Push(m_secondary_status.bits); SetInterrupt(Interrupt::INT3); - UpdateStatusRegister(); + EndCommand(); + return; } - break; case Command::Test: { const u8 subcommand = m_param_fifo.Pop(); ExecuteTestCommand(subcommand); + return; + } + + case Command::GetID: + { + Log_DebugPrintf("CDROM GetID command - stage %u", m_command_stage); + if (m_command_stage == 0) + { + if (!HasMedia()) + { + static constexpr u8 response[] = {0x11, 0x80}; + m_response_fifo.PushRange(response, countof(response)); + SetInterrupt(Interrupt::INT5); + EndCommand(); + } + else + { + // INT3(stat), ... + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + NextCommandStage(true, GetTicksForCommand()); + } + } + else + { + static constexpr u8 response2[] = {0x02, 0x00, 0x20, 0x00, 0x53, 0x43, 0x45, 0x41}; // last byte is 0x49 for EU + m_response_fifo.PushRange(response2, countof(response2)); + SetInterrupt(Interrupt::INT2); + EndCommand(); + } + + return; + } + + case Command::Setloc: + { + // TODO: Verify parameter count + m_setloc.minute = m_param_fifo.Peek(0); + m_setloc.second = m_param_fifo.Peek(1); + m_setloc.frame = m_param_fifo.Peek(2); + Log_DebugPrintf("CDROM setloc command (%u, %u, %u)", ZeroExtend32(m_setloc.minute), ZeroExtend32(m_setloc.second), + ZeroExtend32(m_setloc.frame)); + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + EndCommand(); + return; + } + + case Command::SeekL: + case Command::SeekP: + { + // TODO: Data vs audio mode + Log_DebugPrintf("CDROM seek command"); + + if (m_command_stage == 0) + { + StopReading(); + if (!m_media || !m_media->Seek(m_setloc.minute, m_setloc.second, m_setloc.frame)) + { + Panic("Error in Setloc command"); + return; + } + + m_secondary_status.motor_on = true; + m_secondary_status.seeking = true; + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + NextCommandStage(false, 100); + } + else + { + m_secondary_status.seeking = false; + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT2); + EndCommand(); + } + + return; + } + + case Command::Setmode: + { + const u8 mode = m_param_fifo.Peek(0); + Log_DebugPrintf("CDROM setmode command 0x%02X", ZeroExtend32(mode)); + StopReading(); + + m_mode.bits = mode; + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + EndCommand(); + return; + } + + case Command::ReadN: + { + Log_DebugPrintf("CDROM read command"); + StopReading(); + EndCommand(); + BeginReading(); + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + return; + } + + case Command::Pause: + { + if (m_command_stage == 0) + { + Log_DebugPrintf("CDROM pause command"); + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT3); + StopReading(); + NextCommandStage(true, 100); + } + else + { + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT2); + EndCommand(); + } + + return; } - break; default: Panic("Unknown command"); @@ -298,9 +532,8 @@ void CDROM::ExecuteTestCommand(u8 subcommand) 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(); + EndCommand(); return; } @@ -309,9 +542,8 @@ void CDROM::ExecuteTestCommand(u8 subcommand) Log_DebugPrintf("Get CDROM region ID string"); static constexpr u8 response[] = {'f', 'o', 'r', ' ', 'U', '/', 'C'}; m_response_fifo.PushRange(response, countof(response)); - m_param_fifo.Clear(); SetInterrupt(Interrupt::INT3); - UpdateStatusRegister(); + EndCommand(); return; } @@ -322,3 +554,53 @@ void CDROM::ExecuteTestCommand(u8 subcommand) } } } + +void CDROM::BeginReading() +{ + Log_DebugPrintf("Starting reading"); + m_system->Synchronize(); + + m_secondary_status.motor_on = true; + m_secondary_status.seeking = false; + m_secondary_status.reading = true; + + m_reading = true; + m_sector_read_remaining_ticks = 100; + m_system->SetDowncount(m_sector_read_remaining_ticks); + UpdateStatusRegister(); +} + +void CDROM::DoSectorRead() +{ + if (HasPendingInterrupt()) + { + // can't read with a pending interrupt? + m_sector_read_remaining_ticks += 100; + m_system->SetDowncount(m_sector_read_remaining_ticks); + return; + } + + Log_DebugPrintf("Reading sector %llu", m_media->GetCurrentLBA()); + + // TODO: Error handling + u8 buffer[CDImage::RAW_SECTOR_SIZE]; + m_media->Read(m_mode.read_raw_sector ? CDImage::ReadMode::RawNoSync : CDImage::ReadMode::DataOnly, 1, buffer); + m_data_fifo.Clear(); + m_data_fifo.PushRange(buffer, m_mode.read_raw_sector ? CDImage::RAW_SECTOR_SIZE : CDImage::DATA_SECTOR_SIZE); + m_response_fifo.Push(m_secondary_status.bits); + SetInterrupt(Interrupt::INT1); + UpdateStatusRegister(); + + m_sector_read_remaining_ticks += 100; + m_system->SetDowncount(m_sector_read_remaining_ticks); +} + +void CDROM::StopReading() +{ + if (!m_reading) + return; + + Log_DebugPrintf("Stopping reading"); + m_secondary_status.reading = false; + m_reading = false; +} diff --git a/src/pse/cdrom.h b/src/pse/cdrom.h index 20767940a..4823d298c 100644 --- a/src/pse/cdrom.h +++ b/src/pse/cdrom.h @@ -1,11 +1,12 @@ #pragma once -#include "types.h" #include "common/bitfield.h" #include "common/fifo_queue.h" +#include "types.h" class CDImage; class StateWrapper; +class System; class DMA; class InterruptController; @@ -15,7 +16,7 @@ public: CDROM(); ~CDROM(); - bool Initialize(DMA* dma, InterruptController* interrupt_controller); + bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller); void Reset(); bool DoState(StateWrapper& sw); @@ -26,8 +27,9 @@ public: // I/O u8 ReadRegister(u32 offset); void WriteRegister(u32 offset, u8 value); + u32 DMARead(); - void Execute(); + void Execute(TickCount ticks); private: static constexpr u32 PARAM_FIFO_SIZE = 16; @@ -78,27 +80,24 @@ private: Reset = 0x1C, GetQ = 0x1D, ReadTOC = 0x1E, - VideoCD = 0x1F, + VideoCD = 0x1F }; - bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; } - void SetInterrupt(Interrupt interrupt); - void UpdateStatusRegister(); - void ExecuteCommand(Command command); - void ExecuteTestCommand(u8 subcommand); - - DMA* m_dma; - InterruptController* m_interrupt_controller; - std::unique_ptr m_media; - - enum class State : u32 + enum class CommandState : u32 { - Idle + Idle, + WaitForExecute, + WaitForIRQClear }; - State m_state = State::Idle; + struct Loc + { + u8 minute; + u8 second; + u8 frame; + }; - union + union StatusRegister { u8 bits; BitField index; @@ -108,9 +107,9 @@ private: BitField RSLRRDY; BitField DRQSTS; BitField BUSYSTS; - } m_status = {}; + }; - union + union SecondaryStatusRegister { u8 bits; BitField error; @@ -121,7 +120,59 @@ private: BitField reading; BitField seeking; BitField playing_cdda; - } m_secondary_status = {}; + }; + + union ModeRegister + { + u8 bits; + BitField cdda; + BitField auto_pause; + BitField report_audio; + BitField xa_filter; + BitField ignore_bit; + BitField read_raw_sector; + BitField xa_adpcm; + BitField double_speed; + }; + + union RequestRegister + { + u8 bits; + BitField SMEN; + BitField BFWR; + BitField BFRD; + }; + + bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; } + void SetInterrupt(Interrupt interrupt); + void UpdateStatusRegister(); + + u32 GetTicksForCommand() const; + void BeginCommand(Command command); // also update status register + void NextCommandStage(bool wait_for_irq, u32 time); + void EndCommand(); // also updates status register + void ExecuteCommand(); + void ExecuteTestCommand(u8 subcommand); + void BeginReading(); + void DoSectorRead(); + void StopReading(); + + System* m_system = nullptr; + DMA* m_dma = nullptr; + InterruptController* m_interrupt_controller = nullptr; + std::unique_ptr m_media; + + CommandState m_command_state = CommandState::Idle; + Command m_command = Command::Sync; + u32 m_command_stage = 0; + TickCount m_command_remaining_ticks = 0; + + TickCount m_sector_read_remaining_ticks = 0; + bool m_reading = false; + + StatusRegister m_status = {}; + SecondaryStatusRegister m_secondary_status = {}; + ModeRegister m_mode = {}; u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; u8 m_interrupt_flag_register = 0; @@ -129,5 +180,6 @@ private: InlineFIFOQueue m_param_fifo; InlineFIFOQueue m_response_fifo; HeapFIFOQueue m_data_fifo; -}; + Loc m_setloc = {}; +}; diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index 842d61dd3..80fc49c49 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -1,6 +1,7 @@ #include "dma.h" #include "YBaseLib/Log.h" #include "bus.h" +#include "cdrom.h" #include "common/state_wrapper.h" #include "gpu.h" Log_SetChannel(DMA); @@ -9,10 +10,11 @@ DMA::DMA() = default; DMA::~DMA() = default; -bool DMA::Initialize(Bus* bus, GPU* gpu) +bool DMA::Initialize(Bus* bus, GPU* gpu, CDROM* cdrom) { m_bus = bus; m_gpu = gpu; + m_cdrom = cdrom; return true; } @@ -304,9 +306,11 @@ u32 DMA::DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaini case Channel::GPU: return m_gpu->DMARead(); + case Channel::CDROM: + return m_cdrom->DMARead(); + case Channel::MDECin: case Channel::MDECout: - case Channel::CDROM: case Channel::SPU: case Channel::PIO: default: diff --git a/src/pse/dma.h b/src/pse/dma.h index 5155c874e..0bc08c6b4 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -7,6 +7,7 @@ class StateWrapper; class Bus; class GPU; +class CDROM; class DMA { @@ -30,7 +31,7 @@ public: DMA(); ~DMA(); - bool Initialize(Bus* bus, GPU* gpu); + bool Initialize(Bus* bus, GPU* gpu, CDROM* cdrom); void Reset(); bool DoState(StateWrapper& sw); @@ -63,6 +64,7 @@ private: Bus* m_bus = nullptr; GPU* m_gpu = nullptr; + CDROM* m_cdrom = nullptr; struct ChannelState { diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 58d3de7b5..35f21ced6 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -37,7 +37,7 @@ bool System::Initialize() return false; } - if (!m_dma->Initialize(m_bus.get(), m_gpu.get())) + if (!m_dma->Initialize(m_bus.get(), m_gpu.get(), m_cdrom.get())) return false; if (!m_interrupt_controller->Initialize(m_cpu.get())) @@ -46,7 +46,7 @@ bool System::Initialize() if (!m_gpu->Initialize(this, m_dma.get(), m_interrupt_controller.get(), m_timers.get())) return false; - if (!m_cdrom->Initialize(m_dma.get(), m_interrupt_controller.get())) + if (!m_cdrom->Initialize(this, m_dma.get(), m_interrupt_controller.get())) return false; if (!m_pad->Initialize(m_interrupt_controller.get())) @@ -218,6 +218,7 @@ void System::Synchronize() m_gpu->Execute(pending_ticks); m_timers->AddSystemTicks(pending_ticks); + m_cdrom->Execute(pending_ticks); } void System::SetDowncount(TickCount downcount)