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