#pragma once #include "common/bitfield.h" #include "common/cd_image.h" #include "common/cd_xa.h" #include "common/fifo_queue.h" #include "common/heap_array.h" #include "types.h" #include #include #include class StateWrapper; class System; class DMA; class InterruptController; class SPU; class CDROM { public: CDROM(); ~CDROM(); bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu); void Reset(); bool DoState(StateWrapper& sw); bool HasMedia() const { return static_cast(m_media); } bool InsertMedia(const char* filename); void RemoveMedia(); // I/O u8 ReadRegister(u32 offset); void WriteRegister(u32 offset, u8 value); void DMARead(u32* words, u32 word_count); void Execute(TickCount ticks); // Render statistics debug window. void DrawDebugWindow(); private: enum : u32 { RAW_SECTOR_OUTPUT_SIZE = CDImage::RAW_SECTOR_SIZE - CDImage::SECTOR_SYNC_SIZE, DATA_SECTOR_OUTPUT_SIZE = CDImage::DATA_SECTOR_SIZE, SECTOR_SYNC_SIZE = CDImage::SECTOR_SYNC_SIZE, SECTOR_HEADER_SIZE = CDImage::SECTOR_HEADER_SIZE, XA_RESAMPLE_RING_BUFFER_SIZE = 32, XA_RESAMPLE_ZIGZAG_TABLE_SIZE = 29, XA_RESAMPLE_NUM_ZIGZAG_TABLES = 7, PARAM_FIFO_SIZE = 16, RESPONSE_FIFO_SIZE = 16, DATA_FIFO_SIZE = RAW_SECTOR_OUTPUT_SIZE, NUM_INTERRUPTS = 32 }; static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F; enum class Interrupt : u8 { INT1 = 0x01, INT2 = 0x02, ACK = 0x03, INT4 = 0x04, INT5 = 0x05 }; enum class Command : u16 { 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, None = 0xFFFF }; enum class DriveState : u8 { Idle, Initializing, Seeking, ReadingID, Reading, Playing, Pausing, Stopping, }; union StatusRegister { u8 bits; BitField index; BitField ADPBUSY; BitField PRMEMPTY; BitField PRMWRDY; BitField RSLRRDY; BitField DRQSTS; BitField BUSYSTS; }; union SecondaryStatusRegister { u8 bits; BitField error; BitField motor_on; BitField seek_error; BitField id_error; BitField shell_open; BitField header_valid; BitField seeking; BitField playing_cdda; /// Clears the CDDA/seeking/header valid bits. ALWAYS_INLINE void ClearActiveBits() { header_valid = false; seeking = false; playing_cdda = false; } }; union ModeRegister { u8 bits; BitField cdda; BitField auto_pause; BitField report_audio; BitField xa_filter; BitField ignore_bit; BitField read_raw_sector; BitField xa_enable; BitField double_speed; }; union RequestRegister { u8 bits; BitField SMEN; BitField BFWR; BitField BFRD; }; void SoftReset(); bool IsDriveIdle() const { return m_drive_state == DriveState::Idle; } bool HasPendingCommand() const { return m_command != Command::None; } bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; } bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; } void SetInterrupt(Interrupt interrupt); void SetAsyncInterrupt(Interrupt interrupt); void CancelAsyncInterrupt(); void DeliverAsyncInterrupt(); void SendACKAndStat(); void SendErrorResponse(u8 reason = 0x80); void SendAsyncErrorResponse(u8 reason = 0x80); void UpdateStatusRegister(); TickCount GetAckDelayForCommand() const; TickCount GetTicksForRead() const; TickCount GetTicksForSeek() const; void BeginCommand(Command command); // also update status register void EndCommand(); // also updates status register void ExecuteCommand(); void ExecuteTestCommand(u8 subcommand); void BeginReading(bool cdda); void DoInitComplete(); void DoSeekComplete(); void DoPauseComplete(); void DoStopComplete(); void DoIDRead(); void DoSectorRead(); void ProcessDataSector(const u8* raw_sector); void ProcessXAADPCMSector(const u8* raw_sector); void ProcessCDDASector(const u8* raw_sector); void BeginSeeking(); void LoadDataFIFO(); System* m_system = nullptr; DMA* m_dma = nullptr; InterruptController* m_interrupt_controller = nullptr; SPU* m_spu = nullptr; std::unique_ptr m_media; Command m_command = Command::None; DriveState m_drive_state = DriveState::Idle; TickCount m_command_remaining_ticks = 0; TickCount m_drive_remaining_ticks = 0; StatusRegister m_status = {}; SecondaryStatusRegister m_secondary_status = {}; ModeRegister m_mode = {}; u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; u8 m_interrupt_flag_register = 0; u8 m_pending_async_interrupt = 0; CDImage::Position m_setloc_position = {}; CDImage::Position m_seek_position = {}; bool m_setloc_pending = false; bool m_read_after_seek = false; bool m_play_after_seek = false; bool m_muted = false; bool m_adpcm_muted = false; u8 m_filter_file_number = 0; u8 m_filter_channel_number = 0; CDImage::SectorHeader m_last_sector_header = {}; CDXA::XASubHeader m_last_sector_subheader = {}; std::array, 2> m_cd_audio_volume_matrix{}; std::array, 2> m_next_cd_audio_volume_matrix{}; std::array m_xa_last_samples{}; std::array, 2> m_xa_resample_ring_buffer{}; u8 m_xa_resample_p = 0; u8 m_xa_resample_sixstep = 6; InlineFIFOQueue m_param_fifo; InlineFIFOQueue m_response_fifo; InlineFIFOQueue m_async_response_fifo; HeapFIFOQueue m_data_fifo; std::vector m_sector_buffer; };