#pragma once #include "common/bitfield.h" #include "common/fifo_queue.h" #include "types.h" #include class CDImage; class StateWrapper; class System; class DMA; class InterruptController; class CDROM { public: CDROM(); ~CDROM(); bool Initialize(System* system, DMA* dma, InterruptController* interrupt_controller); 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); u32 DMARead(); void Execute(TickCount ticks); private: static constexpr u32 PARAM_FIFO_SIZE = 16; static constexpr u32 RESPONSE_FIFO_SIZE = 16; static constexpr u32 DATA_FIFO_SIZE = 4096; static constexpr u32 NUM_INTERRUPTS = 32; static constexpr u32 SECTOR_BUFFER_SIZE = (2352 - 12); 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 }; enum class CommandState : u32 { Idle, WaitForExecute, WaitForIRQClear }; struct Loc { u8 minute; u8 second; u8 frame; }; 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 reading; BitField seeking; BitField playing_cdda; }; 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; u32 GetTicksForRead() 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; bool m_muted = false; Loc m_setloc = {}; bool m_setloc_dirty = 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; InlineFIFOQueue m_param_fifo; InlineFIFOQueue m_response_fifo; HeapFIFOQueue m_data_fifo; std::vector m_sector_buffer; };