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(¤t_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, ¤t_file_start_lba, ¤t_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 }