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