duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cdrom.cpp (134277B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "cdrom.h"
      5 #include "cdrom_async_reader.h"
      6 #include "dma.h"
      7 #include "host.h"
      8 #include "host_interface_progress_callback.h"
      9 #include "interrupt_controller.h"
     10 #include "settings.h"
     11 #include "spu.h"
     12 #include "system.h"
     13 #include "timing_event.h"
     14 
     15 #include "util/cd_image.h"
     16 #include "util/imgui_manager.h"
     17 #include "util/iso_reader.h"
     18 #include "util/state_wrapper.h"
     19 
     20 #include "common/align.h"
     21 #include "common/bitfield.h"
     22 #include "common/fifo_queue.h"
     23 #include "common/file_system.h"
     24 #include "common/gsvector.h"
     25 #include "common/heap_array.h"
     26 #include "common/log.h"
     27 
     28 #include "imgui.h"
     29 
     30 #include <cmath>
     31 #include <map>
     32 #include <vector>
     33 
     34 Log_SetChannel(CDROM);
     35 
     36 namespace CDROM {
     37 namespace {
     38 
     39 enum : u32
     40 {
     41   RAW_SECTOR_OUTPUT_SIZE = CDImage::RAW_SECTOR_SIZE - CDImage::SECTOR_SYNC_SIZE,
     42   DATA_SECTOR_OUTPUT_SIZE = CDImage::DATA_SECTOR_SIZE,
     43   SECTOR_SYNC_SIZE = CDImage::SECTOR_SYNC_SIZE,
     44   SECTOR_HEADER_SIZE = CDImage::SECTOR_HEADER_SIZE,
     45 
     46   XA_SUBHEADER_SIZE = 4,
     47   XA_ADPCM_SAMPLES_PER_SECTOR_4BIT = 4032, // 28 words * 8 nibbles per word * 18 chunks
     48   XA_ADPCM_SAMPLES_PER_SECTOR_8BIT = 2016, // 28 words * 4 bytes per word * 18 chunks
     49   XA_RESAMPLE_RING_BUFFER_SIZE = 32,
     50   XA_RESAMPLE_ZIGZAG_TABLE_SIZE = 29,
     51   XA_RESAMPLE_NUM_ZIGZAG_TABLES = 7,
     52 
     53   PARAM_FIFO_SIZE = 16,
     54   RESPONSE_FIFO_SIZE = 16,
     55   DATA_FIFO_SIZE = RAW_SECTOR_OUTPUT_SIZE,
     56   NUM_SECTOR_BUFFERS = 8,
     57   AUDIO_FIFO_SIZE = 44100 * 2,
     58   AUDIO_FIFO_LOW_WATERMARK = 10,
     59 
     60   INIT_TICKS = 4000000,
     61   ID_READ_TICKS = 33868,
     62   MOTOR_ON_RESPONSE_TICKS = 400000,
     63 
     64   MAX_FAST_FORWARD_RATE = 12,
     65   FAST_FORWARD_RATE_STEP = 4,
     66 
     67   MINIMUM_INTERRUPT_DELAY = 1000,
     68   INTERRUPT_DELAY_CYCLES = 500,
     69 };
     70 
     71 static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F;
     72 
     73 enum class Interrupt : u8
     74 {
     75   DataReady = 0x01,
     76   Complete = 0x02,
     77   ACK = 0x03,
     78   DataEnd = 0x04,
     79   Error = 0x05
     80 };
     81 
     82 enum class Command : u16
     83 {
     84   Sync = 0x00,
     85   Getstat = 0x01,
     86   Setloc = 0x02,
     87   Play = 0x03,
     88   Forward = 0x04,
     89   Backward = 0x05,
     90   ReadN = 0x06,
     91   MotorOn = 0x07,
     92   Stop = 0x08,
     93   Pause = 0x09,
     94   Init = 0x0A,
     95   Mute = 0x0B,
     96   Demute = 0x0C,
     97   Setfilter = 0x0D,
     98   Setmode = 0x0E,
     99   Getmode = 0x0F,
    100   GetlocL = 0x10,
    101   GetlocP = 0x11,
    102   ReadT = 0x12,
    103   GetTN = 0x13,
    104   GetTD = 0x14,
    105   SeekL = 0x15,
    106   SeekP = 0x16,
    107   SetClock = 0x17,
    108   GetClock = 0x18,
    109   Test = 0x19,
    110   GetID = 0x1A,
    111   ReadS = 0x1B,
    112   Reset = 0x1C,
    113   GetQ = 0x1D,
    114   ReadTOC = 0x1E,
    115   VideoCD = 0x1F,
    116 
    117   None = 0xFFFF
    118 };
    119 
    120 enum class DriveState : u8
    121 {
    122   Idle,
    123   ShellOpening,
    124   UNUSED_Resetting,
    125   SeekingPhysical,
    126   SeekingLogical,
    127   UNUSED_ReadingID,
    128   UNUSED_ReadingTOC,
    129   Reading,
    130   Playing,
    131   UNUSED_Pausing,
    132   UNUSED_Stopping,
    133   ChangingSession,
    134   SpinningUp,
    135   SeekingImplicit,
    136   ChangingSpeedOrTOCRead
    137 };
    138 
    139 union StatusRegister
    140 {
    141   u8 bits;
    142   BitField<u8, u8, 0, 2> index;
    143   BitField<u8, bool, 2, 1> ADPBUSY;
    144   BitField<u8, bool, 3, 1> PRMEMPTY;
    145   BitField<u8, bool, 4, 1> PRMWRDY;
    146   BitField<u8, bool, 5, 1> RSLRRDY;
    147   BitField<u8, bool, 6, 1> DRQSTS;
    148   BitField<u8, bool, 7, 1> BUSYSTS;
    149 };
    150 
    151 enum StatBits : u8
    152 {
    153   STAT_ERROR = (1 << 0),
    154   STAT_MOTOR_ON = (1 << 1),
    155   STAT_SEEK_ERROR = (1 << 2),
    156   STAT_ID_ERROR = (1 << 3),
    157   STAT_SHELL_OPEN = (1 << 4),
    158   STAT_READING = (1 << 5),
    159   STAT_SEEKING = (1 << 6),
    160   STAT_PLAYING_CDDA = (1 << 7)
    161 };
    162 
    163 enum ErrorReason : u8
    164 {
    165   ERROR_REASON_INVALID_ARGUMENT = 0x10,
    166   ERROR_REASON_INCORRECT_NUMBER_OF_PARAMETERS = 0x20,
    167   ERROR_REASON_INVALID_COMMAND = 0x40,
    168   ERROR_REASON_NOT_READY = 0x80
    169 };
    170 
    171 union SecondaryStatusRegister
    172 {
    173   u8 bits;
    174   BitField<u8, bool, 0, 1> error;
    175   BitField<u8, bool, 1, 1> motor_on;
    176   BitField<u8, bool, 2, 1> seek_error;
    177   BitField<u8, bool, 3, 1> id_error;
    178   BitField<u8, bool, 4, 1> shell_open;
    179   BitField<u8, bool, 5, 1> reading;
    180   BitField<u8, bool, 6, 1> seeking;
    181   BitField<u8, bool, 7, 1> playing_cdda;
    182 
    183   /// Clears the CDDA/seeking bits.
    184   ALWAYS_INLINE void ClearActiveBits() { bits &= ~(STAT_SEEKING | STAT_READING | STAT_PLAYING_CDDA); }
    185 
    186   /// Sets the bits for seeking.
    187   ALWAYS_INLINE void SetSeeking()
    188   {
    189     bits = (bits & ~(STAT_READING | STAT_PLAYING_CDDA)) | (STAT_MOTOR_ON | STAT_SEEKING);
    190   }
    191 
    192   /// Sets the bits for reading/playing.
    193   ALWAYS_INLINE void SetReadingBits(bool audio)
    194   {
    195     bits = (bits & ~(STAT_SEEKING | STAT_READING | STAT_PLAYING_CDDA)) |
    196            ((audio) ? (STAT_MOTOR_ON | STAT_PLAYING_CDDA) : (STAT_MOTOR_ON | STAT_READING));
    197   }
    198 };
    199 
    200 union ModeRegister
    201 {
    202   u8 bits;
    203   BitField<u8, bool, 0, 1> cdda;
    204   BitField<u8, bool, 1, 1> auto_pause;
    205   BitField<u8, bool, 2, 1> report_audio;
    206   BitField<u8, bool, 3, 1> xa_filter;
    207   BitField<u8, bool, 4, 1> ignore_bit;
    208   BitField<u8, bool, 5, 1> read_raw_sector;
    209   BitField<u8, bool, 6, 1> xa_enable;
    210   BitField<u8, bool, 7, 1> double_speed;
    211 };
    212 
    213 union RequestRegister
    214 {
    215   u8 bits;
    216   BitField<u8, bool, 5, 1> SMEN;
    217   BitField<u8, bool, 6, 1> BFWR;
    218   BitField<u8, bool, 7, 1> BFRD;
    219 };
    220 
    221 struct XASubHeader
    222 {
    223   u8 file_number;
    224   u8 channel_number;
    225 
    226   union Submode
    227   {
    228     u8 bits;
    229     BitField<u8, bool, 0, 1> eor;
    230     BitField<u8, bool, 1, 1> video;
    231     BitField<u8, bool, 2, 1> audio;
    232     BitField<u8, bool, 3, 1> data;
    233     BitField<u8, bool, 4, 1> trigger;
    234     BitField<u8, bool, 5, 1> form2;
    235     BitField<u8, bool, 6, 1> realtime;
    236     BitField<u8, bool, 7, 1> eof;
    237   } submode;
    238 
    239   union Codinginfo
    240   {
    241     u8 bits;
    242 
    243     BitField<u8, bool, 0, 1> mono_stereo;
    244     BitField<u8, bool, 2, 1> sample_rate;
    245     BitField<u8, bool, 4, 1> bits_per_sample;
    246     BitField<u8, bool, 6, 1> emphasis;
    247 
    248     ALWAYS_INLINE bool IsStereo() const { return mono_stereo; }
    249     ALWAYS_INLINE bool IsHalfSampleRate() const { return sample_rate; }
    250     ALWAYS_INLINE bool Is8BitADPCM() const { return bits_per_sample; }
    251     u32 GetSamplesPerSector() const
    252     {
    253       return bits_per_sample ? XA_ADPCM_SAMPLES_PER_SECTOR_8BIT : XA_ADPCM_SAMPLES_PER_SECTOR_4BIT;
    254     }
    255   } codinginfo;
    256 };
    257 
    258 union XA_ADPCMBlockHeader
    259 {
    260   u8 bits;
    261 
    262   BitField<u8, u8, 0, 4> shift;
    263   BitField<u8, u8, 4, 2> filter;
    264 
    265   // For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9).
    266   u8 GetShift() const
    267   {
    268     const u8 shift_value = shift;
    269     return (shift_value > 12) ? 9 : shift_value;
    270   }
    271 
    272   u8 GetFilter() const { return filter; }
    273 };
    274 static_assert(sizeof(XA_ADPCMBlockHeader) == 1, "XA-ADPCM block header is one byte");
    275 
    276 } // namespace
    277 
    278 static TickCount SoftReset(TickCount ticks_late);
    279 
    280 static bool IsDriveIdle();
    281 static bool IsMotorOn();
    282 static bool IsSeeking();
    283 static bool IsReadingOrPlaying();
    284 static bool CanReadMedia();
    285 static bool HasPendingCommand();
    286 static bool HasPendingInterrupt();
    287 static bool HasPendingAsyncInterrupt();
    288 static void AddCDAudioFrame(s16 left, s16 right);
    289 
    290 static s32 ApplyVolume(s16 sample, u8 volume);
    291 static s16 SaturateVolume(s32 volume);
    292 
    293 static void SetInterrupt(Interrupt interrupt);
    294 static void SetAsyncInterrupt(Interrupt interrupt);
    295 static void ClearAsyncInterrupt();
    296 static void DeliverAsyncInterrupt(void*, TickCount ticks, TickCount ticks_late);
    297 static void QueueDeliverAsyncInterrupt();
    298 static void SendACKAndStat();
    299 static void SendErrorResponse(u8 stat_bits = STAT_ERROR, u8 reason = 0x80);
    300 static void SendAsyncErrorResponse(u8 stat_bits = STAT_ERROR, u8 reason = 0x80);
    301 static void UpdateStatusRegister();
    302 static void UpdateInterruptRequest();
    303 static bool HasPendingDiscEvent();
    304 
    305 static TickCount GetAckDelayForCommand(Command command);
    306 static TickCount GetTicksForSpinUp();
    307 static TickCount GetTicksForIDRead();
    308 static TickCount GetTicksForRead();
    309 static TickCount GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change = false);
    310 static TickCount GetTicksForStop(bool motor_was_on);
    311 static TickCount GetTicksForSpeedChange();
    312 static TickCount GetTicksForTOCRead();
    313 static CDImage::LBA GetNextSectorToBeRead();
    314 static bool CompleteSeek();
    315 
    316 static void BeginCommand(Command command); // also update status register
    317 static void EndCommand();                  // also updates status register
    318 static void ExecuteCommand(void*, TickCount ticks, TickCount ticks_late);
    319 static void ExecuteTestCommand(u8 subcommand);
    320 static void ExecuteCommandSecondResponse(void*, TickCount ticks, TickCount ticks_late);
    321 static void QueueCommandSecondResponse(Command command, TickCount ticks);
    322 static void ClearCommandSecondResponse();
    323 static void UpdateCommandEvent();
    324 static void ExecuteDrive(void*, TickCount ticks, TickCount ticks_late);
    325 static void ClearDriveState();
    326 static void BeginReading(TickCount ticks_late = 0, bool after_seek = false);
    327 static void BeginPlaying(u8 track, TickCount ticks_late = 0, bool after_seek = false);
    328 static void DoShellOpenComplete(TickCount ticks_late);
    329 static void DoSeekComplete(TickCount ticks_late);
    330 static void DoStatSecondResponse();
    331 static void DoChangeSessionComplete();
    332 static void DoSpinUpComplete();
    333 static void DoSpeedChangeOrImplicitTOCReadComplete();
    334 static void DoIDRead();
    335 static void DoSectorRead();
    336 static void ProcessDataSectorHeader(const u8* raw_sector);
    337 static void ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
    338 static void ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
    339 static void ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq, bool subq_valid);
    340 static void StopReadingWithDataEnd();
    341 static void StartMotor();
    342 static void StopMotor();
    343 static void BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek);
    344 static void UpdatePositionWhileSeeking();
    345 static void UpdatePhysicalPosition(bool update_logical);
    346 static void SetHoldPosition(CDImage::LBA lba, bool update_subq);
    347 static void ResetCurrentXAFile();
    348 static void ResetAudioDecoder();
    349 static void ClearSectorBuffers();
    350 static void CheckForSectorBufferReadComplete();
    351 
    352 // Decodes XA-ADPCM samples in an audio sector. Stereo samples are interleaved with left first.
    353 template<bool IS_STEREO, bool IS_8BIT>
    354 static void DecodeXAADPCMChunks(const u8* chunk_ptr, s16* samples);
    355 template<bool STEREO>
    356 static void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in);
    357 template<bool STEREO>
    358 static void ResampleXAADPCM18900(const s16* frames_in, u32 num_frames_in);
    359 
    360 static TinyString LBAToMSFString(CDImage::LBA lba);
    361 
    362 static void CreateFileMap();
    363 static void CreateFileMap(IsoReader& iso, std::string_view dir);
    364 static const std::string* LookupFileMap(u32 lba, u32* start_lba, u32* end_lba);
    365 
    366 static TimingEvent s_command_event{"CDROM Command Event", 1, 1, &CDROM::ExecuteCommand, nullptr};
    367 static TimingEvent s_command_second_response_event{"CDROM Command Second Response Event", 1, 1,
    368                                                    &CDROM::ExecuteCommandSecondResponse, nullptr};
    369 static TimingEvent s_async_interrupt_event{"CDROM Async Interrupt Event", INTERRUPT_DELAY_CYCLES, 1,
    370                                            &CDROM::DeliverAsyncInterrupt, nullptr};
    371 static TimingEvent s_drive_event{"CDROM Drive Event", 1, 1, &CDROM::ExecuteDrive, nullptr};
    372 
    373 static Command s_command = Command::None;
    374 static Command s_command_second_response = Command::None;
    375 static DriveState s_drive_state = DriveState::Idle;
    376 static DiscRegion s_disc_region = DiscRegion::Other;
    377 
    378 static StatusRegister s_status = {};
    379 static SecondaryStatusRegister s_secondary_status = {};
    380 static ModeRegister s_mode = {};
    381 static RequestRegister s_request_register = {};
    382 
    383 static u8 s_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
    384 static u8 s_interrupt_flag_register = 0;
    385 static u8 s_pending_async_interrupt = 0;
    386 static GlobalTicks s_last_interrupt_time = 0;
    387 
    388 static CDImage::Position s_setloc_position = {};
    389 static CDImage::LBA s_requested_lba{};
    390 static CDImage::LBA s_current_lba{}; // this is the hold position
    391 static CDImage::LBA s_seek_start_lba{};
    392 static CDImage::LBA s_seek_end_lba{};
    393 static CDImage::LBA s_physical_lba{}; // current position of the disc with respect to time
    394 static GlobalTicks s_physical_lba_update_tick = 0;
    395 static u32 s_physical_lba_update_carry = 0;
    396 static bool s_setloc_pending = false;
    397 static bool s_read_after_seek = false;
    398 static bool s_play_after_seek = false;
    399 
    400 static bool s_muted = false;
    401 static bool s_adpcm_muted = false;
    402 
    403 static u8 s_xa_filter_file_number = 0;
    404 static u8 s_xa_filter_channel_number = 0;
    405 static u8 s_xa_current_file_number = 0;
    406 static u8 s_xa_current_channel_number = 0;
    407 static bool s_xa_current_set = false;
    408 static XASubHeader::Codinginfo s_xa_current_codinginfo = {};
    409 
    410 static CDImage::SectorHeader s_last_sector_header{};
    411 static XASubHeader s_last_sector_subheader{};
    412 static bool s_last_sector_header_valid = false; // TODO: Rename to "logical pause" or something.
    413 static CDImage::SubChannelQ s_last_subq{};
    414 static u8 s_last_cdda_report_frame_nibble = 0xFF;
    415 static u8 s_play_track_number_bcd = 0xFF;
    416 static u8 s_async_command_parameter = 0x00;
    417 static s8 s_fast_forward_rate = 0;
    418 
    419 static std::array<std::array<u8, 2>, 2> s_cd_audio_volume_matrix{};
    420 static std::array<std::array<u8, 2>, 2> s_next_cd_audio_volume_matrix{};
    421 
    422 static std::array<s32, 4> s_xa_last_samples{};
    423 static std::array<std::array<s16, XA_RESAMPLE_RING_BUFFER_SIZE>, 2> s_xa_resample_ring_buffer{};
    424 static u8 s_xa_resample_p = 0;
    425 static u8 s_xa_resample_sixstep = 6;
    426 
    427 static InlineFIFOQueue<u8, PARAM_FIFO_SIZE> s_param_fifo;
    428 static InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> s_response_fifo;
    429 static InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> s_async_response_fifo;
    430 
    431 struct SectorBuffer
    432 {
    433   FixedHeapArray<u8, RAW_SECTOR_OUTPUT_SIZE> data;
    434   u32 position;
    435   u32 size;
    436 };
    437 
    438 static u32 s_current_read_sector_buffer = 0;
    439 static u32 s_current_write_sector_buffer = 0;
    440 static std::array<SectorBuffer, NUM_SECTOR_BUFFERS> s_sector_buffers;
    441 
    442 static CDROMAsyncReader s_reader;
    443 
    444 // two 16-bit samples packed in 32-bits
    445 static HeapFIFOQueue<u32, AUDIO_FIFO_SIZE> s_audio_fifo;
    446 
    447 static std::map<u32, std::pair<u32, std::string>> s_file_map;
    448 static bool s_file_map_created = false;
    449 static bool s_show_current_file = false;
    450 
    451 static constexpr std::array<const char*, 15> s_drive_state_names = {
    452   {"Idle", "Opening Shell", "Resetting", "Seeking (Physical)", "Seeking (Logical)", "Reading ID", "Reading TOC",
    453    "Reading", "Playing", "Pausing", "Stopping", "Changing Session", "Spinning Up", "Seeking (Implicit)",
    454    "Changing Speed/Implicit TOC Read"}};
    455 
    456 struct CommandInfo
    457 {
    458   const char* name;
    459   u8 min_parameters;
    460   u8 max_parameters;
    461 };
    462 
    463 static std::array<CommandInfo, 255> s_command_info = {{
    464   {"Sync", 0, 0},     {"Getstat", 0, 0},   {"Setloc", 3, 3},  {"Play", 0, 1},    {"Forward", 0, 0}, {"Backward", 0, 0},
    465   {"ReadN", 0, 0},    {"Standby", 0, 0},   {"Stop", 0, 0},    {"Pause", 0, 0},   {"Init", 0, 0},    {"Mute", 0, 0},
    466   {"Demute", 0, 0},   {"Setfilter", 2, 2}, {"Setmode", 1, 1}, {"Getmode", 0, 0}, {"GetlocL", 0, 0}, {"GetlocP", 0, 0},
    467   {"ReadT", 1, 1},    {"GetTN", 0, 0},     {"GetTD", 1, 1},   {"SeekL", 0, 0},   {"SeekP", 0, 0},   {"SetClock", 0, 0},
    468   {"GetClock", 0, 0}, {"Test", 1, 16},     {"GetID", 0, 0},   {"ReadS", 0, 0},   {"Reset", 0, 0},   {"GetQ", 2, 2},
    469   {"ReadTOC", 0, 0},  {"VideoCD", 6, 16},  {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    470   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    471   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    472   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    473   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    474   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    475   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    476   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    477   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    478   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    479   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    480   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    481   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    482   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    483   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    484   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    485   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    486   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    487   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    488   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    489   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    490   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    491   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    492   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    493   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    494   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    495   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    496   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    497   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    498   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    499   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    500   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    501   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    502   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    503   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    504   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    505   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0}, {"Unknown", 0, 0},
    506   {"Unknown", 0, 0},  {"Unknown", 0, 0},   {nullptr, 0, 0} // Unknown
    507 }};
    508 
    509 } // namespace CDROM
    510 
    511 void CDROM::Initialize()
    512 {
    513   if (g_settings.cdrom_readahead_sectors > 0)
    514     s_reader.StartThread(g_settings.cdrom_readahead_sectors);
    515 
    516   Reset();
    517 }
    518 
    519 void CDROM::Shutdown()
    520 {
    521   s_file_map.clear();
    522   s_file_map_created = false;
    523   s_show_current_file = false;
    524 
    525   s_drive_event.Deactivate();
    526   s_async_interrupt_event.Deactivate();
    527   s_command_second_response_event.Deactivate();
    528   s_command_event.Deactivate();
    529   s_reader.StopThread();
    530   s_reader.RemoveMedia();
    531 }
    532 
    533 void CDROM::Reset()
    534 {
    535   s_command = Command::None;
    536   s_command_event.Deactivate();
    537   ClearCommandSecondResponse();
    538   ClearDriveState();
    539   s_status.bits = 0;
    540   s_secondary_status.bits = 0;
    541   s_secondary_status.motor_on = CanReadMedia();
    542   s_secondary_status.shell_open = !CanReadMedia();
    543   s_mode.bits = 0;
    544   s_mode.read_raw_sector = true;
    545   s_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
    546   s_interrupt_flag_register = 0;
    547   s_last_interrupt_time = System::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY;
    548   ClearAsyncInterrupt();
    549   s_setloc_position = {};
    550   s_seek_start_lba = 0;
    551   s_seek_end_lba = 0;
    552   s_setloc_pending = false;
    553   s_read_after_seek = false;
    554   s_play_after_seek = false;
    555   s_muted = false;
    556   s_adpcm_muted = false;
    557   s_xa_filter_file_number = 0;
    558   s_xa_filter_channel_number = 0;
    559   s_xa_current_file_number = 0;
    560   s_xa_current_channel_number = 0;
    561   s_xa_current_set = false;
    562   std::memset(&s_last_sector_header, 0, sizeof(s_last_sector_header));
    563   std::memset(&s_last_sector_subheader, 0, sizeof(s_last_sector_subheader));
    564   s_last_sector_header_valid = false;
    565   std::memset(&s_last_subq, 0, sizeof(s_last_subq));
    566   s_last_cdda_report_frame_nibble = 0xFF;
    567 
    568   s_next_cd_audio_volume_matrix[0][0] = 0x80;
    569   s_next_cd_audio_volume_matrix[0][1] = 0x00;
    570   s_next_cd_audio_volume_matrix[1][0] = 0x00;
    571   s_next_cd_audio_volume_matrix[1][1] = 0x80;
    572   s_cd_audio_volume_matrix = s_next_cd_audio_volume_matrix;
    573 
    574   ClearSectorBuffers();
    575   ResetAudioDecoder();
    576 
    577   s_param_fifo.Clear();
    578   s_response_fifo.Clear();
    579   s_async_response_fifo.Clear();
    580 
    581   UpdateStatusRegister();
    582 
    583   SetHoldPosition(0, true);
    584 }
    585 
    586 TickCount CDROM::SoftReset(TickCount ticks_late)
    587 {
    588   const bool was_double_speed = s_mode.double_speed;
    589 
    590   ClearCommandSecondResponse();
    591   ClearDriveState();
    592   s_secondary_status.bits = 0;
    593   s_secondary_status.motor_on = CanReadMedia();
    594   s_secondary_status.shell_open = !CanReadMedia();
    595   s_mode.bits = 0;
    596   s_mode.read_raw_sector = true;
    597   s_request_register.bits = 0;
    598   ClearAsyncInterrupt();
    599   s_setloc_position = {};
    600   s_setloc_pending = false;
    601   s_read_after_seek = false;
    602   s_play_after_seek = false;
    603   s_muted = false;
    604   s_adpcm_muted = false;
    605   s_last_cdda_report_frame_nibble = 0xFF;
    606 
    607   ClearSectorBuffers();
    608   ResetAudioDecoder();
    609 
    610   s_param_fifo.Clear();
    611   s_async_response_fifo.Clear();
    612 
    613   UpdateStatusRegister();
    614 
    615   TickCount total_ticks;
    616   if (HasMedia())
    617   {
    618     if (IsSeeking())
    619       UpdatePositionWhileSeeking();
    620     else
    621       UpdatePhysicalPosition(false);
    622 
    623     const TickCount speed_change_ticks = was_double_speed ? GetTicksForSpeedChange() : 0;
    624     const TickCount seek_ticks = (s_current_lba != 0) ? GetTicksForSeek(0) : 0;
    625     total_ticks = std::max<TickCount>(speed_change_ticks + seek_ticks, INIT_TICKS) - ticks_late;
    626     DEV_LOG("CDROM init total disc ticks = {} (speed change = {}, seek = {})", total_ticks, speed_change_ticks,
    627             seek_ticks);
    628 
    629     if (s_current_lba != 0)
    630     {
    631       s_drive_state = DriveState::SeekingImplicit;
    632       s_drive_event.SetIntervalAndSchedule(total_ticks);
    633       s_requested_lba = 0;
    634       s_reader.QueueReadSector(s_requested_lba);
    635       s_seek_start_lba = s_current_lba;
    636       s_seek_end_lba = 0;
    637     }
    638     else
    639     {
    640       s_drive_state = DriveState::ChangingSpeedOrTOCRead;
    641       s_drive_event.Schedule(total_ticks);
    642     }
    643   }
    644   else
    645   {
    646     total_ticks = INIT_TICKS - ticks_late;
    647   }
    648 
    649   return total_ticks;
    650 }
    651 
    652 bool CDROM::DoState(StateWrapper& sw)
    653 {
    654   sw.Do(&s_command);
    655   sw.DoEx(&s_command_second_response, 53, Command::None);
    656   sw.Do(&s_drive_state);
    657   sw.Do(&s_status.bits);
    658   sw.Do(&s_secondary_status.bits);
    659   sw.Do(&s_mode.bits);
    660   sw.DoEx(&s_request_register.bits, 65, static_cast<u8>(0));
    661 
    662   bool current_double_speed = s_mode.double_speed;
    663   sw.Do(&current_double_speed);
    664 
    665   sw.Do(&s_interrupt_enable_register);
    666   sw.Do(&s_interrupt_flag_register);
    667 
    668   if (sw.GetVersion() < 71) [[unlikely]]
    669   {
    670     u32 last_interrupt_time32 = 0;
    671     sw.DoEx(&last_interrupt_time32, 57, static_cast<u32>(System::GetGlobalTickCounter() - MINIMUM_INTERRUPT_DELAY));
    672     s_last_interrupt_time = last_interrupt_time32;
    673   }
    674   else
    675   {
    676     sw.Do(&s_last_interrupt_time);
    677   }
    678 
    679   sw.Do(&s_pending_async_interrupt);
    680   sw.DoPOD(&s_setloc_position);
    681   sw.Do(&s_current_lba);
    682   sw.Do(&s_seek_start_lba);
    683   sw.Do(&s_seek_end_lba);
    684   sw.DoEx(&s_physical_lba, 49, s_current_lba);
    685 
    686   if (sw.GetVersion() < 71) [[unlikely]]
    687   {
    688     u32 physical_lba_update_tick32 = 0;
    689     sw.DoEx(&physical_lba_update_tick32, 49, static_cast<u32>(0));
    690     s_physical_lba_update_tick = physical_lba_update_tick32;
    691   }
    692   else
    693   {
    694     sw.Do(&s_physical_lba_update_tick);
    695   }
    696 
    697   sw.DoEx(&s_physical_lba_update_carry, 54, static_cast<u32>(0));
    698   sw.Do(&s_setloc_pending);
    699   sw.Do(&s_read_after_seek);
    700   sw.Do(&s_play_after_seek);
    701   sw.Do(&s_muted);
    702   sw.Do(&s_adpcm_muted);
    703   sw.Do(&s_xa_filter_file_number);
    704   sw.Do(&s_xa_filter_channel_number);
    705   sw.Do(&s_xa_current_file_number);
    706   sw.Do(&s_xa_current_channel_number);
    707   sw.Do(&s_xa_current_set);
    708   sw.DoBytes(&s_last_sector_header, sizeof(s_last_sector_header));
    709   sw.DoBytes(&s_last_sector_subheader, sizeof(s_last_sector_subheader));
    710   sw.Do(&s_last_sector_header_valid);
    711   sw.DoBytes(&s_last_subq, sizeof(s_last_subq));
    712   sw.Do(&s_last_cdda_report_frame_nibble);
    713   sw.Do(&s_play_track_number_bcd);
    714   sw.Do(&s_async_command_parameter);
    715 
    716   sw.DoEx(&s_fast_forward_rate, 49, static_cast<s8>(0));
    717 
    718   sw.Do(&s_cd_audio_volume_matrix);
    719   sw.Do(&s_next_cd_audio_volume_matrix);
    720   sw.Do(&s_xa_last_samples);
    721   sw.Do(&s_xa_resample_ring_buffer);
    722   sw.Do(&s_xa_resample_p);
    723   sw.Do(&s_xa_resample_sixstep);
    724   sw.Do(&s_param_fifo);
    725   sw.Do(&s_response_fifo);
    726   sw.Do(&s_async_response_fifo);
    727 
    728   if (sw.GetVersion() < 65)
    729   {
    730     // Skip over the "copied out data", we don't care about it.
    731     u32 old_fifo_size = 0;
    732     sw.Do(&old_fifo_size);
    733     sw.SkipBytes(old_fifo_size);
    734 
    735     sw.Do(&s_current_read_sector_buffer);
    736     sw.Do(&s_current_write_sector_buffer);
    737     for (SectorBuffer& sb : s_sector_buffers)
    738     {
    739       sw.Do(&sb.data);
    740       sw.Do(&sb.size);
    741       sb.position = 0;
    742     }
    743 
    744     // Try to transplant the old "data fifo" into the current sector buffer's read position.
    745     // I doubt this is going to work well.... don't save state in the middle of loading, ya goon.
    746     if (old_fifo_size > 0)
    747     {
    748       SectorBuffer& sb = s_sector_buffers[s_current_read_sector_buffer];
    749       sb.size = s_mode.read_raw_sector ? RAW_SECTOR_OUTPUT_SIZE : DATA_SECTOR_OUTPUT_SIZE;
    750       sb.position = (sb.size > old_fifo_size) ? (sb.size - old_fifo_size) : 0;
    751       s_request_register.BFRD = (sb.position > 0);
    752     }
    753 
    754     UpdateStatusRegister();
    755   }
    756   else
    757   {
    758     sw.Do(&s_current_read_sector_buffer);
    759     sw.Do(&s_current_write_sector_buffer);
    760 
    761     for (SectorBuffer& sb : s_sector_buffers)
    762     {
    763       sw.Do(&sb.size);
    764       sw.Do(&sb.position);
    765 
    766       // We're never going to access data that has already been read out, so skip saving it.
    767       if (sb.position < sb.size)
    768       {
    769         sw.DoBytes(&sb.data[sb.position], sb.size - sb.position);
    770 
    771 #ifdef _DEBUG
    772         // Sanity test in debug builds.
    773         if (sb.position > 0)
    774           std::memset(sb.data.data(), 0, sb.position);
    775 #endif
    776       }
    777     }
    778   }
    779 
    780   sw.Do(&s_audio_fifo);
    781   sw.Do(&s_requested_lba);
    782 
    783   if (sw.IsReading())
    784   {
    785     if (s_reader.HasMedia())
    786       s_reader.QueueReadSector(s_requested_lba);
    787     UpdateCommandEvent();
    788     s_drive_event.SetState(!IsDriveIdle());
    789 
    790     // Time will get fixed up later.
    791     s_command_second_response_event.SetState(s_command_second_response != Command::None);
    792   }
    793 
    794   return !sw.HasError();
    795 }
    796 
    797 bool CDROM::HasMedia()
    798 {
    799   return s_reader.HasMedia();
    800 }
    801 
    802 const std::string& CDROM::GetMediaFileName()
    803 {
    804   return s_reader.GetMediaFileName();
    805 }
    806 
    807 const CDImage* CDROM::GetMedia()
    808 {
    809   return s_reader.GetMedia();
    810 }
    811 
    812 DiscRegion CDROM::GetDiscRegion()
    813 {
    814   return s_disc_region;
    815 }
    816 
    817 bool CDROM::IsMediaPS1Disc()
    818 {
    819   return (s_disc_region != DiscRegion::NonPS1);
    820 }
    821 
    822 bool CDROM::IsMediaAudioCD()
    823 {
    824   if (!s_reader.HasMedia())
    825     return false;
    826 
    827   // Check for an audio track as the first track.
    828   return (s_reader.GetMedia()->GetTrackMode(1) == CDImage::TrackMode::Audio);
    829 }
    830 
    831 bool CDROM::DoesMediaRegionMatchConsole()
    832 {
    833   if (!g_settings.cdrom_region_check)
    834     return true;
    835 
    836   if (s_disc_region == DiscRegion::Other)
    837     return false;
    838 
    839   return System::GetRegion() == System::GetConsoleRegionForDiscRegion(s_disc_region);
    840 }
    841 
    842 bool CDROM::IsDriveIdle()
    843 {
    844   return s_drive_state == DriveState::Idle;
    845 }
    846 
    847 bool CDROM::IsMotorOn()
    848 {
    849   return s_secondary_status.motor_on;
    850 }
    851 
    852 bool CDROM::IsSeeking()
    853 {
    854   return (s_drive_state == DriveState::SeekingLogical || s_drive_state == DriveState::SeekingPhysical ||
    855           s_drive_state == DriveState::SeekingImplicit);
    856 }
    857 
    858 bool CDROM::IsReadingOrPlaying()
    859 {
    860   return (s_drive_state == DriveState::Reading || s_drive_state == DriveState::Playing);
    861 }
    862 
    863 bool CDROM::CanReadMedia()
    864 {
    865   return (s_drive_state != DriveState::ShellOpening && s_reader.HasMedia());
    866 }
    867 
    868 void CDROM::InsertMedia(std::unique_ptr<CDImage> media, DiscRegion region)
    869 {
    870   if (CanReadMedia())
    871     RemoveMedia(true);
    872 
    873   INFO_LOG("Inserting new media, disc region: {}, console region: {}", Settings::GetDiscRegionName(region),
    874            Settings::GetConsoleRegionName(System::GetRegion()));
    875 
    876   s_disc_region = region;
    877   s_reader.SetMedia(std::move(media));
    878   SetHoldPosition(0, true);
    879 
    880   // motor automatically spins up
    881   if (s_drive_state != DriveState::ShellOpening)
    882     StartMotor();
    883 
    884   if (s_show_current_file)
    885     CreateFileMap();
    886 }
    887 
    888 std::unique_ptr<CDImage> CDROM::RemoveMedia(bool for_disc_swap)
    889 {
    890   if (!HasMedia())
    891     return nullptr;
    892 
    893   // Add an additional two seconds to the disc swap, some games don't like it happening too quickly.
    894   TickCount stop_ticks = GetTicksForStop(true);
    895   if (for_disc_swap)
    896     stop_ticks += System::ScaleTicksToOverclock(System::MASTER_CLOCK * 2);
    897 
    898   INFO_LOG("Removing CD...");
    899   std::unique_ptr<CDImage> image = s_reader.RemoveMedia();
    900 
    901   if (s_show_current_file)
    902     CreateFileMap();
    903 
    904   s_last_sector_header_valid = false;
    905 
    906   s_secondary_status.motor_on = false;
    907   s_secondary_status.shell_open = true;
    908   s_secondary_status.ClearActiveBits();
    909   s_disc_region = DiscRegion::NonPS1;
    910 
    911   // If the drive was doing anything, we need to abort the command.
    912   ClearDriveState();
    913   ClearCommandSecondResponse();
    914   s_command = Command::None;
    915   s_command_event.Deactivate();
    916 
    917   // The console sends an interrupt when the shell is opened regardless of whether a command was executing.
    918   ClearAsyncInterrupt();
    919   SendAsyncErrorResponse(STAT_ERROR, 0x08);
    920 
    921   // Begin spin-down timer, we can't swap the new disc in immediately for some games (e.g. Metal Gear Solid).
    922   if (for_disc_swap)
    923   {
    924     s_drive_state = DriveState::ShellOpening;
    925     s_drive_event.SetIntervalAndSchedule(stop_ticks);
    926   }
    927 
    928   return image;
    929 }
    930 
    931 bool CDROM::PrecacheMedia()
    932 {
    933   if (!s_reader.HasMedia())
    934     return false;
    935 
    936   if (s_reader.GetMedia()->HasSubImages() && s_reader.GetMedia()->GetSubImageCount() > 1)
    937   {
    938     Host::AddOSDMessage(
    939       fmt::format(TRANSLATE_FS("OSDMessage", "CD image preloading not available for multi-disc image '{}'"),
    940                   FileSystem::GetDisplayNameFromPath(s_reader.GetMedia()->GetFileName())),
    941       Host::OSD_ERROR_DURATION);
    942     return false;
    943   }
    944 
    945   HostInterfaceProgressCallback callback;
    946   if (!s_reader.Precache(&callback))
    947   {
    948     Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Precaching CD image failed, it may be unreliable."),
    949                         Host::OSD_ERROR_DURATION);
    950     return false;
    951   }
    952 
    953   return true;
    954 }
    955 
    956 TinyString CDROM::LBAToMSFString(CDImage::LBA lba)
    957 {
    958   const auto pos = CDImage::Position::FromLBA(lba);
    959   return TinyString::from_format("{:02d}:{:02d}:{:02d}", pos.minute, pos.second, pos.frame);
    960 }
    961 
    962 void CDROM::SetReadaheadSectors(u32 readahead_sectors)
    963 {
    964   const bool want_thread = (readahead_sectors > 0);
    965   if (want_thread == s_reader.IsUsingThread() && s_reader.GetReadaheadCount() == readahead_sectors)
    966     return;
    967 
    968   if (want_thread)
    969     s_reader.StartThread(readahead_sectors);
    970   else
    971     s_reader.StopThread();
    972 
    973   if (HasMedia())
    974     s_reader.QueueReadSector(s_requested_lba);
    975 }
    976 
    977 void CDROM::CPUClockChanged()
    978 {
    979   // reschedule the disc read event
    980   if (IsReadingOrPlaying())
    981     s_drive_event.SetInterval(GetTicksForRead());
    982 }
    983 
    984 u8 CDROM::ReadRegister(u32 offset)
    985 {
    986   switch (offset)
    987   {
    988     case 0: // status register
    989       TRACE_LOG("CDROM read status register -> 0x{:08X}", s_status.bits);
    990       return s_status.bits;
    991 
    992     case 1: // always response FIFO
    993     {
    994       if (s_response_fifo.IsEmpty())
    995       {
    996         DEV_LOG("Response FIFO empty on read");
    997         return 0x00;
    998       }
    999 
   1000       const u8 value = s_response_fifo.Pop();
   1001       UpdateStatusRegister();
   1002       DEBUG_LOG("CDROM read response FIFO -> 0x{:08X}", ZeroExtend32(value));
   1003       return value;
   1004     }
   1005 
   1006     case 2: // always data FIFO
   1007     {
   1008       SectorBuffer& sb = s_sector_buffers[s_current_read_sector_buffer];
   1009       u8 value = 0;
   1010       if (s_request_register.BFRD && sb.position < sb.size)
   1011       {
   1012         value = (sb.position < sb.size) ? sb.data[sb.position++] : 0;
   1013         CheckForSectorBufferReadComplete();
   1014       }
   1015       else
   1016       {
   1017         WARNING_LOG("Sector buffer overread (BDRD={}, buffer={}, pos={}, size={})", s_request_register.BFRD.GetValue(),
   1018                     s_current_read_sector_buffer, sb.position, sb.size);
   1019       }
   1020 
   1021       DEBUG_LOG("CDROM read data FIFO -> 0x{:02X}", value);
   1022       return value;
   1023     }
   1024 
   1025     case 3:
   1026     {
   1027       if (s_status.index & 1)
   1028       {
   1029         const u8 value = s_interrupt_flag_register | ~INTERRUPT_REGISTER_MASK;
   1030         DEBUG_LOG("CDROM read interrupt flag register -> 0x{:02X}", value);
   1031         return value;
   1032       }
   1033       else
   1034       {
   1035         const u8 value = s_interrupt_enable_register | ~INTERRUPT_REGISTER_MASK;
   1036         DEBUG_LOG("CDROM read interrupt enable register -> 0x{:02X}", value);
   1037         return value;
   1038       }
   1039     }
   1040     break;
   1041 
   1042     default:
   1043       [[unlikely]]
   1044       {
   1045         ERROR_LOG("Unknown CDROM register read: offset=0x{:02X}, index={}", offset,
   1046                   ZeroExtend32(s_status.index.GetValue()));
   1047         Panic("Unknown CDROM register");
   1048       }
   1049   }
   1050 }
   1051 
   1052 void CDROM::WriteRegister(u32 offset, u8 value)
   1053 {
   1054   if (offset == 0)
   1055   {
   1056     TRACE_LOG("CDROM status register <- 0x{:02X}", value);
   1057     s_status.bits = (s_status.bits & static_cast<u8>(~3)) | (value & u8(3));
   1058     return;
   1059   }
   1060 
   1061   const u32 reg = (s_status.index * 3u) + (offset - 1);
   1062   switch (reg)
   1063   {
   1064     case 0:
   1065     {
   1066       DEBUG_LOG("CDROM command register <- 0x{:02X} ({})", value, s_command_info[value].name);
   1067       BeginCommand(static_cast<Command>(value));
   1068       return;
   1069     }
   1070 
   1071     case 1:
   1072     {
   1073       if (s_param_fifo.IsFull())
   1074       {
   1075         WARNING_LOG("Parameter FIFO overflow");
   1076         s_param_fifo.RemoveOne();
   1077       }
   1078 
   1079       s_param_fifo.Push(value);
   1080       UpdateStatusRegister();
   1081       return;
   1082     }
   1083 
   1084     case 2:
   1085     {
   1086       DEBUG_LOG("Request register <- 0x{:02X}", value);
   1087       const RequestRegister rr{value};
   1088 
   1089       // Sound map is not currently implemented, haven't found anything which uses it.
   1090       if (rr.SMEN)
   1091         ERROR_LOG("Sound map enable set");
   1092       if (rr.BFWR)
   1093         ERROR_LOG("Buffer write enable set");
   1094 
   1095       s_request_register.bits = rr.bits;
   1096 
   1097       SectorBuffer& sb = s_sector_buffers[s_current_read_sector_buffer];
   1098       DEBUG_LOG("{} BFRD buffer={} pos={} size={}", s_request_register.BFRD ? "Set" : "Clear",
   1099                 s_current_read_sector_buffer, sb.position, sb.size);
   1100 
   1101       if (!s_request_register.BFRD)
   1102       {
   1103         // Clearing BFRD needs to reset the position of the current buffer.
   1104         // Metal Gear Solid: Special Missions (PAL) clears BFRD inbetween two DMAs during its disc detection, and needs
   1105         // the buffer to reset. But during the actual game, it doesn't clear, and needs the pointer to increment.
   1106         sb.position = 0;
   1107       }
   1108       else
   1109       {
   1110         if (sb.size == 0)
   1111           WARNING_LOG("Setting BFRD without a buffer ready.");
   1112       }
   1113 
   1114       UpdateStatusRegister();
   1115       return;
   1116     }
   1117 
   1118     case 3:
   1119     {
   1120       ERROR_LOG("Sound map data out <- 0x{:02X}", value);
   1121       return;
   1122     }
   1123 
   1124     case 4:
   1125     {
   1126       DEBUG_LOG("Interrupt enable register <- 0x{:02X}", value);
   1127       s_interrupt_enable_register = value & INTERRUPT_REGISTER_MASK;
   1128       UpdateInterruptRequest();
   1129       return;
   1130     }
   1131 
   1132     case 5:
   1133     {
   1134       DEBUG_LOG("Interrupt flag register <- 0x{:02X}", value);
   1135 
   1136       const u8 prev_interrupt_flag_register = s_interrupt_flag_register;
   1137       s_interrupt_flag_register &= ~(value & INTERRUPT_REGISTER_MASK);
   1138       if (s_interrupt_flag_register == 0)
   1139       {
   1140         // Start the countdown from when the interrupt was cleared, not it being triggered.
   1141         // Otherwise Ogre Battle, Crime Crackers, Lego Racers, etc have issues.
   1142         if (prev_interrupt_flag_register != 0)
   1143           s_last_interrupt_time = System::GetGlobalTickCounter();
   1144 
   1145         InterruptController::SetLineState(InterruptController::IRQ::CDROM, false);
   1146         if (HasPendingAsyncInterrupt() && !HasPendingCommand())
   1147           QueueDeliverAsyncInterrupt();
   1148         else
   1149           UpdateCommandEvent();
   1150       }
   1151 
   1152       // Bit 6 clears the parameter FIFO.
   1153       if (value & 0x40)
   1154       {
   1155         s_param_fifo.Clear();
   1156         UpdateStatusRegister();
   1157       }
   1158 
   1159       return;
   1160     }
   1161 
   1162     case 6:
   1163     {
   1164       ERROR_LOG("Sound map coding info <- 0x{:02X}", value);
   1165       return;
   1166     }
   1167 
   1168     case 7:
   1169     {
   1170       DEBUG_LOG("Audio volume for left-to-left output <- 0x{:02X}", value);
   1171       s_next_cd_audio_volume_matrix[0][0] = value;
   1172       return;
   1173     }
   1174 
   1175     case 8:
   1176     {
   1177       DEBUG_LOG("Audio volume for left-to-right output <- 0x{:02X}", value);
   1178       s_next_cd_audio_volume_matrix[0][1] = value;
   1179       return;
   1180     }
   1181 
   1182     case 9:
   1183     {
   1184       DEBUG_LOG("Audio volume for right-to-right output <- 0x{:02X}", value);
   1185       s_next_cd_audio_volume_matrix[1][1] = value;
   1186       return;
   1187     }
   1188 
   1189     case 10:
   1190     {
   1191       DEBUG_LOG("Audio volume for right-to-left output <- 0x{:02X}", value);
   1192       s_next_cd_audio_volume_matrix[1][0] = value;
   1193       return;
   1194     }
   1195 
   1196     case 11:
   1197     {
   1198       DEBUG_LOG("Audio volume apply changes <- 0x{:02X}", value);
   1199 
   1200       const bool adpcm_muted = ConvertToBoolUnchecked(value & u8(0x01));
   1201       if (adpcm_muted != s_adpcm_muted ||
   1202           (value & 0x20 && std::memcmp(s_cd_audio_volume_matrix.data(), s_next_cd_audio_volume_matrix.data(),
   1203                                        sizeof(s_cd_audio_volume_matrix)) != 0))
   1204       {
   1205         if (HasPendingDiscEvent())
   1206           s_drive_event.InvokeEarly();
   1207         SPU::GeneratePendingSamples();
   1208       }
   1209 
   1210       s_adpcm_muted = adpcm_muted;
   1211       if (value & 0x20)
   1212         s_cd_audio_volume_matrix = s_next_cd_audio_volume_matrix;
   1213       return;
   1214     }
   1215 
   1216     default:
   1217       [[unlikely]]
   1218       {
   1219         ERROR_LOG("Unknown CDROM register write: offset=0x{:02X}, index={}, reg={}, value=0x{:02X}", offset,
   1220                   s_status.index.GetValue(), reg, value);
   1221         return;
   1222       }
   1223   }
   1224 }
   1225 
   1226 void CDROM::DMARead(u32* words, u32 word_count)
   1227 {
   1228   SectorBuffer& sb = s_sector_buffers[s_current_read_sector_buffer];
   1229   const u32 bytes_available = (s_request_register.BFRD && sb.position < sb.size) ? (sb.size - sb.position) : 0;
   1230   u8* dst_ptr = reinterpret_cast<u8*>(words);
   1231   u32 bytes_remaining = word_count * sizeof(u32);
   1232   if (bytes_available > 0)
   1233   {
   1234     const u32 transfer_size = std::min(bytes_available, bytes_remaining);
   1235     std::memcpy(dst_ptr, &sb.data[sb.position], transfer_size);
   1236     sb.position += transfer_size;
   1237     dst_ptr += transfer_size;
   1238     bytes_remaining -= transfer_size;
   1239   }
   1240 
   1241   if (bytes_remaining > 0)
   1242   {
   1243     ERROR_LOG("Sector buffer overread by {} bytes", bytes_remaining);
   1244     std::memset(dst_ptr, 0, bytes_remaining);
   1245   }
   1246 
   1247   CheckForSectorBufferReadComplete();
   1248 }
   1249 
   1250 bool CDROM::HasPendingCommand()
   1251 {
   1252   return s_command != Command::None;
   1253 }
   1254 
   1255 bool CDROM::HasPendingInterrupt()
   1256 {
   1257   return s_interrupt_flag_register != 0;
   1258 }
   1259 
   1260 bool CDROM::HasPendingAsyncInterrupt()
   1261 {
   1262   return s_pending_async_interrupt != 0;
   1263 }
   1264 
   1265 void CDROM::SetInterrupt(Interrupt interrupt)
   1266 {
   1267   s_interrupt_flag_register = static_cast<u8>(interrupt);
   1268   UpdateInterruptRequest();
   1269 }
   1270 
   1271 void CDROM::SetAsyncInterrupt(Interrupt interrupt)
   1272 {
   1273   if (s_interrupt_flag_register == static_cast<u8>(interrupt))
   1274   {
   1275     DEV_LOG("Not setting async interrupt {} because there is already one unacknowledged", static_cast<u8>(interrupt));
   1276     s_async_response_fifo.Clear();
   1277     return;
   1278   }
   1279 
   1280   Assert(s_pending_async_interrupt == 0);
   1281   s_pending_async_interrupt = static_cast<u8>(interrupt);
   1282   if (!HasPendingInterrupt())
   1283   {
   1284     // Pending interrupt should block INT1 from going through. But pending command needs to as well, for games like
   1285     // Gokujou Parodius Da! Deluxe Pack that spam GetlocL while data is being played back, if they get an INT1 instead
   1286     // of an INT3 during the small window of time that the INT3 is delayed, causes a lock-up.
   1287     if (!HasPendingCommand())
   1288       QueueDeliverAsyncInterrupt();
   1289     else
   1290       DEBUG_LOG("Delaying async interrupt {} because of pending command", s_pending_async_interrupt);
   1291   }
   1292   else
   1293   {
   1294     DEBUG_LOG("Delaying async interrupt {} because of pending interrupt {}", s_pending_async_interrupt,
   1295               s_interrupt_flag_register);
   1296   }
   1297 }
   1298 
   1299 void CDROM::ClearAsyncInterrupt()
   1300 {
   1301   s_pending_async_interrupt = 0;
   1302   s_async_interrupt_event.Deactivate();
   1303   s_async_response_fifo.Clear();
   1304 }
   1305 
   1306 void CDROM::QueueDeliverAsyncInterrupt()
   1307 {
   1308   // Why do we need this mess? A few games, such as Ogre Battle, like to spam GetlocL or GetlocP while
   1309   // XA playback is going. The problem is, when that happens and an INT1 also comes in. Instead of
   1310   // reading the interrupt flag, reading the FIFO, and then clearing the interrupt, they clear the
   1311   // interrupt, then read the FIFO. If an INT1 comes in during that time, it'll read the INT1 response
   1312   // instead of the INT3 response, and the game gets confused. So, we just delay INT1s a bit, if there
   1313   // has been any recent INT3s - give it enough time to read the response out. The real console does
   1314   // something similar anyway, the INT1 task won't run immediately after the INT3 is cleared.
   1315   DebugAssert(HasPendingAsyncInterrupt());
   1316 
   1317   const u32 diff = static_cast<u32>(System::GetGlobalTickCounter() - s_last_interrupt_time);
   1318   if (diff >= MINIMUM_INTERRUPT_DELAY)
   1319   {
   1320     DeliverAsyncInterrupt(nullptr, 0, 0);
   1321   }
   1322   else
   1323   {
   1324     DEV_LOG("Delaying async interrupt {} because it's been {} cycles since last interrupt", s_pending_async_interrupt,
   1325             diff);
   1326     s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
   1327   }
   1328 }
   1329 
   1330 void CDROM::DeliverAsyncInterrupt(void*, TickCount ticks, TickCount ticks_late)
   1331 {
   1332   if (HasPendingInterrupt())
   1333   {
   1334     // This shouldn't really happen, because we should block command execution.. but just in case.
   1335     if (!s_async_interrupt_event.IsActive())
   1336       s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
   1337   }
   1338   else
   1339   {
   1340     s_async_interrupt_event.Deactivate();
   1341 
   1342     Assert(s_pending_async_interrupt != 0 && !HasPendingInterrupt());
   1343     DEBUG_LOG("Delivering async interrupt {}", s_pending_async_interrupt);
   1344 
   1345     // This is the HC05 setting the read position from the decoder.
   1346     if (s_pending_async_interrupt == static_cast<u8>(Interrupt::DataReady))
   1347       s_current_read_sector_buffer = s_current_write_sector_buffer;
   1348 
   1349     s_response_fifo.Clear();
   1350     s_response_fifo.PushFromQueue(&s_async_response_fifo);
   1351     s_interrupt_flag_register = s_pending_async_interrupt;
   1352     s_pending_async_interrupt = 0;
   1353     UpdateInterruptRequest();
   1354     UpdateStatusRegister();
   1355     UpdateCommandEvent();
   1356   }
   1357 }
   1358 
   1359 void CDROM::SendACKAndStat()
   1360 {
   1361   s_response_fifo.Push(s_secondary_status.bits);
   1362   SetInterrupt(Interrupt::ACK);
   1363 }
   1364 
   1365 void CDROM::SendErrorResponse(u8 stat_bits /* = STAT_ERROR */, u8 reason /* = 0x80 */)
   1366 {
   1367   s_response_fifo.Push(s_secondary_status.bits | stat_bits);
   1368   s_response_fifo.Push(reason);
   1369   SetInterrupt(Interrupt::Error);
   1370 }
   1371 
   1372 void CDROM::SendAsyncErrorResponse(u8 stat_bits /* = STAT_ERROR */, u8 reason /* = 0x80 */)
   1373 {
   1374   s_async_response_fifo.Push(s_secondary_status.bits | stat_bits);
   1375   s_async_response_fifo.Push(reason);
   1376   SetAsyncInterrupt(Interrupt::Error);
   1377 }
   1378 
   1379 void CDROM::UpdateStatusRegister()
   1380 {
   1381   s_status.ADPBUSY = false;
   1382   s_status.PRMEMPTY = s_param_fifo.IsEmpty();
   1383   s_status.PRMWRDY = !s_param_fifo.IsFull();
   1384   s_status.RSLRRDY = !s_response_fifo.IsEmpty();
   1385   s_status.DRQSTS = s_request_register.BFRD;
   1386   s_status.BUSYSTS = HasPendingCommand();
   1387 
   1388   DMA::SetRequest(DMA::Channel::CDROM, s_status.DRQSTS);
   1389 }
   1390 
   1391 void CDROM::UpdateInterruptRequest()
   1392 {
   1393   InterruptController::SetLineState(InterruptController::IRQ::CDROM,
   1394                                     (s_interrupt_flag_register & s_interrupt_enable_register) != 0);
   1395 }
   1396 
   1397 bool CDROM::HasPendingDiscEvent()
   1398 {
   1399   return (s_drive_event.IsActive() && s_drive_event.GetTicksUntilNextExecution() <= 0);
   1400 }
   1401 
   1402 TickCount CDROM::GetAckDelayForCommand(Command command)
   1403 {
   1404   if (command == Command::Init)
   1405   {
   1406     // Init takes longer.
   1407     return 80000;
   1408   }
   1409 
   1410   // Tests show that the average time to acknowledge a command is significantly higher when a disc is in the drive,
   1411   // presumably because the controller is busy doing discy-things.
   1412   constexpr u32 default_ack_delay_no_disc = 15000;
   1413   constexpr u32 default_ack_delay_with_disc = 25000;
   1414   return CanReadMedia() ? default_ack_delay_with_disc : default_ack_delay_no_disc;
   1415 }
   1416 
   1417 TickCount CDROM::GetTicksForSpinUp()
   1418 {
   1419   // 1 second
   1420   return System::GetTicksPerSecond();
   1421 }
   1422 
   1423 TickCount CDROM::GetTicksForIDRead()
   1424 {
   1425   TickCount ticks = ID_READ_TICKS;
   1426   if (s_drive_state == DriveState::SpinningUp)
   1427     ticks += s_drive_event.GetTicksUntilNextExecution();
   1428 
   1429   return ticks;
   1430 }
   1431 
   1432 TickCount CDROM::GetTicksForRead()
   1433 {
   1434   const TickCount tps = System::GetTicksPerSecond();
   1435 
   1436   if (g_settings.cdrom_read_speedup > 1 && !s_mode.cdda && !s_mode.xa_enable && s_mode.double_speed)
   1437     return tps / (150 * g_settings.cdrom_read_speedup);
   1438 
   1439   return s_mode.double_speed ? (tps / 150) : (tps / 75);
   1440 }
   1441 
   1442 TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
   1443 {
   1444   static constexpr TickCount MIN_TICKS = 30000;
   1445 
   1446   if (g_settings.cdrom_seek_speedup == 0)
   1447     return MIN_TICKS;
   1448 
   1449   u32 ticks = 0;
   1450 
   1451   // Update start position for seek.
   1452   if (IsSeeking())
   1453     UpdatePositionWhileSeeking();
   1454   else
   1455     UpdatePhysicalPosition(false);
   1456 
   1457   const CDImage::LBA current_lba = IsMotorOn() ? (IsSeeking() ? s_seek_end_lba : s_physical_lba) : 0;
   1458   const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
   1459 
   1460   // Motor spin-up time.
   1461   if (!IsMotorOn())
   1462   {
   1463     ticks +=
   1464       (s_drive_state == DriveState::SpinningUp) ? s_drive_event.GetTicksUntilNextExecution() : GetTicksForSpinUp();
   1465     if (s_drive_state == DriveState::ShellOpening || s_drive_state == DriveState::SpinningUp)
   1466       ClearDriveState();
   1467   }
   1468 
   1469   float seconds;
   1470   if (current_lba < new_lba && lba_diff < 10)
   1471   {
   1472     // If we're behind the current sector, and within a small distance, the mech just waits for the sector to come up by
   1473     // reading normally (or apparently moves the lens according to some?). This timing is actually needed for
   1474     // Transformers - Beast Wars Transmetals, it gets very unstable during loading if seeks are too fast.
   1475     const u32 ticks_per_sector =
   1476       s_mode.double_speed ? static_cast<u32>(System::MASTER_CLOCK / 150) : static_cast<u32>(System::MASTER_CLOCK / 75);
   1477     ticks += ticks_per_sector * std::min<u32>(5u, lba_diff);
   1478     seconds = 0.0f;
   1479   }
   1480   else if (lba_diff < 7200)
   1481   {
   1482     // Not sled. The point at which we switch from faster to slower seeks varies across the disc. Around ~60 distance
   1483     // towards the end, but ~330 at the beginning. Likely based on sectors per track, so we use a logarithmic curve.
   1484     const u32 switch_point = static_cast<u32>(
   1485       330.0f +
   1486       (-63.1333f * std::log(std::clamp(static_cast<float>(current_lba) / static_cast<float>(CDImage::FRAMES_PER_MINUTE),
   1487                                        1.0f, 72.0f))));
   1488     seconds = (lba_diff < switch_point) ? 0.05f : 0.1f;
   1489   }
   1490   else
   1491   {
   1492     // Sled seek. Minimum of approx. 200ms, up to 900ms or so. Mapped to a linear and logarithmic component, because
   1493     // there is a fixed cost which ramps up quickly, but the very slow sled seeks are only when doing a full disc sweep.
   1494     constexpr float SLED_FIXED_COST = 0.05f;
   1495     constexpr float SLED_VARIABLE_COST = 0.9f - SLED_FIXED_COST;
   1496     constexpr float LOG_WEIGHT = 0.4f;
   1497     constexpr float MAX_SLED_LBA = static_cast<float>(72 * CDImage::FRAMES_PER_MINUTE);
   1498     seconds =
   1499       SLED_FIXED_COST +
   1500       (((SLED_VARIABLE_COST * (std::log(static_cast<float>(lba_diff)) / std::log(MAX_SLED_LBA)))) * LOG_WEIGHT) +
   1501       ((SLED_VARIABLE_COST * (lba_diff / MAX_SLED_LBA)) * (1.0f - LOG_WEIGHT));
   1502   }
   1503 
   1504   constexpr u32 ticks_per_second = static_cast<u32>(System::MASTER_CLOCK);
   1505   ticks += static_cast<u32>(seconds * static_cast<float>(ticks_per_second));
   1506 
   1507   if (s_drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change)
   1508   {
   1509     // we're still reading the TOC, so add that time in
   1510     const TickCount remaining_change_ticks = s_drive_event.GetTicksUntilNextExecution();
   1511     ticks += remaining_change_ticks;
   1512 
   1513     DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms) ({} for speed change/implicit TOC read)", lba_diff, ticks,
   1514             (static_cast<float>(ticks) / static_cast<float>(ticks_per_second)) * 1000.0f, remaining_change_ticks);
   1515   }
   1516   else
   1517   {
   1518     DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms)", lba_diff, ticks,
   1519             (static_cast<float>(ticks) / static_cast<float>(ticks_per_second)) * 1000.0f);
   1520   }
   1521 
   1522   if (g_settings.cdrom_seek_speedup > 1)
   1523     ticks = std::max<u32>(ticks / g_settings.cdrom_seek_speedup, MIN_TICKS);
   1524 
   1525   return System::ScaleTicksToOverclock(static_cast<TickCount>(ticks));
   1526 }
   1527 
   1528 TickCount CDROM::GetTicksForStop(bool motor_was_on)
   1529 {
   1530   return System::ScaleTicksToOverclock(motor_was_on ? (s_mode.double_speed ? 25000000 : 13000000) : 7000);
   1531 }
   1532 
   1533 TickCount CDROM::GetTicksForSpeedChange()
   1534 {
   1535   static constexpr u32 ticks_single_to_double = static_cast<u32>(0.6 * static_cast<double>(System::MASTER_CLOCK));
   1536   static constexpr u32 ticks_double_to_single = static_cast<u32>(0.7 * static_cast<double>(System::MASTER_CLOCK));
   1537   return System::ScaleTicksToOverclock(s_mode.double_speed ? ticks_single_to_double : ticks_double_to_single);
   1538 }
   1539 
   1540 TickCount CDROM::GetTicksForTOCRead()
   1541 {
   1542   if (!HasMedia())
   1543     return 0;
   1544 
   1545   return System::GetTicksPerSecond() / 2u;
   1546 }
   1547 
   1548 CDImage::LBA CDROM::GetNextSectorToBeRead()
   1549 {
   1550   if (!IsReadingOrPlaying())
   1551     return s_current_lba;
   1552 
   1553   s_reader.WaitForReadToComplete();
   1554   return s_reader.GetLastReadSector();
   1555 }
   1556 
   1557 void CDROM::BeginCommand(Command command)
   1558 {
   1559   TickCount ack_delay = GetAckDelayForCommand(command);
   1560 
   1561   if (HasPendingCommand())
   1562   {
   1563     // The behavior here is kinda.. interesting. Some commands seem to take precedence over others, for example
   1564     // sending a Nop command followed by a GetlocP will return the GetlocP response, and the same for the inverse.
   1565     // However, other combinations result in strange behavior, for example sending a Setloc followed by a ReadN will
   1566     // fail with ERROR_REASON_INCORRECT_NUMBER_OF_PARAMETERS. This particular example happens in Voice Idol
   1567     // Collection - Pool Bar Story, and the loading time is lengthened as well as audio slowing down if this
   1568     // behavior is not correct. So, let's use a heuristic; if the number of parameters of the "old" command is
   1569     // greater than the "new" command, empty the FIFO, which will return the error when the command executes.
   1570     // Otherwise, override the command with the new one.
   1571     if (s_command_info[static_cast<u8>(s_command)].min_parameters >
   1572         s_command_info[static_cast<u8>(command)].min_parameters)
   1573     {
   1574       WARNING_LOG("Ignoring command 0x{:02X} ({}) and emptying FIFO as 0x{:02X} ({}) is still pending",
   1575                   static_cast<u8>(command), s_command_info[static_cast<u8>(command)].name, static_cast<u8>(s_command),
   1576                   s_command_info[static_cast<u8>(s_command)].name);
   1577       s_param_fifo.Clear();
   1578       return;
   1579     }
   1580 
   1581     WARNING_LOG("Cancelling pending command 0x{:02X} ({}) for new command 0x{:02X} ({})", static_cast<u8>(s_command),
   1582                 s_command_info[static_cast<u8>(s_command)].name, static_cast<u8>(command),
   1583                 s_command_info[static_cast<u8>(command)].name);
   1584 
   1585     // subtract the currently-elapsed ack ticks from the new command
   1586     if (s_command_event.IsActive())
   1587     {
   1588       const TickCount elapsed_ticks = s_command_event.GetInterval() - s_command_event.GetTicksUntilNextExecution();
   1589       ack_delay = std::max(ack_delay - elapsed_ticks, 1);
   1590       s_command_event.Deactivate();
   1591 
   1592       // If there's a pending async interrupt, we need to deliver it now, since we've deactivated the command that was
   1593       // blocking it from being delivered. Not doing so will cause lockups in Street Fighter Alpha 3, where it spams
   1594       // multiple pause commands while an INT1 is scheduled, and there isn't much that can stop an INT1 once it's been
   1595       // queued on real hardware.
   1596       if (HasPendingAsyncInterrupt())
   1597       {
   1598         WARNING_LOG("Delivering pending interrupt after command {} cancellation for {}.",
   1599                     s_command_info[static_cast<u8>(s_command)].name, s_command_info[static_cast<u8>(command)].name);
   1600         QueueDeliverAsyncInterrupt();
   1601       }
   1602     }
   1603   }
   1604 
   1605   s_command = command;
   1606   s_command_event.SetIntervalAndSchedule(ack_delay);
   1607   UpdateCommandEvent();
   1608   UpdateStatusRegister();
   1609 }
   1610 
   1611 void CDROM::EndCommand()
   1612 {
   1613   s_param_fifo.Clear();
   1614 
   1615   s_command = Command::None;
   1616   s_command_event.Deactivate();
   1617   UpdateStatusRegister();
   1618 }
   1619 
   1620 void CDROM::ExecuteCommand(void*, TickCount ticks, TickCount ticks_late)
   1621 {
   1622   const CommandInfo& ci = s_command_info[static_cast<u8>(s_command)];
   1623   if (Log::IsLogVisible(LOGLEVEL_DEV, ___LogChannel___)) [[unlikely]]
   1624   {
   1625     SmallString params;
   1626     for (u32 i = 0; i < s_param_fifo.GetSize(); i++)
   1627       params.append_format("{}0x{:02X}", (i == 0) ? "" : ", ", s_param_fifo.Peek(i));
   1628     DEV_LOG("CDROM executing command 0x{:02X} ({}), stat = 0x{:02X}, params = [{}]", static_cast<u8>(s_command),
   1629             ci.name, s_secondary_status.bits, params);
   1630   }
   1631 
   1632   if (s_param_fifo.GetSize() < ci.min_parameters || s_param_fifo.GetSize() > ci.max_parameters) [[unlikely]]
   1633   {
   1634     WARNING_LOG("Incorrect parameters for command 0x{:02X} ({}), expecting {}-{} got {}", static_cast<u8>(s_command),
   1635                 ci.name, ci.min_parameters, ci.max_parameters, s_param_fifo.GetSize());
   1636     SendErrorResponse(STAT_ERROR, ERROR_REASON_INCORRECT_NUMBER_OF_PARAMETERS);
   1637     EndCommand();
   1638     return;
   1639   }
   1640 
   1641   if (!s_response_fifo.IsEmpty())
   1642   {
   1643     DEBUG_LOG("Response FIFO not empty on command begin");
   1644     s_response_fifo.Clear();
   1645   }
   1646 
   1647   // Stop command event first, reduces our chances of ending up with out-of-order events.
   1648   s_command_event.Deactivate();
   1649 
   1650   switch (s_command)
   1651   {
   1652     case Command::Getstat:
   1653     {
   1654       DEBUG_LOG("CDROM Getstat command");
   1655 
   1656       // if bit 0 or 2 is set, send an additional byte
   1657       SendACKAndStat();
   1658 
   1659       // shell open bit is cleared after sending the status
   1660       if (CanReadMedia())
   1661         s_secondary_status.shell_open = false;
   1662 
   1663       EndCommand();
   1664       return;
   1665     }
   1666 
   1667     case Command::Test:
   1668     {
   1669       const u8 subcommand = s_param_fifo.Pop();
   1670       ExecuteTestCommand(subcommand);
   1671       return;
   1672     }
   1673 
   1674     case Command::GetID:
   1675     {
   1676       DEBUG_LOG("CDROM GetID command");
   1677       ClearCommandSecondResponse();
   1678 
   1679       if (!CanReadMedia())
   1680       {
   1681         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1682       }
   1683       else
   1684       {
   1685         SendACKAndStat();
   1686         QueueCommandSecondResponse(Command::GetID, GetTicksForIDRead());
   1687       }
   1688 
   1689       EndCommand();
   1690       return;
   1691     }
   1692 
   1693     case Command::ReadTOC:
   1694     {
   1695       DEBUG_LOG("CDROM ReadTOC command");
   1696       ClearCommandSecondResponse();
   1697 
   1698       if (!CanReadMedia())
   1699       {
   1700         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1701       }
   1702       else
   1703       {
   1704         SendACKAndStat();
   1705         SetHoldPosition(0, true);
   1706         QueueCommandSecondResponse(Command::ReadTOC, GetTicksForTOCRead());
   1707       }
   1708 
   1709       EndCommand();
   1710       return;
   1711     }
   1712 
   1713     case Command::Setfilter:
   1714     {
   1715       const u8 file = s_param_fifo.Peek(0);
   1716       const u8 channel = s_param_fifo.Peek(1);
   1717       DEBUG_LOG("CDROM setfilter command 0x{:02X} 0x{:02X}", ZeroExtend32(file), ZeroExtend32(channel));
   1718       s_xa_filter_file_number = file;
   1719       s_xa_filter_channel_number = channel;
   1720       s_xa_current_set = false;
   1721       SendACKAndStat();
   1722       EndCommand();
   1723       return;
   1724     }
   1725 
   1726     case Command::Setmode:
   1727     {
   1728       const u8 mode = s_param_fifo.Peek(0);
   1729       const bool speed_change = (mode & 0x80) != (s_mode.bits & 0x80);
   1730       DEV_LOG("CDROM setmode command 0x{:02X}", ZeroExtend32(mode));
   1731 
   1732       s_mode.bits = mode;
   1733       SendACKAndStat();
   1734       EndCommand();
   1735 
   1736       if (speed_change)
   1737       {
   1738         if (s_drive_state == DriveState::ChangingSpeedOrTOCRead)
   1739         {
   1740           // cancel the speed change if it's less than a quarter complete
   1741           if (s_drive_event.GetTicksUntilNextExecution() >= (GetTicksForSpeedChange() / 4))
   1742           {
   1743             DEV_LOG("Cancelling speed change event");
   1744             ClearDriveState();
   1745           }
   1746         }
   1747         else if (s_drive_state != DriveState::SeekingImplicit && s_drive_state != DriveState::ShellOpening)
   1748         {
   1749           // if we're seeking or reading, we need to add time to the current seek/read
   1750           const TickCount change_ticks = GetTicksForSpeedChange();
   1751           if (s_drive_state != DriveState::Idle)
   1752           {
   1753             DEV_LOG("Drive is {}, delaying event by {} ticks for speed change to {}-speed",
   1754                     s_drive_state_names[static_cast<u8>(s_drive_state)], change_ticks,
   1755                     s_mode.double_speed ? "double" : "single");
   1756             s_drive_event.Delay(change_ticks);
   1757 
   1758             if (IsReadingOrPlaying())
   1759             {
   1760               WARNING_LOG("Speed change while reading/playing, reads will be temporarily delayed.");
   1761               s_drive_event.SetInterval(GetTicksForRead());
   1762             }
   1763           }
   1764           else
   1765           {
   1766             DEV_LOG("Drive is idle, speed change takes {} ticks", change_ticks);
   1767             s_drive_state = DriveState::ChangingSpeedOrTOCRead;
   1768             s_drive_event.Schedule(change_ticks);
   1769           }
   1770         }
   1771       }
   1772 
   1773       return;
   1774     }
   1775 
   1776     case Command::Setloc:
   1777     {
   1778       const u8 mm = s_param_fifo.Peek(0);
   1779       const u8 ss = s_param_fifo.Peek(1);
   1780       const u8 ff = s_param_fifo.Peek(2);
   1781       DEV_LOG("CDROM setloc command ({:02X}, {:02X}, {:02X})", mm, ss, ff);
   1782 
   1783       // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
   1784       if (((mm & 0x0F) > 0x09) || (mm > 0x99) || ((ss & 0x0F) > 0x09) || (ss >= 0x60) || ((ff & 0x0F) > 0x09) ||
   1785           (ff >= 0x75))
   1786       {
   1787         ERROR_LOG("Invalid/out of range seek to {:02X}:{:02X}:{:02X}", mm, ss, ff);
   1788         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_ARGUMENT);
   1789       }
   1790       else
   1791       {
   1792         SendACKAndStat();
   1793 
   1794         s_setloc_position.minute = PackedBCDToBinary(mm);
   1795         s_setloc_position.second = PackedBCDToBinary(ss);
   1796         s_setloc_position.frame = PackedBCDToBinary(ff);
   1797         s_setloc_pending = true;
   1798       }
   1799 
   1800       EndCommand();
   1801       return;
   1802     }
   1803 
   1804     case Command::SeekL:
   1805     case Command::SeekP:
   1806     {
   1807       const bool logical = (s_command == Command::SeekL);
   1808       DEBUG_LOG("CDROM {} command", logical ? "SeekL" : "SeekP");
   1809 
   1810       if (!CanReadMedia())
   1811       {
   1812         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1813       }
   1814       else
   1815       {
   1816         SendACKAndStat();
   1817         BeginSeeking(logical, false, false);
   1818       }
   1819 
   1820       EndCommand();
   1821       return;
   1822     }
   1823 
   1824     case Command::ReadT:
   1825     {
   1826       const u8 session = s_param_fifo.Peek(0);
   1827       DEBUG_LOG("CDROM ReadT command, session={}", session);
   1828 
   1829       if (!CanReadMedia() || s_drive_state == DriveState::Reading || s_drive_state == DriveState::Playing)
   1830       {
   1831         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1832       }
   1833       else if (session == 0)
   1834       {
   1835         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_ARGUMENT);
   1836       }
   1837       else
   1838       {
   1839         ClearCommandSecondResponse();
   1840         SendACKAndStat();
   1841 
   1842         s_async_command_parameter = session;
   1843         s_drive_state = DriveState::ChangingSession;
   1844         s_drive_event.Schedule(GetTicksForTOCRead());
   1845       }
   1846 
   1847       EndCommand();
   1848       return;
   1849     }
   1850 
   1851     case Command::ReadN:
   1852     case Command::ReadS:
   1853     {
   1854       DEBUG_LOG("CDROM read command");
   1855       if (!CanReadMedia())
   1856       {
   1857         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1858       }
   1859       else if ((!IsMediaPS1Disc() || !DoesMediaRegionMatchConsole()) && !s_mode.cdda)
   1860       {
   1861         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_COMMAND);
   1862       }
   1863       else
   1864       {
   1865         SendACKAndStat();
   1866 
   1867         if ((!s_setloc_pending || s_setloc_position.ToLBA() == GetNextSectorToBeRead()) &&
   1868             (s_drive_state == DriveState::Reading || (IsSeeking() && s_read_after_seek)))
   1869         {
   1870           DEV_LOG("Ignoring read command with {} setloc, already reading/reading after seek",
   1871                   s_setloc_pending ? "pending" : "same");
   1872           s_setloc_pending = false;
   1873         }
   1874         else
   1875         {
   1876           BeginReading();
   1877         }
   1878       }
   1879 
   1880       EndCommand();
   1881       return;
   1882     }
   1883 
   1884     case Command::Play:
   1885     {
   1886       const u8 track = s_param_fifo.IsEmpty() ? 0 : PackedBCDToBinary(s_param_fifo.Peek(0));
   1887       DEBUG_LOG("CDROM play command, track={}", track);
   1888 
   1889       if (!CanReadMedia())
   1890       {
   1891         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1892       }
   1893       else
   1894       {
   1895         SendACKAndStat();
   1896 
   1897         if (track == 0 && (!s_setloc_pending || s_setloc_position.ToLBA() == GetNextSectorToBeRead()) &&
   1898             (s_drive_state == DriveState::Playing || (IsSeeking() && s_play_after_seek)))
   1899         {
   1900           DEV_LOG("Ignoring play command with no/same setloc, already playing/playing after seek");
   1901           s_fast_forward_rate = 0;
   1902           s_setloc_pending = false;
   1903         }
   1904         else
   1905         {
   1906           BeginPlaying(track);
   1907         }
   1908       }
   1909 
   1910       EndCommand();
   1911       return;
   1912     }
   1913 
   1914     case Command::Forward:
   1915     {
   1916       if (s_drive_state != DriveState::Playing || !CanReadMedia())
   1917       {
   1918         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1919       }
   1920       else
   1921       {
   1922         SendACKAndStat();
   1923 
   1924         if (s_fast_forward_rate < 0)
   1925           s_fast_forward_rate = 0;
   1926 
   1927         s_fast_forward_rate += static_cast<s8>(FAST_FORWARD_RATE_STEP);
   1928         s_fast_forward_rate = std::min<s8>(s_fast_forward_rate, static_cast<s8>(MAX_FAST_FORWARD_RATE));
   1929       }
   1930 
   1931       EndCommand();
   1932       return;
   1933     }
   1934 
   1935     case Command::Backward:
   1936     {
   1937       if (s_drive_state != DriveState::Playing || !CanReadMedia())
   1938       {
   1939         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1940       }
   1941       else
   1942       {
   1943         SendACKAndStat();
   1944 
   1945         if (s_fast_forward_rate > 0)
   1946           s_fast_forward_rate = 0;
   1947 
   1948         s_fast_forward_rate -= static_cast<s8>(FAST_FORWARD_RATE_STEP);
   1949         s_fast_forward_rate = std::max<s8>(s_fast_forward_rate, -static_cast<s8>(MAX_FAST_FORWARD_RATE));
   1950       }
   1951 
   1952       EndCommand();
   1953       return;
   1954     }
   1955 
   1956     case Command::Pause:
   1957     {
   1958       const bool was_reading = (s_drive_state == DriveState::Reading || s_drive_state == DriveState::Playing);
   1959       const TickCount pause_time = was_reading ? (s_mode.double_speed ? 2000000 : 1000000) : 7000;
   1960 
   1961       ClearCommandSecondResponse();
   1962       SendACKAndStat();
   1963 
   1964       // This behaviour has been verified with hardware tests! The mech will reject pause commands if the game
   1965       // just started a read/seek, and it hasn't processed the first sector yet. This makes some games go bananas
   1966       // and spam pause commands until eventually it succeeds, but it is correct behaviour.
   1967       if (s_drive_state == DriveState::SeekingLogical || s_drive_state == DriveState::SeekingPhysical ||
   1968           ((s_drive_state == DriveState::Reading || s_drive_state == DriveState::Playing) &&
   1969            s_secondary_status.seeking))
   1970       {
   1971         WARNING_LOG("CDROM Pause command while seeking - sending error response");
   1972         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   1973         EndCommand();
   1974         return;
   1975       }
   1976       else
   1977       {
   1978         // Small window of time when another INT1 could sneak in, don't let it.
   1979         ClearAsyncInterrupt();
   1980 
   1981         // Stop reading.
   1982         s_drive_state = DriveState::Idle;
   1983         s_drive_event.Deactivate();
   1984         s_secondary_status.ClearActiveBits();
   1985       }
   1986 
   1987       // Reset audio buffer here - control room cutscene audio repeats in Dino Crisis otherwise.
   1988       ResetAudioDecoder();
   1989 
   1990       QueueCommandSecondResponse(Command::Pause, pause_time);
   1991 
   1992       EndCommand();
   1993       return;
   1994     }
   1995 
   1996     case Command::Stop:
   1997     {
   1998       const TickCount stop_time = GetTicksForStop(IsMotorOn());
   1999       ClearAsyncInterrupt();
   2000       ClearCommandSecondResponse();
   2001       SendACKAndStat();
   2002 
   2003       StopMotor();
   2004       QueueCommandSecondResponse(Command::Stop, stop_time);
   2005 
   2006       EndCommand();
   2007       return;
   2008     }
   2009 
   2010     case Command::Init:
   2011     {
   2012       DEBUG_LOG("CDROM init command");
   2013 
   2014       if (s_command_second_response == Command::Init)
   2015       {
   2016         // still pending
   2017         EndCommand();
   2018         return;
   2019       }
   2020 
   2021       SendACKAndStat();
   2022 
   2023       const TickCount reset_ticks = SoftReset(ticks_late);
   2024       QueueCommandSecondResponse(Command::Init, reset_ticks);
   2025       EndCommand();
   2026       return;
   2027     }
   2028 
   2029     case Command::MotorOn:
   2030     {
   2031       DEBUG_LOG("CDROM motor on command");
   2032       if (IsMotorOn())
   2033       {
   2034         SendErrorResponse(STAT_ERROR, ERROR_REASON_INCORRECT_NUMBER_OF_PARAMETERS);
   2035       }
   2036       else
   2037       {
   2038         SendACKAndStat();
   2039 
   2040         // still pending?
   2041         if (s_command_second_response == Command::MotorOn)
   2042         {
   2043           EndCommand();
   2044           return;
   2045         }
   2046 
   2047         if (CanReadMedia())
   2048           StartMotor();
   2049 
   2050         QueueCommandSecondResponse(Command::MotorOn, MOTOR_ON_RESPONSE_TICKS);
   2051       }
   2052 
   2053       EndCommand();
   2054       return;
   2055     }
   2056 
   2057     case Command::Mute:
   2058     {
   2059       DEBUG_LOG("CDROM mute command");
   2060       s_muted = true;
   2061       SendACKAndStat();
   2062       EndCommand();
   2063       return;
   2064     }
   2065 
   2066     case Command::Demute:
   2067     {
   2068       DEBUG_LOG("CDROM demute command");
   2069       s_muted = false;
   2070       SendACKAndStat();
   2071       EndCommand();
   2072       return;
   2073     }
   2074 
   2075     case Command::GetlocL:
   2076     {
   2077       if (!s_last_sector_header_valid)
   2078       {
   2079         DEV_LOG("CDROM GetlocL command - header invalid, status 0x{:02X}", s_secondary_status.bits);
   2080         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   2081       }
   2082       else
   2083       {
   2084         UpdatePhysicalPosition(true);
   2085 
   2086         DEBUG_LOG("CDROM GetlocL command - [{:02X}:{:02X}:{:02X}]", s_last_sector_header.minute,
   2087                   s_last_sector_header.second, s_last_sector_header.frame);
   2088 
   2089         s_response_fifo.PushRange(reinterpret_cast<const u8*>(&s_last_sector_header), sizeof(s_last_sector_header));
   2090         s_response_fifo.PushRange(reinterpret_cast<const u8*>(&s_last_sector_subheader),
   2091                                   sizeof(s_last_sector_subheader));
   2092         SetInterrupt(Interrupt::ACK);
   2093       }
   2094 
   2095       EndCommand();
   2096       return;
   2097     }
   2098 
   2099     case Command::GetlocP:
   2100     {
   2101       if (!CanReadMedia())
   2102       {
   2103         DEBUG_LOG("CDROM GetlocP command - not ready");
   2104         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   2105       }
   2106       else
   2107       {
   2108         if (IsSeeking())
   2109           UpdatePositionWhileSeeking();
   2110         else
   2111           UpdatePhysicalPosition(false);
   2112 
   2113         DEV_LOG("CDROM GetlocP command - T{:02x} I{:02x} R[{:02x}:{:02x}:{:02x}] A[{:02x}:{:02x}:{:02x}]",
   2114                 s_last_subq.track_number_bcd, s_last_subq.index_number_bcd, s_last_subq.relative_minute_bcd,
   2115                 s_last_subq.relative_second_bcd, s_last_subq.relative_frame_bcd, s_last_subq.absolute_minute_bcd,
   2116                 s_last_subq.absolute_second_bcd, s_last_subq.absolute_frame_bcd);
   2117 
   2118         s_response_fifo.Push(s_last_subq.track_number_bcd);
   2119         s_response_fifo.Push(s_last_subq.index_number_bcd);
   2120         s_response_fifo.Push(s_last_subq.relative_minute_bcd);
   2121         s_response_fifo.Push(s_last_subq.relative_second_bcd);
   2122         s_response_fifo.Push(s_last_subq.relative_frame_bcd);
   2123         s_response_fifo.Push(s_last_subq.absolute_minute_bcd);
   2124         s_response_fifo.Push(s_last_subq.absolute_second_bcd);
   2125         s_response_fifo.Push(s_last_subq.absolute_frame_bcd);
   2126         SetInterrupt(Interrupt::ACK);
   2127       }
   2128 
   2129       EndCommand();
   2130       return;
   2131     }
   2132 
   2133     case Command::GetTN:
   2134     {
   2135       DEBUG_LOG("CDROM GetTN command");
   2136       if (CanReadMedia())
   2137       {
   2138         DEV_LOG("GetTN -> {} {}", s_reader.GetMedia()->GetFirstTrackNumber(),
   2139                 s_reader.GetMedia()->GetLastTrackNumber());
   2140 
   2141         s_response_fifo.Push(s_secondary_status.bits);
   2142         s_response_fifo.Push(BinaryToBCD(Truncate8(s_reader.GetMedia()->GetFirstTrackNumber())));
   2143         s_response_fifo.Push(BinaryToBCD(Truncate8(s_reader.GetMedia()->GetLastTrackNumber())));
   2144         SetInterrupt(Interrupt::ACK);
   2145       }
   2146       else
   2147       {
   2148         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   2149       }
   2150 
   2151       EndCommand();
   2152       return;
   2153     }
   2154 
   2155     case Command::GetTD:
   2156     {
   2157       DEBUG_LOG("CDROM GetTD command");
   2158       Assert(s_param_fifo.GetSize() >= 1);
   2159 
   2160       if (!CanReadMedia())
   2161       {
   2162         SendErrorResponse(STAT_ERROR, ERROR_REASON_NOT_READY);
   2163         EndCommand();
   2164         return;
   2165       }
   2166 
   2167       const u8 track_bcd = s_param_fifo.Peek();
   2168       if (!IsValidPackedBCD(track_bcd))
   2169       {
   2170         ERROR_LOG("Invalid track number in GetTD: {:02X}", track_bcd);
   2171         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_ARGUMENT);
   2172         EndCommand();
   2173         return;
   2174       }
   2175 
   2176       const u8 track = PackedBCDToBinary(track_bcd);
   2177       if (track > s_reader.GetMedia()->GetTrackCount())
   2178       {
   2179         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_ARGUMENT);
   2180       }
   2181       else
   2182       {
   2183         CDImage::Position pos;
   2184         if (track == 0)
   2185           pos = CDImage::Position::FromLBA(s_reader.GetMedia()->GetLBACount());
   2186         else
   2187           pos = s_reader.GetMedia()->GetTrackStartMSFPosition(track);
   2188 
   2189         s_response_fifo.Push(s_secondary_status.bits);
   2190         s_response_fifo.Push(BinaryToBCD(Truncate8(pos.minute)));
   2191         s_response_fifo.Push(BinaryToBCD(Truncate8(pos.second)));
   2192         DEV_LOG("GetTD {} -> {} {}", track, pos.minute, pos.second);
   2193 
   2194         SetInterrupt(Interrupt::ACK);
   2195       }
   2196 
   2197       EndCommand();
   2198       return;
   2199     }
   2200 
   2201     case Command::Getmode:
   2202     {
   2203       DEBUG_LOG("CDROM Getmode command");
   2204 
   2205       s_response_fifo.Push(s_secondary_status.bits);
   2206       s_response_fifo.Push(s_mode.bits);
   2207       s_response_fifo.Push(0);
   2208       s_response_fifo.Push(s_xa_filter_file_number);
   2209       s_response_fifo.Push(s_xa_filter_channel_number);
   2210       SetInterrupt(Interrupt::ACK);
   2211       EndCommand();
   2212       return;
   2213     }
   2214 
   2215     case Command::Sync:
   2216     {
   2217       DEBUG_LOG("CDROM sync command");
   2218 
   2219       SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_COMMAND);
   2220       EndCommand();
   2221       return;
   2222     }
   2223 
   2224     case Command::VideoCD:
   2225     {
   2226       DEBUG_LOG("CDROM VideoCD command");
   2227       SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_COMMAND);
   2228 
   2229       // According to nocash this doesn't clear the parameter FIFO.
   2230       s_command = Command::None;
   2231       s_command_event.Deactivate();
   2232       UpdateStatusRegister();
   2233       return;
   2234     }
   2235 
   2236     default:
   2237       [[unlikely]]
   2238       {
   2239         ERROR_LOG("Unknown CDROM command 0x{:04X} with {} parameters, please report", static_cast<u16>(s_command),
   2240                   s_param_fifo.GetSize());
   2241         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_COMMAND);
   2242         EndCommand();
   2243         return;
   2244       }
   2245   }
   2246 }
   2247 
   2248 void CDROM::ExecuteTestCommand(u8 subcommand)
   2249 {
   2250   switch (subcommand)
   2251   {
   2252     case 0x04: // Reset SCEx counters
   2253     {
   2254       DEBUG_LOG("Reset SCEx counters");
   2255       s_secondary_status.motor_on = true;
   2256       s_response_fifo.Push(s_secondary_status.bits);
   2257       SetInterrupt(Interrupt::ACK);
   2258       EndCommand();
   2259       return;
   2260     }
   2261 
   2262     case 0x05: // Read SCEx counters
   2263     {
   2264       DEBUG_LOG("Read SCEx counters");
   2265       s_response_fifo.Push(s_secondary_status.bits);
   2266       s_response_fifo.Push(0); // # of TOC reads?
   2267       s_response_fifo.Push(0); // # of SCEx strings received
   2268       SetInterrupt(Interrupt::ACK);
   2269       EndCommand();
   2270       return;
   2271     }
   2272 
   2273     case 0x20: // Get CDROM BIOS Date/Version
   2274     {
   2275       DEBUG_LOG("Get CDROM BIOS Date/Version");
   2276 
   2277       static constexpr const u8 version_table[][4] = {
   2278         {0x94, 0x09, 0x19, 0xC0}, // PSX (PU-7)               19 Sep 1994, version vC0 (a)
   2279         {0x94, 0x11, 0x18, 0xC0}, // PSX (PU-7)               18 Nov 1994, version vC0 (b)
   2280         {0x95, 0x05, 0x16, 0xC1}, // PSX (EARLY-PU-8)         16 May 1995, version vC1 (a)
   2281         {0x95, 0x07, 0x24, 0xC1}, // PSX (LATE-PU-8)          24 Jul 1995, version vC1 (b)
   2282         {0x95, 0x07, 0x24, 0xD1}, // PSX (LATE-PU-8,debug ver)24 Jul 1995, version vD1 (debug)
   2283         {0x96, 0x08, 0x15, 0xC2}, // PSX (PU-16, Video CD)    15 Aug 1996, version vC2 (VCD)
   2284         {0x96, 0x08, 0x18, 0xC1}, // PSX (LATE-PU-8,yaroze)   18 Aug 1996, version vC1 (yaroze)
   2285         {0x96, 0x09, 0x12, 0xC2}, // PSX (PU-18) (japan)      12 Sep 1996, version vC2 (a.jap)
   2286         {0x97, 0x01, 0x10, 0xC2}, // PSX (PU-18) (us/eur)     10 Jan 1997, version vC2 (a)
   2287         {0x97, 0x08, 0x14, 0xC2}, // PSX (PU-20)              14 Aug 1997, version vC2 (b)
   2288         {0x98, 0x06, 0x10, 0xC3}, // PSX (PU-22)              10 Jul 1998, version vC3 (a)
   2289         {0x99, 0x02, 0x01, 0xC3}, // PSX/PSone (PU-23, PM-41) 01 Feb 1999, version vC3 (b)
   2290         {0xA1, 0x03, 0x06, 0xC3}, // PSone/late (PM-41(2))    06 Jun 2001, version vC3 (c)
   2291       };
   2292 
   2293       s_response_fifo.PushRange(version_table[static_cast<u8>(g_settings.cdrom_mechacon_version)],
   2294                                 countof(version_table[0]));
   2295       SetInterrupt(Interrupt::ACK);
   2296       EndCommand();
   2297       return;
   2298     }
   2299 
   2300     case 0x22:
   2301     {
   2302       DEBUG_LOG("Get CDROM region ID string");
   2303 
   2304       switch (System::GetRegion())
   2305       {
   2306         case ConsoleRegion::NTSC_J:
   2307         {
   2308           static constexpr u8 response[] = {'f', 'o', 'r', ' ', 'J', 'a', 'p', 'a', 'n'};
   2309           s_response_fifo.PushRange(response, countof(response));
   2310         }
   2311         break;
   2312 
   2313         case ConsoleRegion::PAL:
   2314         {
   2315           static constexpr u8 response[] = {'f', 'o', 'r', ' ', 'E', 'u', 'r', 'o', 'p', 'e'};
   2316           s_response_fifo.PushRange(response, countof(response));
   2317         }
   2318         break;
   2319 
   2320         case ConsoleRegion::NTSC_U:
   2321         default:
   2322         {
   2323           static constexpr u8 response[] = {'f', 'o', 'r', ' ', 'U', '/', 'C'};
   2324           s_response_fifo.PushRange(response, countof(response));
   2325         }
   2326         break;
   2327       }
   2328 
   2329       SetInterrupt(Interrupt::ACK);
   2330       EndCommand();
   2331       return;
   2332     }
   2333 
   2334     case 0x60:
   2335     {
   2336       if (s_param_fifo.GetSize() < 2) [[unlikely]]
   2337       {
   2338         SendErrorResponse(STAT_ERROR, ERROR_REASON_INCORRECT_NUMBER_OF_PARAMETERS);
   2339         EndCommand();
   2340         return;
   2341       }
   2342 
   2343       const u16 addr = ZeroExtend16(s_param_fifo.Peek(0)) | ZeroExtend16(s_param_fifo.Peek(1));
   2344       WARNING_LOG("Read memory from 0x{:04X}, returning zero", addr);
   2345       s_response_fifo.Push(0x00); // NOTE: No STAT here.
   2346       SetInterrupt(Interrupt::ACK);
   2347       EndCommand();
   2348       return;
   2349     }
   2350 
   2351     default:
   2352       [[unlikely]]
   2353       {
   2354         ERROR_LOG("Unknown test command 0x{:02X}, {} parameters", subcommand, s_param_fifo.GetSize());
   2355         SendErrorResponse(STAT_ERROR, ERROR_REASON_INVALID_COMMAND);
   2356         EndCommand();
   2357         return;
   2358       }
   2359   }
   2360 }
   2361 
   2362 void CDROM::ExecuteCommandSecondResponse(void*, TickCount ticks, TickCount ticks_late)
   2363 {
   2364   switch (s_command_second_response)
   2365   {
   2366     case Command::GetID:
   2367       DoIDRead();
   2368       break;
   2369 
   2370     case Command::Init:
   2371     {
   2372       // OpenBIOS spams Init, so we need to ensure the completion actually gets through.
   2373       // If we have a pending command (which is probably init), cancel it.
   2374       if (HasPendingCommand())
   2375       {
   2376         WARNING_LOG("Cancelling pending command 0x{:02X} ({}) due to init completion.", static_cast<u8>(s_command),
   2377                     s_command_info[static_cast<u8>(s_command)].name);
   2378         EndCommand();
   2379       }
   2380     }
   2381       [[fallthrough]];
   2382 
   2383     case Command::ReadTOC:
   2384     case Command::Pause:
   2385     case Command::MotorOn:
   2386     case Command::Stop:
   2387       DoStatSecondResponse();
   2388       break;
   2389 
   2390     default:
   2391       break;
   2392   }
   2393 
   2394   s_command_second_response = Command::None;
   2395   s_command_second_response_event.Deactivate();
   2396 }
   2397 
   2398 void CDROM::QueueCommandSecondResponse(Command command, TickCount ticks)
   2399 {
   2400   ClearCommandSecondResponse();
   2401   s_command_second_response = command;
   2402   s_command_second_response_event.Schedule(ticks);
   2403 }
   2404 
   2405 void CDROM::ClearCommandSecondResponse()
   2406 {
   2407   if (s_command_second_response != Command::None)
   2408   {
   2409     DEV_LOG("Cancelling pending command 0x{:02X} ({}) second response", static_cast<u16>(s_command_second_response),
   2410             s_command_info[static_cast<u16>(s_command_second_response)].name);
   2411   }
   2412 
   2413   s_command_second_response_event.Deactivate();
   2414   s_command_second_response = Command::None;
   2415 }
   2416 
   2417 void CDROM::UpdateCommandEvent()
   2418 {
   2419   // if there's a pending interrupt, we can't execute the command yet
   2420   // so deactivate it until the interrupt is acknowledged
   2421   if (!HasPendingCommand() || HasPendingInterrupt() || HasPendingAsyncInterrupt())
   2422   {
   2423     s_command_event.Deactivate();
   2424     return;
   2425   }
   2426   else if (HasPendingCommand())
   2427   {
   2428     s_command_event.Activate();
   2429   }
   2430 }
   2431 
   2432 void CDROM::ExecuteDrive(void*, TickCount ticks, TickCount ticks_late)
   2433 {
   2434   switch (s_drive_state)
   2435   {
   2436     case DriveState::ShellOpening:
   2437       DoShellOpenComplete(ticks_late);
   2438       break;
   2439 
   2440     case DriveState::SeekingPhysical:
   2441     case DriveState::SeekingLogical:
   2442       DoSeekComplete(ticks_late);
   2443       break;
   2444 
   2445     case DriveState::SeekingImplicit:
   2446       CompleteSeek();
   2447       break;
   2448 
   2449     case DriveState::Reading:
   2450     case DriveState::Playing:
   2451       DoSectorRead();
   2452       break;
   2453 
   2454     case DriveState::ChangingSession:
   2455       DoChangeSessionComplete();
   2456       break;
   2457 
   2458     case DriveState::SpinningUp:
   2459       DoSpinUpComplete();
   2460       break;
   2461 
   2462     case DriveState::ChangingSpeedOrTOCRead:
   2463       DoSpeedChangeOrImplicitTOCReadComplete();
   2464       break;
   2465 
   2466       // old states, no longer used, but kept for save state compatibility
   2467     case DriveState::UNUSED_ReadingID:
   2468     {
   2469       ClearDriveState();
   2470       DoIDRead();
   2471     }
   2472     break;
   2473 
   2474     case DriveState::UNUSED_Resetting:
   2475     case DriveState::UNUSED_ReadingTOC:
   2476     {
   2477       ClearDriveState();
   2478       DoStatSecondResponse();
   2479     }
   2480     break;
   2481 
   2482     case DriveState::UNUSED_Pausing:
   2483     {
   2484       ClearDriveState();
   2485       s_secondary_status.ClearActiveBits();
   2486       DoStatSecondResponse();
   2487     }
   2488     break;
   2489 
   2490     case DriveState::UNUSED_Stopping:
   2491     {
   2492       ClearDriveState();
   2493       StopMotor();
   2494       DoStatSecondResponse();
   2495     }
   2496     break;
   2497 
   2498     case DriveState::Idle:
   2499     default:
   2500       break;
   2501   }
   2502 }
   2503 
   2504 void CDROM::ClearDriveState()
   2505 {
   2506   s_drive_state = DriveState::Idle;
   2507   s_drive_event.Deactivate();
   2508 }
   2509 
   2510 void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
   2511 {
   2512   if (!after_seek && s_setloc_pending)
   2513   {
   2514     BeginSeeking(true, true, false);
   2515     return;
   2516   }
   2517 
   2518   // If we were seeking, we want to start reading from the seek target, not the current sector
   2519   // Fixes crash in Disney's The Lion King - Simba's Mighty Adventure.
   2520   if (IsSeeking())
   2521   {
   2522     DEV_LOG("Read command while seeking, scheduling read after seek {} -> {} finishes in {} ticks", s_seek_start_lba,
   2523             s_seek_end_lba, s_drive_event.GetTicksUntilNextExecution());
   2524 
   2525     // Implicit seeks won't trigger the read, so swap it for a logical.
   2526     if (s_drive_state == DriveState::SeekingImplicit)
   2527       s_drive_state = DriveState::SeekingLogical;
   2528 
   2529     s_read_after_seek = true;
   2530     s_play_after_seek = false;
   2531     return;
   2532   }
   2533 
   2534   DEBUG_LOG("Starting reading @ LBA {}", s_current_lba);
   2535 
   2536   const TickCount ticks = GetTicksForRead();
   2537   const TickCount first_sector_ticks = ticks + (after_seek ? 0 : GetTicksForSeek(s_current_lba)) - ticks_late;
   2538 
   2539   ClearCommandSecondResponse();
   2540   ClearAsyncInterrupt();
   2541   ClearSectorBuffers();
   2542   ResetAudioDecoder();
   2543 
   2544   // Even though this isn't "officially" a seek, we still need to jump back to the target sector unless we're
   2545   // immediately following a seek from Play/Read. The seeking bit will get cleared after the first sector is processed.
   2546   if (!after_seek)
   2547     s_secondary_status.SetSeeking();
   2548 
   2549   s_drive_state = DriveState::Reading;
   2550   s_drive_event.SetInterval(ticks);
   2551   s_drive_event.Schedule(first_sector_ticks);
   2552 
   2553   s_requested_lba = s_current_lba;
   2554   s_reader.QueueReadSector(s_requested_lba);
   2555 }
   2556 
   2557 void CDROM::BeginPlaying(u8 track, TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
   2558 {
   2559   DEBUG_LOG("Starting playing CDDA track {}", track);
   2560   s_last_cdda_report_frame_nibble = 0xFF;
   2561   s_play_track_number_bcd = track;
   2562   s_fast_forward_rate = 0;
   2563 
   2564   // if track zero, start from current position
   2565   if (track != 0)
   2566   {
   2567     // play specific track?
   2568     if (track > s_reader.GetMedia()->GetTrackCount())
   2569     {
   2570       // restart current track
   2571       track = Truncate8(s_reader.GetMedia()->GetTrackNumber());
   2572     }
   2573 
   2574     s_setloc_position = s_reader.GetMedia()->GetTrackStartMSFPosition(track);
   2575     s_setloc_pending = true;
   2576   }
   2577 
   2578   if (s_setloc_pending)
   2579   {
   2580     BeginSeeking(false, false, true);
   2581     return;
   2582   }
   2583 
   2584   const TickCount ticks = GetTicksForRead();
   2585   const TickCount first_sector_ticks = ticks + (after_seek ? 0 : GetTicksForSeek(s_current_lba, true)) - ticks_late;
   2586 
   2587   ClearCommandSecondResponse();
   2588   ClearAsyncInterrupt();
   2589   ClearSectorBuffers();
   2590   ResetAudioDecoder();
   2591 
   2592   s_drive_state = DriveState::Playing;
   2593   s_drive_event.SetInterval(ticks);
   2594   s_drive_event.Schedule(first_sector_ticks);
   2595 
   2596   s_requested_lba = s_current_lba;
   2597   s_reader.QueueReadSector(s_requested_lba);
   2598 }
   2599 
   2600 void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek)
   2601 {
   2602   if (!s_setloc_pending)
   2603     WARNING_LOG("Seeking without setloc set");
   2604 
   2605   s_read_after_seek = read_after_seek;
   2606   s_play_after_seek = play_after_seek;
   2607 
   2608   // TODO: Pending should stay set on seek command.
   2609   s_setloc_pending = false;
   2610 
   2611   DEBUG_LOG("Seeking to [{:02d}:{:02d}:{:02d}] (LBA {}) ({})", s_setloc_position.minute, s_setloc_position.second,
   2612             s_setloc_position.frame, s_setloc_position.ToLBA(), logical ? "logical" : "physical");
   2613 
   2614   const CDImage::LBA seek_lba = s_setloc_position.ToLBA();
   2615   const TickCount seek_time = GetTicksForSeek(seek_lba, play_after_seek);
   2616 
   2617   ClearCommandSecondResponse();
   2618   ClearAsyncInterrupt();
   2619   ClearSectorBuffers();
   2620   ResetAudioDecoder();
   2621 
   2622   s_secondary_status.SetSeeking();
   2623   s_last_sector_header_valid = false;
   2624 
   2625   s_drive_state = logical ? DriveState::SeekingLogical : DriveState::SeekingPhysical;
   2626   s_drive_event.SetIntervalAndSchedule(seek_time);
   2627 
   2628   s_seek_start_lba = s_current_lba;
   2629   s_seek_end_lba = seek_lba;
   2630   s_requested_lba = seek_lba;
   2631   s_reader.QueueReadSector(s_requested_lba);
   2632 }
   2633 
   2634 void CDROM::UpdatePositionWhileSeeking()
   2635 {
   2636   DebugAssert(IsSeeking());
   2637 
   2638   const float completed_frac = 1.0f - std::min(static_cast<float>(s_drive_event.GetTicksUntilNextExecution()) /
   2639                                                  static_cast<float>(s_drive_event.GetInterval()),
   2640                                                1.0f);
   2641 
   2642   CDImage::LBA current_lba;
   2643   if (s_seek_end_lba > s_seek_start_lba)
   2644   {
   2645     current_lba =
   2646       s_seek_start_lba +
   2647       std::max<CDImage::LBA>(
   2648         static_cast<CDImage::LBA>(static_cast<float>(s_seek_end_lba - s_seek_start_lba) * completed_frac), 1);
   2649   }
   2650   else if (s_seek_end_lba < s_seek_start_lba)
   2651   {
   2652     current_lba =
   2653       s_seek_start_lba -
   2654       std::max<CDImage::LBA>(
   2655         static_cast<CDImage::LBA>(static_cast<float>(s_seek_start_lba - s_seek_end_lba) * completed_frac), 1);
   2656   }
   2657   else
   2658   {
   2659     // strange seek...
   2660     return;
   2661   }
   2662 
   2663   DEV_LOG("Update position while seeking from {} to {} - {} ({:.2f})", s_seek_start_lba, s_seek_end_lba, current_lba,
   2664           completed_frac);
   2665 
   2666   // access the image directly since we want to preserve the cached data for the seek complete
   2667   CDImage::SubChannelQ subq;
   2668   if (!s_reader.ReadSectorUncached(current_lba, &subq, nullptr))
   2669     ERROR_LOG("Failed to read subq for sector {} for physical position", current_lba);
   2670   else if (subq.IsCRCValid())
   2671     s_last_subq = subq;
   2672 
   2673   s_current_lba = current_lba;
   2674   s_physical_lba = current_lba;
   2675   s_physical_lba_update_tick = System::GetGlobalTickCounter();
   2676   s_physical_lba_update_carry = 0;
   2677 }
   2678 
   2679 void CDROM::UpdatePhysicalPosition(bool update_logical)
   2680 {
   2681   const GlobalTicks ticks = System::GetGlobalTickCounter();
   2682   if (IsSeeking() || IsReadingOrPlaying() || !IsMotorOn())
   2683   {
   2684     // If we're seeking+reading the first sector (no stat bits set), we need to return the set/current lba, not the last
   2685     // physical LBA. Failing to do so may result in a track-jumped position getting returned in GetlocP, which causes
   2686     // Mad Panic Coaster to go into a seek+play loop.
   2687     if ((s_secondary_status.bits & (STAT_READING | STAT_PLAYING_CDDA | STAT_MOTOR_ON)) == STAT_MOTOR_ON &&
   2688         s_current_lba != s_physical_lba)
   2689     {
   2690       WARNING_LOG("Jumping to hold position [{}->{}] while {} first sector", s_physical_lba, s_current_lba,
   2691                   (s_drive_state == DriveState::Reading) ? "reading" : "playing");
   2692       SetHoldPosition(s_current_lba, true);
   2693     }
   2694 
   2695     // Otherwise, this gets updated by the read event.
   2696     return;
   2697   }
   2698 
   2699   const u32 ticks_per_read = GetTicksForRead();
   2700   const u32 diff = static_cast<u32>((ticks - s_physical_lba_update_tick) + s_physical_lba_update_carry);
   2701   const u32 sector_diff = diff / ticks_per_read;
   2702   const u32 carry = diff % ticks_per_read;
   2703   if (sector_diff > 0)
   2704   {
   2705     CDImage::LBA hold_offset;
   2706     CDImage::LBA sectors_per_track;
   2707 
   2708     // hardware tests show that it holds much closer to the target sector in logical mode
   2709     if (s_last_sector_header_valid)
   2710     {
   2711       hold_offset = 2;
   2712       sectors_per_track = 4;
   2713     }
   2714     else
   2715     {
   2716       hold_offset = 0;
   2717       sectors_per_track =
   2718         static_cast<CDImage::LBA>(7.0f + 2.811844405f * std::log(static_cast<float>(s_current_lba / 4500u) + 1u));
   2719     }
   2720 
   2721     const CDImage::LBA hold_position = s_current_lba + hold_offset;
   2722     const CDImage::LBA base =
   2723       (hold_position >= (sectors_per_track - 1)) ? (hold_position - (sectors_per_track - 1)) : hold_position;
   2724     if (s_physical_lba < base)
   2725       s_physical_lba = base;
   2726 
   2727     const CDImage::LBA old_offset = s_physical_lba - base;
   2728     const CDImage::LBA new_offset = (old_offset + sector_diff) % sectors_per_track;
   2729     const CDImage::LBA new_physical_lba = base + new_offset;
   2730 #ifdef _DEBUG
   2731     DEV_LOG("Tick diff {}, sector diff {}, old pos {}, new pos {}", diff, sector_diff, LBAToMSFString(s_physical_lba),
   2732             LBAToMSFString(new_physical_lba));
   2733 #endif
   2734     if (s_physical_lba != new_physical_lba)
   2735     {
   2736       s_physical_lba = new_physical_lba;
   2737 
   2738       CDImage::SubChannelQ subq;
   2739       CDROMAsyncReader::SectorBuffer raw_sector;
   2740       if (!s_reader.ReadSectorUncached(new_physical_lba, &subq, update_logical ? &raw_sector : nullptr))
   2741       {
   2742         ERROR_LOG("Failed to read subq for sector {} for physical position", new_physical_lba);
   2743       }
   2744       else
   2745       {
   2746         if (subq.IsCRCValid())
   2747           s_last_subq = subq;
   2748 
   2749         if (update_logical)
   2750           ProcessDataSectorHeader(raw_sector.data());
   2751       }
   2752 
   2753       s_physical_lba_update_tick = ticks;
   2754       s_physical_lba_update_carry = carry;
   2755     }
   2756   }
   2757 }
   2758 
   2759 void CDROM::SetHoldPosition(CDImage::LBA lba, bool update_subq)
   2760 {
   2761   if (update_subq && s_physical_lba != lba && CanReadMedia())
   2762   {
   2763     CDImage::SubChannelQ subq;
   2764     if (!s_reader.ReadSectorUncached(lba, &subq, nullptr))
   2765       ERROR_LOG("Failed to read subq for sector {} for physical position", lba);
   2766     else if (subq.IsCRCValid())
   2767       s_last_subq = subq;
   2768   }
   2769 
   2770   s_current_lba = lba;
   2771   s_physical_lba = lba;
   2772   s_physical_lba_update_tick = System::GetGlobalTickCounter();
   2773   s_physical_lba_update_carry = 0;
   2774 }
   2775 
   2776 void CDROM::DoShellOpenComplete(TickCount ticks_late)
   2777 {
   2778   // media is now readable (if any)
   2779   ClearDriveState();
   2780 
   2781   if (CanReadMedia())
   2782     StartMotor();
   2783 }
   2784 
   2785 bool CDROM::CompleteSeek()
   2786 {
   2787   const bool logical = (s_drive_state == DriveState::SeekingLogical);
   2788   ClearDriveState();
   2789 
   2790   bool seek_okay = s_reader.WaitForReadToComplete();
   2791   if (seek_okay)
   2792   {
   2793     const CDImage::SubChannelQ& subq = s_reader.GetSectorSubQ();
   2794     if (subq.IsCRCValid())
   2795     {
   2796       // seek and update sub-q for ReadP command
   2797       s_last_subq = subq;
   2798       const auto [seek_mm, seek_ss, seek_ff] = CDImage::Position::FromLBA(s_reader.GetLastReadSector()).ToBCD();
   2799       seek_okay = (subq.IsCRCValid() && subq.absolute_minute_bcd == seek_mm && subq.absolute_second_bcd == seek_ss &&
   2800                    subq.absolute_frame_bcd == seek_ff);
   2801       if (seek_okay)
   2802       {
   2803         if (subq.IsData())
   2804         {
   2805           if (logical)
   2806           {
   2807             ProcessDataSectorHeader(s_reader.GetSectorBuffer().data());
   2808             seek_okay = (s_last_sector_header.minute == seek_mm && s_last_sector_header.second == seek_ss &&
   2809                          s_last_sector_header.frame == seek_ff);
   2810           }
   2811         }
   2812         else
   2813         {
   2814           if (logical)
   2815           {
   2816             WARNING_LOG("Logical seek to non-data sector [{:02x}:{:02x}:{:02x}]{}", seek_mm, seek_ss, seek_ff,
   2817                         s_read_after_seek ? ", reading after seek" : "");
   2818 
   2819             // If CDDA mode isn't enabled and we're reading an audio sector, we need to fail the seek.
   2820             // Test cases:
   2821             //  - Wizard's Harmony does a logical seek to an audio sector, and expects it to succeed.
   2822             //  - Vib-ribbon starts a read at an audio sector, and expects it to fail.
   2823             if (s_read_after_seek)
   2824               seek_okay = s_mode.cdda;
   2825           }
   2826         }
   2827 
   2828         if (subq.track_number_bcd == CDImage::LEAD_OUT_TRACK_NUMBER)
   2829         {
   2830           WARNING_LOG("Invalid seek to lead-out area (LBA {})", s_reader.GetLastReadSector());
   2831           seek_okay = false;
   2832         }
   2833       }
   2834     }
   2835 
   2836     s_current_lba = s_reader.GetLastReadSector();
   2837   }
   2838 
   2839   s_physical_lba = s_current_lba;
   2840   s_physical_lba_update_tick = System::GetGlobalTickCounter();
   2841   s_physical_lba_update_carry = 0;
   2842   return seek_okay;
   2843 }
   2844 
   2845 void CDROM::DoSeekComplete(TickCount ticks_late)
   2846 {
   2847   const bool logical = (s_drive_state == DriveState::SeekingLogical);
   2848   const bool seek_okay = CompleteSeek();
   2849   if (seek_okay)
   2850   {
   2851     // seek complete, transition to play/read if requested
   2852     // INT2 is not sent on play/read
   2853     if (s_read_after_seek)
   2854     {
   2855       BeginReading(ticks_late, true);
   2856     }
   2857     else if (s_play_after_seek)
   2858     {
   2859       BeginPlaying(0, ticks_late, true);
   2860     }
   2861     else
   2862     {
   2863       s_secondary_status.ClearActiveBits();
   2864       s_async_response_fifo.Push(s_secondary_status.bits);
   2865       SetAsyncInterrupt(Interrupt::Complete);
   2866     }
   2867   }
   2868   else
   2869   {
   2870     WARNING_LOG("{} seek to [{}] failed", logical ? "Logical" : "Physical",
   2871                 LBAToMSFString(s_reader.GetLastReadSector()));
   2872     s_secondary_status.ClearActiveBits();
   2873     SendAsyncErrorResponse(STAT_SEEK_ERROR, 0x04);
   2874     s_last_sector_header_valid = false;
   2875   }
   2876 
   2877   s_setloc_pending = false;
   2878   s_read_after_seek = false;
   2879   s_play_after_seek = false;
   2880   UpdateStatusRegister();
   2881 }
   2882 
   2883 void CDROM::DoStatSecondResponse()
   2884 {
   2885   // Mainly for Reset/MotorOn.
   2886   if (!CanReadMedia())
   2887   {
   2888     SendAsyncErrorResponse(STAT_ERROR, 0x08);
   2889     return;
   2890   }
   2891 
   2892   s_async_response_fifo.Clear();
   2893   s_async_response_fifo.Push(s_secondary_status.bits);
   2894   SetAsyncInterrupt(Interrupt::Complete);
   2895 }
   2896 
   2897 void CDROM::DoChangeSessionComplete()
   2898 {
   2899   DEBUG_LOG("Changing session complete");
   2900   ClearDriveState();
   2901   s_secondary_status.ClearActiveBits();
   2902   s_secondary_status.motor_on = true;
   2903 
   2904   s_async_response_fifo.Clear();
   2905   if (s_async_command_parameter == 0x01)
   2906   {
   2907     s_async_response_fifo.Push(s_secondary_status.bits);
   2908     SetAsyncInterrupt(Interrupt::Complete);
   2909   }
   2910   else
   2911   {
   2912     // we don't emulate multisession discs.. for now
   2913     SendAsyncErrorResponse(STAT_SEEK_ERROR, 0x40);
   2914   }
   2915 }
   2916 
   2917 void CDROM::DoSpinUpComplete()
   2918 {
   2919   DEBUG_LOG("Spinup complete");
   2920   s_drive_state = DriveState::Idle;
   2921   s_drive_event.Deactivate();
   2922   s_secondary_status.ClearActiveBits();
   2923   s_secondary_status.motor_on = true;
   2924 }
   2925 
   2926 void CDROM::DoSpeedChangeOrImplicitTOCReadComplete()
   2927 {
   2928   DEBUG_LOG("Speed change/implicit TOC read complete");
   2929   s_drive_state = DriveState::Idle;
   2930   s_drive_event.Deactivate();
   2931 }
   2932 
   2933 void CDROM::DoIDRead()
   2934 {
   2935   DEBUG_LOG("ID read complete");
   2936   s_secondary_status.ClearActiveBits();
   2937   s_secondary_status.motor_on = CanReadMedia();
   2938 
   2939   // TODO: Audio CD.
   2940   u8 stat_byte = s_secondary_status.bits;
   2941   u8 flags_byte = 0;
   2942   if (!CanReadMedia())
   2943   {
   2944     stat_byte |= STAT_ID_ERROR;
   2945     flags_byte |= (1 << 6); // Disc Missing
   2946   }
   2947   else
   2948   {
   2949     if (IsMediaAudioCD())
   2950     {
   2951       stat_byte |= STAT_ID_ERROR;
   2952       flags_byte |= (1 << 7) | (1 << 4); // Unlicensed + Audio CD
   2953     }
   2954     else if (!IsMediaPS1Disc() || !DoesMediaRegionMatchConsole())
   2955     {
   2956       stat_byte |= STAT_ID_ERROR;
   2957       flags_byte |= (1 << 7); // Unlicensed
   2958     }
   2959   }
   2960 
   2961   s_async_response_fifo.Clear();
   2962   s_async_response_fifo.Push(stat_byte);
   2963   s_async_response_fifo.Push(flags_byte);
   2964   s_async_response_fifo.Push(0x20); // TODO: Disc type from TOC
   2965   s_async_response_fifo.Push(0x00); // TODO: Session info?
   2966 
   2967   static constexpr u32 REGION_STRING_LENGTH = 4;
   2968   static constexpr std::array<std::array<u8, REGION_STRING_LENGTH>, static_cast<size_t>(DiscRegion::Count)>
   2969     region_strings = {{{'S', 'C', 'E', 'I'}, {'S', 'C', 'E', 'A'}, {'S', 'C', 'E', 'E'}, {0, 0, 0, 0}, {0, 0, 0, 0}}};
   2970   s_async_response_fifo.PushRange(region_strings[static_cast<u8>(s_disc_region)].data(), REGION_STRING_LENGTH);
   2971 
   2972   SetAsyncInterrupt((flags_byte != 0) ? Interrupt::Error : Interrupt::Complete);
   2973 }
   2974 
   2975 void CDROM::StopReadingWithDataEnd()
   2976 {
   2977   ClearAsyncInterrupt();
   2978   s_async_response_fifo.Push(s_secondary_status.bits);
   2979   SetAsyncInterrupt(Interrupt::DataEnd);
   2980 
   2981   s_secondary_status.ClearActiveBits();
   2982   ClearDriveState();
   2983 }
   2984 
   2985 void CDROM::StartMotor()
   2986 {
   2987   if (s_drive_state == DriveState::SpinningUp)
   2988   {
   2989     DEV_LOG("Starting motor - already spinning up");
   2990     return;
   2991   }
   2992 
   2993   DEV_LOG("Starting motor");
   2994   s_drive_state = DriveState::SpinningUp;
   2995   s_drive_event.Schedule(GetTicksForSpinUp());
   2996 }
   2997 
   2998 void CDROM::StopMotor()
   2999 {
   3000   s_secondary_status.ClearActiveBits();
   3001   s_secondary_status.motor_on = false;
   3002   ClearDriveState();
   3003   SetHoldPosition(0, false);
   3004   s_last_sector_header_valid = false; // TODO: correct?
   3005 }
   3006 
   3007 void CDROM::DoSectorRead()
   3008 {
   3009   // TODO: Queue the next read here and swap the buffer.
   3010   // TODO: Error handling
   3011   if (!s_reader.WaitForReadToComplete())
   3012     Panic("Sector read failed");
   3013 
   3014   s_current_lba = s_reader.GetLastReadSector();
   3015   s_physical_lba = s_current_lba;
   3016   s_physical_lba_update_tick = System::GetGlobalTickCounter();
   3017   s_physical_lba_update_carry = 0;
   3018 
   3019   s_secondary_status.SetReadingBits(s_drive_state == DriveState::Playing);
   3020 
   3021   const CDImage::SubChannelQ& subq = s_reader.GetSectorSubQ();
   3022   const bool subq_valid = subq.IsCRCValid();
   3023   if (subq_valid)
   3024   {
   3025     s_last_subq = subq;
   3026   }
   3027   else
   3028   {
   3029     DEV_LOG("Sector {} [{}] has invalid subchannel Q", s_current_lba, LBAToMSFString(s_current_lba));
   3030   }
   3031 
   3032   if (subq.track_number_bcd == CDImage::LEAD_OUT_TRACK_NUMBER)
   3033   {
   3034     DEV_LOG("Read reached lead-out area of disc at LBA {}, stopping", s_reader.GetLastReadSector());
   3035     StopReadingWithDataEnd();
   3036     StopMotor();
   3037     return;
   3038   }
   3039 
   3040   const bool is_data_sector = subq.IsData();
   3041   if (is_data_sector)
   3042   {
   3043     ProcessDataSectorHeader(s_reader.GetSectorBuffer().data());
   3044   }
   3045   else if (s_mode.auto_pause)
   3046   {
   3047     // Only update the tracked track-to-pause-after once auto pause is enabled. Pitball's menu music starts mid-second,
   3048     // and there's no pregap, so the first couple of reports are for the previous track. It doesn't enable autopause
   3049     // until receiving a couple, and it's actually playing the track it wants.
   3050     if (s_play_track_number_bcd == 0)
   3051     {
   3052       // track number was not specified, but we've found the track now
   3053       s_play_track_number_bcd = subq.track_number_bcd;
   3054       DEBUG_LOG("Setting playing track number to {}", s_play_track_number_bcd);
   3055     }
   3056     else if (subq.track_number_bcd != s_play_track_number_bcd)
   3057     {
   3058       // we don't want to update the position if the track changes, so we check it before reading the actual sector.
   3059       DEV_LOG("Auto pause at the start of track {:02x} (LBA {})", s_last_subq.track_number_bcd, s_current_lba);
   3060       StopReadingWithDataEnd();
   3061       return;
   3062     }
   3063   }
   3064 
   3065   u32 next_sector = s_current_lba + 1u;
   3066   if (is_data_sector && s_drive_state == DriveState::Reading)
   3067   {
   3068     ProcessDataSector(s_reader.GetSectorBuffer().data(), subq);
   3069   }
   3070   else if (!is_data_sector &&
   3071            (s_drive_state == DriveState::Playing || (s_drive_state == DriveState::Reading && s_mode.cdda)))
   3072   {
   3073     ProcessCDDASector(s_reader.GetSectorBuffer().data(), subq, subq_valid);
   3074 
   3075     if (s_fast_forward_rate != 0)
   3076       next_sector = s_current_lba + SignExtend32(s_fast_forward_rate);
   3077   }
   3078   else if (s_drive_state != DriveState::Reading && s_drive_state != DriveState::Playing)
   3079   {
   3080     Panic("Not reading or playing");
   3081   }
   3082   else
   3083   {
   3084     WARNING_LOG("Skipping sector {} as it is a {} sector and we're not {}", s_current_lba,
   3085                 is_data_sector ? "data" : "audio", is_data_sector ? "reading" : "playing");
   3086   }
   3087 
   3088   s_requested_lba = next_sector;
   3089   s_reader.QueueReadSector(s_requested_lba);
   3090 }
   3091 
   3092 ALWAYS_INLINE_RELEASE void CDROM::ProcessDataSectorHeader(const u8* raw_sector)
   3093 {
   3094   std::memcpy(&s_last_sector_header, &raw_sector[SECTOR_SYNC_SIZE], sizeof(s_last_sector_header));
   3095   std::memcpy(&s_last_sector_subheader, &raw_sector[SECTOR_SYNC_SIZE + sizeof(s_last_sector_header)],
   3096               sizeof(s_last_sector_subheader));
   3097   s_last_sector_header_valid = true;
   3098 }
   3099 
   3100 ALWAYS_INLINE_RELEASE void CDROM::ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
   3101 {
   3102   const u32 sb_num = (s_current_write_sector_buffer + 1) % NUM_SECTOR_BUFFERS;
   3103   DEV_LOG("Read sector {} [{}]: mode {} submode 0x{:02X} into buffer {}", s_current_lba, LBAToMSFString(s_current_lba),
   3104           s_last_sector_header.sector_mode, ZeroExtend32(s_last_sector_subheader.submode.bits), sb_num);
   3105 
   3106   if (s_mode.xa_enable && s_last_sector_header.sector_mode == 2)
   3107   {
   3108     if (s_last_sector_subheader.submode.realtime && s_last_sector_subheader.submode.audio)
   3109     {
   3110       ProcessXAADPCMSector(raw_sector, subq);
   3111 
   3112       // Audio+realtime sectors aren't delivered to the CPU.
   3113       return;
   3114     }
   3115   }
   3116 
   3117   // TODO: How does XA relate to this buffering?
   3118   SectorBuffer* sb = &s_sector_buffers[sb_num];
   3119   if (sb->position == 0 && sb->size > 0)
   3120   {
   3121     DEV_LOG("Sector buffer {} was not read, previous sector dropped",
   3122             (s_current_write_sector_buffer - 1) % NUM_SECTOR_BUFFERS);
   3123   }
   3124 
   3125   if (s_mode.ignore_bit)
   3126     WARNING_LOG("SetMode.4 bit set on read of sector {}", s_current_lba);
   3127 
   3128   if (s_mode.read_raw_sector)
   3129   {
   3130     std::memcpy(sb->data.data(), raw_sector + SECTOR_SYNC_SIZE, RAW_SECTOR_OUTPUT_SIZE);
   3131     sb->size = RAW_SECTOR_OUTPUT_SIZE;
   3132   }
   3133   else
   3134   {
   3135     // TODO: This should actually depend on the mode...
   3136     if (s_last_sector_header.sector_mode != 2)
   3137     {
   3138       WARNING_LOG("Ignoring non-mode2 sector at {}", s_current_lba);
   3139       return;
   3140     }
   3141 
   3142     std::memcpy(sb->data.data(), raw_sector + CDImage::SECTOR_SYNC_SIZE + 12, DATA_SECTOR_OUTPUT_SIZE);
   3143     sb->size = DATA_SECTOR_OUTPUT_SIZE;
   3144   }
   3145 
   3146   sb->position = 0;
   3147   s_current_write_sector_buffer = sb_num;
   3148 
   3149   // Deliver to CPU
   3150   if (HasPendingAsyncInterrupt())
   3151   {
   3152     WARNING_LOG("Data interrupt was not delivered");
   3153     ClearAsyncInterrupt();
   3154   }
   3155 
   3156   if (HasPendingInterrupt())
   3157   {
   3158     const u32 sectors_missed = (s_current_write_sector_buffer - s_current_read_sector_buffer) % NUM_SECTOR_BUFFERS;
   3159     if (sectors_missed > 1)
   3160       WARNING_LOG("Interrupt not processed in time, missed {} sectors", sectors_missed - 1);
   3161   }
   3162 
   3163   s_async_response_fifo.Push(s_secondary_status.bits);
   3164   SetAsyncInterrupt(Interrupt::DataReady);
   3165 }
   3166 
   3167 std::tuple<s16, s16> CDROM::GetAudioFrame()
   3168 {
   3169   const u32 frame = s_audio_fifo.IsEmpty() ? 0u : s_audio_fifo.Pop();
   3170   const s16 left = static_cast<s16>(Truncate16(frame));
   3171   const s16 right = static_cast<s16>(Truncate16(frame >> 16));
   3172   const s16 left_out = SaturateVolume(ApplyVolume(left, s_cd_audio_volume_matrix[0][0]) +
   3173                                       ApplyVolume(right, s_cd_audio_volume_matrix[1][0]));
   3174   const s16 right_out = SaturateVolume(ApplyVolume(left, s_cd_audio_volume_matrix[0][1]) +
   3175                                        ApplyVolume(right, s_cd_audio_volume_matrix[1][1]));
   3176   return std::tuple<s16, s16>(left_out, right_out);
   3177 }
   3178 
   3179 void CDROM::AddCDAudioFrame(s16 left, s16 right)
   3180 {
   3181   s_audio_fifo.Push(ZeroExtend32(static_cast<u16>(left)) | (ZeroExtend32(static_cast<u16>(right)) << 16));
   3182 }
   3183 
   3184 s32 CDROM::ApplyVolume(s16 sample, u8 volume)
   3185 {
   3186   return s32(sample) * static_cast<s32>(ZeroExtend32(volume)) >> 7;
   3187 }
   3188 
   3189 s16 CDROM::SaturateVolume(s32 volume)
   3190 {
   3191   return static_cast<s16>((volume < -0x8000) ? -0x8000 : ((volume > 0x7FFF) ? 0x7FFF : volume));
   3192 }
   3193 
   3194 template<bool IS_STEREO, bool IS_8BIT>
   3195 void CDROM::DecodeXAADPCMChunks(const u8* chunk_ptr, s16* samples)
   3196 {
   3197   static constexpr std::array<s8, 16> s_xa_adpcm_filter_table_pos = {
   3198     {0, 60, 115, 98, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
   3199 
   3200   static constexpr std::array<s8, 16> s_xa_adpcm_filter_table_neg = {
   3201     {0, 0, -52, -55, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
   3202 
   3203   // The data layout is annoying here. Each word of data is interleaved with the other blocks, requiring multiple
   3204   // passes to decode the whole chunk.
   3205   constexpr u32 NUM_CHUNKS = 18;
   3206   constexpr u32 CHUNK_SIZE_IN_BYTES = 128;
   3207   constexpr u32 WORDS_PER_CHUNK = 28;
   3208   constexpr u32 SAMPLES_PER_CHUNK = WORDS_PER_CHUNK * (IS_8BIT ? 4 : 8);
   3209   constexpr u32 NUM_BLOCKS = IS_8BIT ? 4 : 8;
   3210   constexpr u32 WORDS_PER_BLOCK = 28;
   3211 
   3212   for (u32 i = 0; i < NUM_CHUNKS; i++)
   3213   {
   3214     const u8* headers_ptr = chunk_ptr + 4;
   3215     const u8* words_ptr = chunk_ptr + 16;
   3216 
   3217     for (u32 block = 0; block < NUM_BLOCKS; block++)
   3218     {
   3219       const XA_ADPCMBlockHeader block_header{headers_ptr[block]};
   3220       const u8 shift = block_header.GetShift();
   3221       const u8 filter = block_header.GetFilter();
   3222       const s32 filter_pos = s_xa_adpcm_filter_table_pos[filter];
   3223       const s32 filter_neg = s_xa_adpcm_filter_table_neg[filter];
   3224 
   3225       s16* out_samples_ptr =
   3226         IS_STEREO ? &samples[(block / 2) * (WORDS_PER_BLOCK * 2) + (block % 2)] : &samples[block * WORDS_PER_BLOCK];
   3227       constexpr u32 out_samples_increment = IS_STEREO ? 2 : 1;
   3228 
   3229       for (u32 word = 0; word < 28; word++)
   3230       {
   3231         // NOTE: assumes LE
   3232         u32 word_data;
   3233         std::memcpy(&word_data, &words_ptr[word * sizeof(u32)], sizeof(word_data));
   3234 
   3235         // extract nibble from block
   3236         const u32 nibble = IS_8BIT ? ((word_data >> (block * 8)) & 0xFF) : ((word_data >> (block * 4)) & 0x0F);
   3237         const s16 sample = static_cast<s16>(Truncate16(nibble << (IS_8BIT ? 8 : 12))) >> shift;
   3238 
   3239         // mix in previous values
   3240         s32* prev = IS_STEREO ? &s_xa_last_samples[(block & 1) * 2] : &s_xa_last_samples[0];
   3241         const s32 interp_sample = std::clamp<s32>(
   3242           static_cast<s32>(sample) + ((prev[0] * filter_pos) >> 6) + ((prev[1] * filter_neg) >> 6), -32767, 32768);
   3243 
   3244         // update previous values
   3245         prev[1] = prev[0];
   3246         prev[0] = interp_sample;
   3247 
   3248         *out_samples_ptr = static_cast<s16>(interp_sample);
   3249         out_samples_ptr += out_samples_increment;
   3250       }
   3251     }
   3252 
   3253     samples += SAMPLES_PER_CHUNK;
   3254     chunk_ptr += CHUNK_SIZE_IN_BYTES;
   3255   }
   3256 }
   3257 
   3258 template<bool STEREO>
   3259 void CDROM::ResampleXAADPCM(const s16* frames_in, u32 num_frames_in)
   3260 {
   3261   static constexpr auto zigzag_interpolate = [](const s16* ringbuf, u32 table_index, u32 p) -> s16 {
   3262     static std::array<std::array<s16, 29>, 7> tables = {
   3263       {{0,      0x0,     0x0,     0x0,    0x0,     -0x0002, 0x000A,  -0x0022, 0x0041, -0x0054,
   3264         0x0034, 0x0009,  -0x010A, 0x0400, -0x0A78, 0x234C,  0x6794,  -0x1780, 0x0BCD, -0x0623,
   3265         0x0350, -0x016D, 0x006B,  0x000A, -0x0010, 0x0011,  -0x0008, 0x0003,  -0x0001},
   3266        {0,       0x0,    0x0,     -0x0002, 0x0,    0x0003,  -0x0013, 0x003C,  -0x004B, 0x00A2,
   3267         -0x00E3, 0x0132, -0x0043, -0x0267, 0x0C9D, 0x74BB,  -0x11B4, 0x09B8,  -0x05BF, 0x0372,
   3268         -0x01A8, 0x00A6, -0x001B, 0x0005,  0x0006, -0x0008, 0x0003,  -0x0001, 0x0},
   3269        {0,      0x0,     -0x0001, 0x0003,  -0x0002, -0x0005, 0x001F,  -0x004A, 0x00B3, -0x0192,
   3270         0x02B1, -0x039E, 0x04F8,  -0x05A6, 0x7939,  -0x05A6, 0x04F8,  -0x039E, 0x02B1, -0x0192,
   3271         0x00B3, -0x004A, 0x001F,  -0x0005, -0x0002, 0x0003,  -0x0001, 0x0,     0x0},
   3272        {0,       -0x0001, 0x0003,  -0x0008, 0x0006, 0x0005,  -0x001B, 0x00A6, -0x01A8, 0x0372,
   3273         -0x05BF, 0x09B8,  -0x11B4, 0x74BB,  0x0C9D, -0x0267, -0x0043, 0x0132, -0x00E3, 0x00A2,
   3274         -0x004B, 0x003C,  -0x0013, 0x0003,  0x0,    -0x0002, 0x0,     0x0,    0x0},
   3275        {-0x0001, 0x0003,  -0x0008, 0x0011,  -0x0010, 0x000A, 0x006B,  -0x016D, 0x0350, -0x0623,
   3276         0x0BCD,  -0x1780, 0x6794,  0x234C,  -0x0A78, 0x0400, -0x010A, 0x0009,  0x0034, -0x0054,
   3277         0x0041,  -0x0022, 0x000A,  -0x0001, 0x0,     0x0001, 0x0,     0x0,     0x0},
   3278        {0x0002,  -0x0008, 0x0010,  -0x0023, 0x002B, 0x001A,  -0x00EB, 0x027B,  -0x0548, 0x0AFA,
   3279         -0x16FA, 0x53E0,  0x3C07,  -0x1249, 0x080E, -0x0347, 0x015B,  -0x0044, -0x0017, 0x0046,
   3280         -0x0023, 0x0011,  -0x0005, 0x0,     0x0,    0x0,     0x0,     0x0,     0x0},
   3281        {-0x0005, 0x0011,  -0x0023, 0x0046, -0x0017, -0x0044, 0x015B,  -0x0347, 0x080E, -0x1249,
   3282         0x3C07,  0x53E0,  -0x16FA, 0x0AFA, -0x0548, 0x027B,  -0x00EB, 0x001A,  0x002B, -0x0023,
   3283         0x0010,  -0x0008, 0x0002,  0x0,    0x0,     0x0,     0x0,     0x0,     0x0}}};
   3284 
   3285     const s16* table = tables[table_index].data();
   3286     s32 sum = 0;
   3287     for (u32 i = 0; i < 29; i++)
   3288       sum += (static_cast<s32>(ringbuf[(p - i) & 0x1F]) * static_cast<s32>(table[i])) >> 15;
   3289 
   3290     return static_cast<s16>(std::clamp<s32>(sum, -0x8000, 0x7FFF));
   3291   };
   3292 
   3293   s16* const left_ringbuf = s_xa_resample_ring_buffer[0].data();
   3294   [[maybe_unused]] s16* const right_ringbuf = s_xa_resample_ring_buffer[1].data();
   3295   u32 p = s_xa_resample_p;
   3296   u32 sixstep = s_xa_resample_sixstep;
   3297 
   3298   for (u32 in_sample_index = 0; in_sample_index < num_frames_in; in_sample_index++)
   3299   {
   3300     // TODO: We can vectorize the multiplications in zigzag_interpolate by duplicating the sample in the ringbuffer at
   3301     // offset +32, allowing it to wrap once.
   3302     left_ringbuf[p] = *(frames_in++);
   3303     if constexpr (STEREO)
   3304       right_ringbuf[p] = *(frames_in++);
   3305     p = (p + 1) % 32;
   3306     sixstep--;
   3307 
   3308     if (sixstep == 0)
   3309     {
   3310       sixstep = 6;
   3311       for (u32 j = 0; j < 7; j++)
   3312       {
   3313         const s16 left_interp = zigzag_interpolate(left_ringbuf, j, p);
   3314         const s16 right_interp = STEREO ? zigzag_interpolate(right_ringbuf, j, p) : left_interp;
   3315         AddCDAudioFrame(left_interp, right_interp);
   3316       }
   3317     }
   3318   }
   3319 
   3320   s_xa_resample_p = Truncate8(p);
   3321   s_xa_resample_sixstep = Truncate8(sixstep);
   3322 }
   3323 
   3324 template<bool STEREO>
   3325 void CDROM::ResampleXAADPCM18900(const s16* frames_in, u32 num_frames_in)
   3326 {
   3327   // Weights originally from Mednafen's interpolator. It's unclear where these came from, perhaps it was calculated
   3328   // somehow. This doesn't appear to use a zigzag pattern like psx-spx suggests, therefore it is restricted to only
   3329   // 18900hz resampling. Duplicating the 18900hz samples to 37800hz sounds even more awful than lower sample rate audio
   3330   // should, with a big spike at ~16KHz, especially with music in FMVs. Fortunately, few games actually use 18900hz XA.
   3331   static constexpr auto interpolate = [](const s16* ringbuf, u32 table_index, u32 p) -> s16 {
   3332     static std::array<std::array<s16, 25>, 7> tables = {{
   3333       {{0x0,     -0x5,  0x11,   -0x23, 0x46,  -0x17, -0x44, 0x15b, -0x347, 0x80e, -0x1249, 0x3c07, 0x53e0,
   3334         -0x16fa, 0xafa, -0x548, 0x27b, -0xeb, 0x1a,  0x2b,  -0x23, 0x10,   -0x8,  0x2,     0x0}},
   3335       {{0x0,     -0x2,  0xa,    -0x22, 0x41,   -0x54, 0x34, 0x9,   -0x10a, 0x400, -0xa78, 0x234c, 0x6794,
   3336         -0x1780, 0xbcd, -0x623, 0x350, -0x16d, 0x6b,  0xa,  -0x10, 0x11,   -0x8,  0x3,    -0x1}},
   3337       {{-0x2,    0x0,   0x3,    -0x13, 0x3c,   -0x4b, 0xa2,  -0xe3, 0x132, -0x43, -0x267, 0xc9d, 0x74bb,
   3338         -0x11b4, 0x9b8, -0x5bf, 0x372, -0x1a8, 0xa6,  -0x1b, 0x5,   0x6,   -0x8,  0x3,    -0x1}},
   3339       {{-0x1,   0x3,   -0x2,   -0x5,  0x1f,   -0x4a, 0xb3,  -0x192, 0x2b1, -0x39e, 0x4f8, -0x5a6, 0x7939,
   3340         -0x5a6, 0x4f8, -0x39e, 0x2b1, -0x192, 0xb3,  -0x4a, 0x1f,   -0x5,  -0x2,   0x3,   -0x1}},
   3341       {{-0x1,  0x3,    -0x8,  0x6,   0x5,   -0x1b, 0xa6,  -0x1a8, 0x372, -0x5bf, 0x9b8, -0x11b4, 0x74bb,
   3342         0xc9d, -0x267, -0x43, 0x132, -0xe3, 0xa2,  -0x4b, 0x3c,   -0x13, 0x3,    0x0,   -0x2}},
   3343       {{-0x1,   0x3,    -0x8,  0x11,   -0x10, 0xa,  0x6b,  -0x16d, 0x350, -0x623, 0xbcd, -0x1780, 0x6794,
   3344         0x234c, -0xa78, 0x400, -0x10a, 0x9,   0x34, -0x54, 0x41,   -0x22, 0xa,    -0x2,  0x0}},
   3345       {{0x0,    0x2,     -0x8,  0x10,   -0x23, 0x2b,  0x1a,  -0xeb, 0x27b, -0x548, 0xafa, -0x16fa, 0x53e0,
   3346         0x3c07, -0x1249, 0x80e, -0x347, 0x15b, -0x44, -0x17, 0x46,  -0x23, 0x11,   -0x5,  0x0}},
   3347     }};
   3348 
   3349     const s16* table = tables[table_index].data();
   3350     s32 sum = 0;
   3351     for (u32 i = 0; i < 25; i++)
   3352       sum += (static_cast<s32>(ringbuf[(p + 32 - 25 + i) & 0x1F]) * static_cast<s32>(table[i]));
   3353 
   3354     return static_cast<s16>(std::clamp<s32>(sum >> 15, -0x8000, 0x7FFF));
   3355   };
   3356 
   3357   s16* const left_ringbuf = s_xa_resample_ring_buffer[0].data();
   3358   [[maybe_unused]] s16* const right_ringbuf = s_xa_resample_ring_buffer[1].data();
   3359   u32 p = s_xa_resample_p;
   3360   u32 sixstep = s_xa_resample_sixstep;
   3361 
   3362   for (u32 in_sample_index = 0; in_sample_index < num_frames_in;)
   3363   {
   3364     if (sixstep >= 7)
   3365     {
   3366       sixstep -= 7;
   3367       p = (p + 1) % 32;
   3368 
   3369       left_ringbuf[p] = *(frames_in++);
   3370       if constexpr (STEREO)
   3371         right_ringbuf[p] = *(frames_in++);
   3372 
   3373       in_sample_index++;
   3374     }
   3375 
   3376     const s16 left_interp = interpolate(left_ringbuf, sixstep, p);
   3377     const s16 right_interp = STEREO ? interpolate(right_ringbuf, sixstep, p) : left_interp;
   3378     AddCDAudioFrame(left_interp, right_interp);
   3379     sixstep += 3;
   3380   }
   3381 
   3382   s_xa_resample_p = Truncate8(p);
   3383   s_xa_resample_sixstep = Truncate8(sixstep);
   3384 }
   3385 
   3386 void CDROM::ResetCurrentXAFile()
   3387 {
   3388   s_xa_current_channel_number = 0;
   3389   s_xa_current_file_number = 0;
   3390   s_xa_current_set = false;
   3391 }
   3392 
   3393 void CDROM::ResetAudioDecoder()
   3394 {
   3395   ResetCurrentXAFile();
   3396 
   3397   s_xa_last_samples.fill(0);
   3398   for (u32 i = 0; i < 2; i++)
   3399   {
   3400     s_xa_resample_ring_buffer[i].fill(0);
   3401     s_xa_resample_p = 0;
   3402     s_xa_resample_sixstep = 6;
   3403   }
   3404   s_audio_fifo.Clear();
   3405 }
   3406 
   3407 ALWAYS_INLINE_RELEASE void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
   3408 {
   3409   // Check for automatic ADPCM filter.
   3410   if (s_mode.xa_filter && (s_last_sector_subheader.file_number != s_xa_filter_file_number ||
   3411                            s_last_sector_subheader.channel_number != s_xa_filter_channel_number))
   3412   {
   3413     DEBUG_LOG("Skipping sector due to filter mismatch (expected {}/{} got {}/{})", s_xa_filter_file_number,
   3414               s_xa_filter_channel_number, s_last_sector_subheader.file_number, s_last_sector_subheader.channel_number);
   3415     return;
   3416   }
   3417 
   3418   // Track the current file being played. If this is not set by the filter, it'll be set by the first file/sector which
   3419   // is read. Fixes audio in Tomb Raider III menu.
   3420   if (!s_xa_current_set)
   3421   {
   3422     // Some games (Taxi 2 and Blues Clues) have junk audio sectors with a channel number of 255.
   3423     // We need to skip them otherwise it ends up playing the incorrect file.
   3424     // TODO: Verify with a hardware test.
   3425     if (s_last_sector_subheader.channel_number == 255 && (!s_mode.xa_filter || s_xa_filter_channel_number != 255))
   3426     {
   3427       WARNING_LOG("Skipping XA file with file number {} and channel number {} (submode 0x{:02X} coding 0x{:02X})",
   3428                   s_last_sector_subheader.file_number, s_last_sector_subheader.channel_number,
   3429                   s_last_sector_subheader.submode.bits, s_last_sector_subheader.codinginfo.bits);
   3430       return;
   3431     }
   3432 
   3433     s_xa_current_file_number = s_last_sector_subheader.file_number;
   3434     s_xa_current_channel_number = s_last_sector_subheader.channel_number;
   3435     s_xa_current_set = true;
   3436   }
   3437   else if (s_last_sector_subheader.file_number != s_xa_current_file_number ||
   3438            s_last_sector_subheader.channel_number != s_xa_current_channel_number)
   3439   {
   3440     DEBUG_LOG("Skipping sector due to current file mismatch (expected {}/{} got {}/{})", s_xa_current_file_number,
   3441               s_xa_current_channel_number, s_last_sector_subheader.file_number, s_last_sector_subheader.channel_number);
   3442     return;
   3443   }
   3444 
   3445   // Reset current file on EOF, and play the file in the next sector.
   3446   if (s_last_sector_subheader.submode.eof)
   3447     ResetCurrentXAFile();
   3448 
   3449   // Ensure the SPU is caught up for the test below.
   3450   SPU::GeneratePendingSamples();
   3451 
   3452   // Since the disc reads and SPU are running at different speeds, we might be _slightly_ behind, which is fine, since
   3453   // the SPU will over-read in the next batch to catch up. We also should not process the sector, because it'll affect
   3454   // the previous samples used for interpolation/ADPCM. Not doing so causes crackling audio in Simple 1500 Series Vol.
   3455   // 92 - The Tozan RPG - Ginrei no Hasha (Japan).
   3456   const u32 num_frames = s_last_sector_subheader.codinginfo.GetSamplesPerSector() >>
   3457                          BoolToUInt8(s_last_sector_subheader.codinginfo.IsStereo());
   3458   if (s_audio_fifo.GetSize() > AUDIO_FIFO_LOW_WATERMARK)
   3459   {
   3460     DEV_LOG("Dropping {} XA frames because audio FIFO still has {} frames", num_frames, s_audio_fifo.GetSize());
   3461     return;
   3462   }
   3463 
   3464   // If muted, we still need to decode the data, to update the previous samples.
   3465   std::array<s16, XA_ADPCM_SAMPLES_PER_SECTOR_4BIT> sample_buffer;
   3466   const u8* xa_block_start =
   3467     raw_sector + CDImage::SECTOR_SYNC_SIZE + sizeof(CDImage::SectorHeader) + sizeof(XASubHeader) * 2;
   3468   s_xa_current_codinginfo.bits = s_last_sector_subheader.codinginfo.bits;
   3469 
   3470   if (s_last_sector_subheader.codinginfo.Is8BitADPCM())
   3471   {
   3472     if (s_last_sector_subheader.codinginfo.IsStereo())
   3473       DecodeXAADPCMChunks<true, true>(xa_block_start, sample_buffer.data());
   3474     else
   3475       DecodeXAADPCMChunks<false, true>(xa_block_start, sample_buffer.data());
   3476   }
   3477   else
   3478   {
   3479     if (s_last_sector_subheader.codinginfo.IsStereo())
   3480       DecodeXAADPCMChunks<true, false>(xa_block_start, sample_buffer.data());
   3481     else
   3482       DecodeXAADPCMChunks<false, false>(xa_block_start, sample_buffer.data());
   3483   }
   3484 
   3485   // Only send to SPU if we're not muted.
   3486   if (s_muted || s_adpcm_muted || g_settings.cdrom_mute_cd_audio)
   3487     return;
   3488 
   3489   if (s_last_sector_subheader.codinginfo.IsStereo())
   3490   {
   3491     if (s_last_sector_subheader.codinginfo.IsHalfSampleRate())
   3492       ResampleXAADPCM18900<true>(sample_buffer.data(), num_frames);
   3493     else
   3494       ResampleXAADPCM<true>(sample_buffer.data(), num_frames);
   3495   }
   3496   else
   3497   {
   3498     if (s_last_sector_subheader.codinginfo.IsHalfSampleRate())
   3499       ResampleXAADPCM18900<false>(sample_buffer.data(), num_frames);
   3500     else
   3501       ResampleXAADPCM<false>(sample_buffer.data(), num_frames);
   3502   }
   3503 }
   3504 
   3505 static s16 GetPeakVolume(const u8* raw_sector, u8 channel)
   3506 {
   3507   static constexpr u32 NUM_SAMPLES = CDImage::RAW_SECTOR_SIZE / sizeof(s16);
   3508 
   3509   static_assert(Common::IsAlignedPow2(NUM_SAMPLES, 8));
   3510   const u8* current_ptr = raw_sector;
   3511   GSVector4i v_peak = GSVector4i::zero();
   3512   for (u32 i = 0; i < NUM_SAMPLES; i += 8)
   3513   {
   3514     v_peak = v_peak.max_i16(GSVector4i::load<false>(current_ptr));
   3515     current_ptr += sizeof(v_peak);
   3516   }
   3517 
   3518   // Convert 16->32bit, removing the unneeded channel.
   3519   if (channel == 0)
   3520     v_peak = v_peak.sll32<16>();
   3521   v_peak = v_peak.sra32<16>();
   3522   return static_cast<s16>(v_peak.maxv_s32());
   3523 }
   3524 
   3525 ALWAYS_INLINE_RELEASE void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq,
   3526                                                     bool subq_valid)
   3527 {
   3528   // For CDDA sectors, the whole sector contains the audio data.
   3529   DEV_LOG("Read sector {} as CDDA", s_current_lba);
   3530 
   3531   // The reporting doesn't happen if we're reading with the CDDA mode bit set.
   3532   if (s_drive_state == DriveState::Playing && s_mode.report_audio && subq_valid)
   3533   {
   3534     const u8 frame_nibble = subq.absolute_frame_bcd >> 4;
   3535 
   3536     if (s_last_cdda_report_frame_nibble != frame_nibble)
   3537     {
   3538       s_last_cdda_report_frame_nibble = frame_nibble;
   3539 
   3540       ClearAsyncInterrupt();
   3541       s_async_response_fifo.Push(s_secondary_status.bits);
   3542       s_async_response_fifo.Push(subq.track_number_bcd);
   3543       s_async_response_fifo.Push(subq.index_number_bcd);
   3544       if (subq.absolute_frame_bcd & 0x10)
   3545       {
   3546         s_async_response_fifo.Push(subq.relative_minute_bcd);
   3547         s_async_response_fifo.Push(0x80 | subq.relative_second_bcd);
   3548         s_async_response_fifo.Push(subq.relative_frame_bcd);
   3549       }
   3550       else
   3551       {
   3552         s_async_response_fifo.Push(subq.absolute_minute_bcd);
   3553         s_async_response_fifo.Push(subq.absolute_second_bcd);
   3554         s_async_response_fifo.Push(subq.absolute_frame_bcd);
   3555       }
   3556 
   3557       const u8 channel = subq.absolute_second_bcd & 1u;
   3558       const s16 peak_volume = std::min<s16>(GetPeakVolume(raw_sector, channel), 32767);
   3559       const u16 peak_value = (ZeroExtend16(channel) << 15) | peak_volume;
   3560 
   3561       s_async_response_fifo.Push(Truncate8(peak_value));      // peak low
   3562       s_async_response_fifo.Push(Truncate8(peak_value >> 8)); // peak high
   3563       SetAsyncInterrupt(Interrupt::DataReady);
   3564 
   3565       DEV_LOG(
   3566         "CDDA report at track[{:02x}] index[{:02x}] rel[{:02x}:{:02x}:{:02x}] abs[{:02x}:{:02x}:{:02x}] peak[{}:{}]",
   3567         subq.track_number_bcd, subq.index_number_bcd, subq.relative_minute_bcd, subq.relative_second_bcd,
   3568         subq.relative_frame_bcd, subq.absolute_minute_bcd, subq.absolute_second_bcd, subq.absolute_frame_bcd, channel,
   3569         peak_volume);
   3570     }
   3571   }
   3572 
   3573   // Apply volume when pushing sectors to SPU.
   3574   if (s_muted || g_settings.cdrom_mute_cd_audio)
   3575     return;
   3576 
   3577   SPU::GeneratePendingSamples();
   3578 
   3579   // 2 samples per channel, always stereo.
   3580   // Apparently in 2X mode, only half the samples in a sector get processed.
   3581   // Test cast: Menu background sound in 360 Three Sixty.
   3582   const u32 num_samples = (CDImage::RAW_SECTOR_SIZE / sizeof(s16)) / (s_mode.double_speed ? 4 : 2);
   3583   const u32 remaining_space = s_audio_fifo.GetSpace();
   3584   if (remaining_space < num_samples)
   3585   {
   3586     WARNING_LOG("Dropping {} frames from audio FIFO", num_samples - remaining_space);
   3587     s_audio_fifo.Remove(num_samples - remaining_space);
   3588   }
   3589 
   3590   const u8* sector_ptr = raw_sector;
   3591   const size_t step = s_mode.double_speed ? (sizeof(s16) * 4) : (sizeof(s16) * 2);
   3592   for (u32 i = 0; i < num_samples; i++)
   3593   {
   3594     s16 samp_left, samp_right;
   3595     std::memcpy(&samp_left, sector_ptr, sizeof(samp_left));
   3596     std::memcpy(&samp_right, sector_ptr + sizeof(s16), sizeof(samp_right));
   3597     sector_ptr += step;
   3598     AddCDAudioFrame(samp_left, samp_right);
   3599   }
   3600 }
   3601 
   3602 void CDROM::ClearSectorBuffers()
   3603 {
   3604   s_current_read_sector_buffer = 0;
   3605   s_current_write_sector_buffer = 0;
   3606 
   3607   for (SectorBuffer& sb : s_sector_buffers)
   3608   {
   3609     sb.position = 0;
   3610     sb.size = 0;
   3611   }
   3612 
   3613   s_request_register.BFRD = false;
   3614   s_status.DRQSTS = false;
   3615 }
   3616 
   3617 void CDROM::CheckForSectorBufferReadComplete()
   3618 {
   3619   SectorBuffer& sb = s_sector_buffers[s_current_read_sector_buffer];
   3620 
   3621   // BFRD gets cleared on DMA completion.
   3622   s_request_register.BFRD = (s_request_register.BFRD && sb.position < sb.size);
   3623   s_status.DRQSTS = s_request_register.BFRD;
   3624 
   3625   // Buffer complete?
   3626   if (sb.position >= sb.size)
   3627   {
   3628     sb.position = 0;
   3629     sb.size = 0;
   3630   }
   3631 
   3632   // Redeliver missed sector on DMA/read complete.
   3633   // This would be the main loop checking when the DMA is complete, if there's another sector pending.
   3634   // Normally, this would happen some time after the DMA actually completes, so we need to put it on a delay.
   3635   // Otherwise, if games read the header then data out as two separate transfers (which is typical), they'll
   3636   // get the header for one sector, and the header for the next in the second transfer.
   3637   SectorBuffer& next_sb = s_sector_buffers[s_current_write_sector_buffer];
   3638   if (next_sb.position == 0 && next_sb.size > 0 && !HasPendingAsyncInterrupt())
   3639   {
   3640     DEV_LOG("Sending additional INT1 for missed sector in buffer {}", s_current_write_sector_buffer);
   3641     s_async_response_fifo.Push(s_secondary_status.bits);
   3642     s_pending_async_interrupt = static_cast<u8>(Interrupt::DataReady);
   3643     s_async_interrupt_event.Schedule(INTERRUPT_DELAY_CYCLES);
   3644   }
   3645 }
   3646 
   3647 void CDROM::CreateFileMap()
   3648 {
   3649   s_file_map.clear();
   3650   s_file_map_created = true;
   3651 
   3652   if (!s_reader.HasMedia())
   3653     return;
   3654 
   3655   s_reader.WaitForIdle();
   3656   CDImage* media = s_reader.GetMedia();
   3657   IsoReader iso;
   3658   if (!iso.Open(media, 1))
   3659   {
   3660     ERROR_LOG("Failed to open ISO filesystem.");
   3661     return;
   3662   }
   3663 
   3664   DEV_LOG("Creating file map for {}...", media->GetFileName());
   3665   s_file_map.emplace(iso.GetPVDLBA(), std::make_pair(iso.GetPVDLBA(), std::string("PVD")));
   3666   CreateFileMap(iso, std::string_view());
   3667   DEV_LOG("Found {} files", s_file_map.size());
   3668 }
   3669 
   3670 void CDROM::CreateFileMap(IsoReader& iso, std::string_view dir)
   3671 {
   3672   for (auto& [path, entry] : iso.GetEntriesInDirectory(dir))
   3673   {
   3674     if (entry.IsDirectory())
   3675     {
   3676       DEV_LOG("{}-{} = {}", entry.location_le, entry.location_le + entry.GetSizeInSectors() - 1, path);
   3677       s_file_map.emplace(entry.location_le, std::make_pair(entry.location_le + entry.GetSizeInSectors() - 1,
   3678                                                            fmt::format("<DIR> {}", path)));
   3679 
   3680       CreateFileMap(iso, path);
   3681       continue;
   3682     }
   3683 
   3684     DEV_LOG("{}-{} = {}", entry.location_le, entry.location_le + entry.GetSizeInSectors() - 1, path);
   3685     s_file_map.emplace(entry.location_le,
   3686                        std::make_pair(entry.location_le + entry.GetSizeInSectors() - 1, std::move(path)));
   3687   }
   3688 }
   3689 
   3690 const std::string* CDROM::LookupFileMap(u32 lba, u32* start_lba, u32* end_lba)
   3691 {
   3692   if (s_file_map.empty())
   3693     return nullptr;
   3694 
   3695   auto iter = s_file_map.lower_bound(lba);
   3696   if (iter == s_file_map.end())
   3697     iter = (++s_file_map.rbegin()).base();
   3698   if (lba < iter->first)
   3699   {
   3700     // before first file
   3701     if (iter == s_file_map.begin())
   3702       return nullptr;
   3703 
   3704     --iter;
   3705   }
   3706   if (lba > iter->second.first)
   3707     return nullptr;
   3708 
   3709   *start_lba = iter->first;
   3710   *end_lba = iter->second.first;
   3711   return &iter->second.second;
   3712 }
   3713 
   3714 void CDROM::DrawDebugWindow()
   3715 {
   3716   static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
   3717   static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
   3718   const float framebuffer_scale = ImGuiManager::GetGlobalScale();
   3719 
   3720   ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 580.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
   3721   if (!ImGui::Begin("CDROM State", nullptr))
   3722   {
   3723     ImGui::End();
   3724     return;
   3725   }
   3726 
   3727   // draw voice states
   3728   if (ImGui::CollapsingHeader("Media", ImGuiTreeNodeFlags_DefaultOpen))
   3729   {
   3730     if (s_reader.HasMedia())
   3731     {
   3732       const CDImage* media = s_reader.GetMedia();
   3733       const CDImage::Position disc_position = CDImage::Position::FromLBA(s_current_lba);
   3734       const float start_y = ImGui::GetCursorPosY();
   3735 
   3736       if (media->HasSubImages())
   3737       {
   3738         ImGui::Text("Filename: %s [Subimage %u of %u] [%u buffered sectors]", media->GetFileName().c_str(),
   3739                     media->GetCurrentSubImage() + 1u, media->GetSubImageCount(), s_reader.GetBufferedSectorCount());
   3740       }
   3741       else
   3742       {
   3743         ImGui::Text("Filename: %s [%u buffered sectors]", media->GetFileName().c_str(),
   3744                     s_reader.GetBufferedSectorCount());
   3745       }
   3746 
   3747       ImGui::Text("Disc Position: MSF[%02u:%02u:%02u] LBA[%u]", disc_position.minute, disc_position.second,
   3748                   disc_position.frame, disc_position.ToLBA());
   3749 
   3750       if (media->GetTrackNumber() > media->GetTrackCount())
   3751       {
   3752         ImGui::Text("Track Position: Lead-out");
   3753       }
   3754       else
   3755       {
   3756         const CDImage::Position track_position = CDImage::Position::FromLBA(
   3757           s_current_lba - media->GetTrackStartPosition(static_cast<u8>(media->GetTrackNumber())));
   3758         ImGui::Text("Track Position: Number[%u] MSF[%02u:%02u:%02u] LBA[%u]", media->GetTrackNumber(),
   3759                     track_position.minute, track_position.second, track_position.frame, track_position.ToLBA());
   3760       }
   3761 
   3762       ImGui::Text("Last Sector: %02X:%02X:%02X (Mode %u)", s_last_sector_header.minute, s_last_sector_header.second,
   3763                   s_last_sector_header.frame, s_last_sector_header.sector_mode);
   3764 
   3765       if (s_show_current_file)
   3766       {
   3767         if (media->GetTrackNumber() == 1)
   3768         {
   3769           if (!s_file_map_created)
   3770             CreateFileMap();
   3771 
   3772           u32 current_file_start_lba, current_file_end_lba;
   3773           const u32 track_lba = s_current_lba - media->GetTrackStartPosition(static_cast<u8>(media->GetTrackNumber()));
   3774           const std::string* current_file = LookupFileMap(track_lba, &current_file_start_lba, &current_file_end_lba);
   3775           if (current_file)
   3776           {
   3777             static constexpr auto readable_size = [](u32 val) {
   3778               // based on
   3779               // https://stackoverflow.com/questions/1449805/how-to-format-a-number-using-comma-as-thousands-separator-in-c
   3780               // don't want to use locale...
   3781               TinyString ret;
   3782               TinyString temp;
   3783               temp.append_format("{}", val);
   3784 
   3785               u32 commas = 2u - (temp.length() % 3u);
   3786               for (const char* p = temp.c_str(); *p != 0u; p++)
   3787               {
   3788                 ret.append(*p);
   3789                 if (commas == 1)
   3790                   ret.append(',');
   3791                 commas = (commas + 1) % 3;
   3792               }
   3793 
   3794               DebugAssert(!ret.empty());
   3795               ret.erase(-1);
   3796               return ret;
   3797             };
   3798             ImGui::Text(
   3799               "Current File: %s (%s of %s bytes)", current_file->c_str(),
   3800               readable_size((track_lba - current_file_start_lba) * CDImage::DATA_SECTOR_SIZE).c_str(),
   3801               readable_size((current_file_end_lba - current_file_start_lba + 1) * CDImage::DATA_SECTOR_SIZE).c_str());
   3802           }
   3803           else
   3804           {
   3805             ImGui::Text("Current File: <Unknown>");
   3806           }
   3807         }
   3808         else
   3809         {
   3810           ImGui::Text("Current File: <Non-Data Track>");
   3811         }
   3812 
   3813         ImGui::SameLine();
   3814         ImGui::Text("[%u files on disc]", static_cast<u32>(s_file_map.size()));
   3815       }
   3816       else
   3817       {
   3818         const float end_y = ImGui::GetCursorPosY();
   3819         ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 120.0f * framebuffer_scale);
   3820         ImGui::SetCursorPosY(start_y);
   3821         if (ImGui::Button("Show Current File"))
   3822           s_show_current_file = true;
   3823 
   3824         ImGui::SetCursorPosY(end_y);
   3825       }
   3826     }
   3827     else
   3828     {
   3829       ImGui::Text("No media inserted.");
   3830     }
   3831   }
   3832 
   3833   if (ImGui::CollapsingHeader("Status/Mode", ImGuiTreeNodeFlags_DefaultOpen))
   3834   {
   3835     ImGui::Columns(3);
   3836 
   3837     ImGui::Text("Status");
   3838     ImGui::NextColumn();
   3839     ImGui::Text("Secondary Status");
   3840     ImGui::NextColumn();
   3841     ImGui::Text("Mode Status");
   3842     ImGui::NextColumn();
   3843 
   3844     ImGui::TextColored(s_status.ADPBUSY ? active_color : inactive_color, "ADPBUSY: %s",
   3845                        s_status.ADPBUSY ? "Yes" : "No");
   3846     ImGui::NextColumn();
   3847     ImGui::TextColored(s_secondary_status.error ? active_color : inactive_color, "Error: %s",
   3848                        s_secondary_status.error ? "Yes" : "No");
   3849     ImGui::NextColumn();
   3850     ImGui::TextColored(s_mode.cdda ? active_color : inactive_color, "CDDA: %s", s_mode.cdda ? "Yes" : "No");
   3851     ImGui::NextColumn();
   3852 
   3853     ImGui::TextColored(s_status.PRMEMPTY ? active_color : inactive_color, "PRMEMPTY: %s",
   3854                        s_status.PRMEMPTY ? "Yes" : "No");
   3855     ImGui::NextColumn();
   3856     ImGui::TextColored(s_secondary_status.motor_on ? active_color : inactive_color, "Motor On: %s",
   3857                        s_secondary_status.motor_on ? "Yes" : "No");
   3858     ImGui::NextColumn();
   3859     ImGui::TextColored(s_mode.auto_pause ? active_color : inactive_color, "Auto Pause: %s",
   3860                        s_mode.auto_pause ? "Yes" : "No");
   3861     ImGui::NextColumn();
   3862 
   3863     ImGui::TextColored(s_status.PRMWRDY ? active_color : inactive_color, "PRMWRDY: %s",
   3864                        s_status.PRMWRDY ? "Yes" : "No");
   3865     ImGui::NextColumn();
   3866     ImGui::TextColored(s_secondary_status.seek_error ? active_color : inactive_color, "Seek Error: %s",
   3867                        s_secondary_status.seek_error ? "Yes" : "No");
   3868     ImGui::NextColumn();
   3869     ImGui::TextColored(s_mode.report_audio ? active_color : inactive_color, "Report Audio: %s",
   3870                        s_mode.report_audio ? "Yes" : "No");
   3871     ImGui::NextColumn();
   3872 
   3873     ImGui::TextColored(s_status.RSLRRDY ? active_color : inactive_color, "RSLRRDY: %s",
   3874                        s_status.RSLRRDY ? "Yes" : "No");
   3875     ImGui::NextColumn();
   3876     ImGui::TextColored(s_secondary_status.id_error ? active_color : inactive_color, "ID Error: %s",
   3877                        s_secondary_status.id_error ? "Yes" : "No");
   3878     ImGui::NextColumn();
   3879     ImGui::TextColored(s_mode.xa_filter ? active_color : inactive_color, "XA Filter: %s (File %u Channel %u)",
   3880                        s_mode.xa_filter ? "Yes" : "No", s_xa_filter_file_number, s_xa_filter_channel_number);
   3881     ImGui::NextColumn();
   3882 
   3883     ImGui::TextColored(s_status.DRQSTS ? active_color : inactive_color, "DRQSTS: %s", s_status.DRQSTS ? "Yes" : "No");
   3884     ImGui::NextColumn();
   3885     ImGui::TextColored(s_secondary_status.shell_open ? active_color : inactive_color, "Shell Open: %s",
   3886                        s_secondary_status.shell_open ? "Yes" : "No");
   3887     ImGui::NextColumn();
   3888     ImGui::TextColored(s_mode.ignore_bit ? active_color : inactive_color, "Ignore Bit: %s",
   3889                        s_mode.ignore_bit ? "Yes" : "No");
   3890     ImGui::NextColumn();
   3891 
   3892     ImGui::TextColored(s_status.BUSYSTS ? active_color : inactive_color, "BUSYSTS: %s",
   3893                        s_status.BUSYSTS ? "Yes" : "No");
   3894     ImGui::NextColumn();
   3895     ImGui::TextColored(s_secondary_status.reading ? active_color : inactive_color, "Reading: %s",
   3896                        s_secondary_status.reading ? "Yes" : "No");
   3897     ImGui::NextColumn();
   3898     ImGui::TextColored(s_mode.read_raw_sector ? active_color : inactive_color, "Read Raw Sectors: %s",
   3899                        s_mode.read_raw_sector ? "Yes" : "No");
   3900     ImGui::NextColumn();
   3901 
   3902     ImGui::NextColumn();
   3903     ImGui::TextColored(s_secondary_status.seeking ? active_color : inactive_color, "Seeking: %s",
   3904                        s_secondary_status.seeking ? "Yes" : "No");
   3905     ImGui::NextColumn();
   3906     ImGui::TextColored(s_mode.xa_enable ? active_color : inactive_color, "XA Enable: %s",
   3907                        s_mode.xa_enable ? "Yes" : "No");
   3908     ImGui::NextColumn();
   3909 
   3910     ImGui::NextColumn();
   3911     ImGui::TextColored(s_secondary_status.playing_cdda ? active_color : inactive_color, "Playing CDDA: %s",
   3912                        s_secondary_status.playing_cdda ? "Yes" : "No");
   3913     ImGui::NextColumn();
   3914     ImGui::TextColored(s_mode.double_speed ? active_color : inactive_color, "Double Speed: %s",
   3915                        s_mode.double_speed ? "Yes" : "No");
   3916     ImGui::NextColumn();
   3917 
   3918     ImGui::Columns(1);
   3919     ImGui::NewLine();
   3920 
   3921     if (HasPendingCommand())
   3922     {
   3923       ImGui::TextColored(active_color, "Command: %s (0x%02X) (%d ticks remaining)",
   3924                          s_command_info[static_cast<u8>(s_command)].name, static_cast<u8>(s_command),
   3925                          s_command_event.IsActive() ? s_command_event.GetTicksUntilNextExecution() : 0);
   3926     }
   3927     else
   3928     {
   3929       ImGui::TextColored(inactive_color, "Command: None");
   3930     }
   3931 
   3932     if (IsDriveIdle())
   3933     {
   3934       ImGui::TextColored(inactive_color, "Drive: Idle");
   3935     }
   3936     else
   3937     {
   3938       ImGui::TextColored(active_color, "Drive: %s (%d ticks remaining)",
   3939                          s_drive_state_names[static_cast<u8>(s_drive_state)],
   3940                          s_drive_event.IsActive() ? s_drive_event.GetTicksUntilNextExecution() : 0);
   3941     }
   3942 
   3943     ImGui::Text("Interrupt Enable Register: 0x%02X", s_interrupt_enable_register);
   3944     ImGui::Text("Interrupt Flag Register: 0x%02X", s_interrupt_flag_register);
   3945 
   3946     if (HasPendingAsyncInterrupt())
   3947     {
   3948       ImGui::SameLine();
   3949       ImGui::TextColored(inactive_color, " (0x%02X pending)", s_pending_async_interrupt);
   3950     }
   3951   }
   3952 
   3953   if (ImGui::CollapsingHeader("CD Audio", ImGuiTreeNodeFlags_DefaultOpen))
   3954   {
   3955     if (s_drive_state == DriveState::Reading && s_mode.xa_enable)
   3956     {
   3957       ImGui::TextColored(active_color, "Playing: XA-ADPCM (File %u | Channel %u | %s | %s | %s)",
   3958                          s_xa_current_file_number, s_xa_current_channel_number,
   3959                          s_xa_current_codinginfo.IsStereo() ? "Stereo" : "Mono",
   3960                          s_xa_current_codinginfo.Is8BitADPCM() ? "8-bit" : "4-bit",
   3961                          s_xa_current_codinginfo.IsHalfSampleRate() ? "18900hz" : "37800hz");
   3962     }
   3963     else if (s_drive_state == DriveState::Playing)
   3964     {
   3965       ImGui::TextColored(active_color, "Playing: CDDA (Track %x)", s_last_subq.track_number_bcd);
   3966     }
   3967     else
   3968     {
   3969       ImGui::TextColored(inactive_color, "Playing: Inactive");
   3970     }
   3971 
   3972     ImGui::TextColored(s_muted ? inactive_color : active_color, "Muted: %s", s_muted ? "Yes" : "No");
   3973     ImGui::Text("Left Output: Left Channel=%02X (%u%%), Right Channel=%02X (%u%%)", s_cd_audio_volume_matrix[0][0],
   3974                 ZeroExtend32(s_cd_audio_volume_matrix[0][0]) * 100 / 0x80, s_cd_audio_volume_matrix[1][0],
   3975                 ZeroExtend32(s_cd_audio_volume_matrix[1][0]) * 100 / 0x80);
   3976     ImGui::Text("Right Output: Left Channel=%02X (%u%%), Right Channel=%02X (%u%%)", s_cd_audio_volume_matrix[0][1],
   3977                 ZeroExtend32(s_cd_audio_volume_matrix[0][1]) * 100 / 0x80, s_cd_audio_volume_matrix[1][1],
   3978                 ZeroExtend32(s_cd_audio_volume_matrix[1][1]) * 100 / 0x80);
   3979 
   3980     ImGui::Text("Audio FIFO Size: %u frames", s_audio_fifo.GetSize());
   3981   }
   3982 
   3983   ImGui::End();
   3984 }