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