rework input code
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
CommitLineData
ef79bbde
P
1/***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
22bbabf6 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
ef79bbde
P
18 ***************************************************************************/
19
22bbabf6 20/*
ef79bbde
P
21* Handles all CD-ROM registers and functions.
22*/
23
20a88ae9 24#include <stdalign.h>
752c1c85 25#include <assert.h>
ef79bbde 26#include "cdrom.h"
08d9d257 27#include "cdrom-async.h"
de74f599 28#include "misc.h"
ef79bbde 29#include "ppf.h"
9f8b032d 30#include "psxdma.h"
9a0a61d2 31#include "psxevents.h"
8f2bb0cb 32#include "arm_features.h"
ef79bbde 33
8b380e97 34/* logging */
35#if 0
36#define CDR_LOG SysPrintf
37#else
38#define CDR_LOG(...)
39#endif
40#if 0
41#define CDR_LOG_I SysPrintf
42#else
10b9bf1e 43#define CDR_LOG_I(fmt, ...) \
44 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
8b380e97 45#endif
46#if 0
47#define CDR_LOG_IO SysPrintf
48#else
49#define CDR_LOG_IO(...)
50#endif
49cebb24 51//#define CDR_LOG_CMD
52//#define CDR_LOG_CMD_ACK
8b380e97 53
af93c8be 54static struct {
7f2576b2 55 // unused members maintain savesate compatibility
56 unsigned char unused0;
57 unsigned char unused1;
0b988c8b 58 unsigned char IrqMask;
7f2576b2 59 unsigned char unused2;
af93c8be 60 unsigned char Ctrl;
0b988c8b 61 unsigned char IrqStat;
af93c8be 62
63 unsigned char StatP;
64
65 unsigned char Transfer[DATA_SIZE];
66 struct {
67 unsigned char Track;
68 unsigned char Index;
69 unsigned char Relative[3];
70 unsigned char Absolute[3];
71 } subq;
72 unsigned char TrackChanged;
a6e03490 73 unsigned char ReportDelay;
e0216409 74 unsigned char PhysCdPropagations;
b9a092f5 75 unsigned short sectorsRead;
af93c8be 76 unsigned int freeze_ver;
77
78 unsigned char Prev[4];
79 unsigned char Param[8];
80 unsigned char Result[16];
81
82 unsigned char ParamC;
83 unsigned char ParamP;
84 unsigned char ResultC;
85 unsigned char ResultP;
86 unsigned char ResultReady;
87 unsigned char Cmd;
752c1c85 88 unsigned char SubqForwardSectors;
af93c8be 89 unsigned char SetlocPending;
90 u32 Reading;
91
92 unsigned char ResultTN[6];
93 unsigned char ResultTD[4];
94 unsigned char SetSectorPlay[4];
95 unsigned char SetSectorEnd[4];
96 unsigned char SetSector[4];
97 unsigned char Track;
98 boolean Play, Muted;
99 int CurTrack;
5c5e6c0c 100 unsigned char Mode;
101 unsigned char FileChannelSelected;
102 unsigned char CurFile, CurChannel;
103 int FilterFile, FilterChannel;
70c1043e 104 unsigned char LocL[8];
5c5e6c0c 105 int unused4;
af93c8be 106
107 xa_decode_t Xa;
108
742a21f2 109 u16 FifoOffset;
110 u16 FifoSize;
af93c8be 111
7f2576b2 112 u16 CmdInProgress;
f5450cfb 113 u8 Irq1Pending;
5c5e6c0c 114 u8 AdpcmActive;
e7606d0e 115 u32 LastReadSeekCycles;
af93c8be 116
0aa7361c 117 u8 RetryDetected;
af93c8be 118
cc1e8bd4 119 u8 DriveState; // enum drive_state
af93c8be 120 u8 FastForward;
121 u8 FastBackward;
4f38d1f8 122 u8 errorRetryhack;
af93c8be 123
124 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
125 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
126 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
127 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
128} cdr;
20a88ae9 129alignas(64) static s16 read_buf[CD_FRAMESIZE_RAW_ALIGNED / 2];
ef79bbde 130
08d9d257 131struct SubQ {
132 char res0[12];
133 unsigned char ControlAndADR;
134 unsigned char TrackNumber;
135 unsigned char IndexNumber;
136 unsigned char TrackRelativeAddress[3];
137 unsigned char Filler;
138 unsigned char AbsoluteAddress[3];
139 unsigned char CRC[2];
140 char res1[72];
141};
142
ef79bbde 143/* CD-ROM magic numbers */
1f035e27 144#define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
ef79bbde
P
145#define CdlNop 1
146#define CdlSetloc 2
147#define CdlPlay 3
148#define CdlForward 4
149#define CdlBackward 5
150#define CdlReadN 6
151#define CdlStandby 7
152#define CdlStop 8
153#define CdlPause 9
b0bd140d 154#define CdlReset 10
ef79bbde
P
155#define CdlMute 11
156#define CdlDemute 12
157#define CdlSetfilter 13
158#define CdlSetmode 14
dcd72441 159#define CdlGetparam 15
ef79bbde
P
160#define CdlGetlocL 16
161#define CdlGetlocP 17
162#define CdlReadT 18
163#define CdlGetTN 19
164#define CdlGetTD 20
165#define CdlSeekL 21
166#define CdlSeekP 22
167#define CdlSetclock 23
168#define CdlGetclock 24
169#define CdlTest 25
170#define CdlID 26
171#define CdlReadS 27
b0bd140d 172#define CdlInit 28
e4268a49 173#define CdlGetQ 29
ef79bbde
P
174#define CdlReadToc 30
175
49cebb24 176#ifdef CDR_LOG_CMD
af93c8be 177static const char * const CmdName[0x100] = {
ef79bbde
P
178 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
179 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
b0bd140d 180 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
dcd72441 181 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
ef79bbde
P
182 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
183 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
184 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
b0bd140d 185 "CdlInit", NULL, "CDlReadToc", NULL
ef79bbde 186};
af93c8be 187#endif
ef79bbde
P
188
189unsigned char Test04[] = { 0 };
190unsigned char Test05[] = { 0 };
191unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
192unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
193unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
194
0b988c8b 195// cdr.IrqStat:
9f8b032d 196#define NoIntr 0
197#define DataReady 1
198#define Complete 2
199#define Acknowledge 3
200#define DataEnd 4
201#define DiskError 5
202
203/* Modes flags */
204#define MODE_SPEED (1<<7) // 0x80
205#define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
206#define MODE_SIZE_2340 (1<<5) // 0x20
49cebb24 207#define MODE_SIZE_2328 (1<<4) // 0x10 (likely wrong)
cd0c9f5b 208#define MODE_SIZE_2048 (0<<4) // 0x00
49cebb24 209#define MODE_BIT4 (1<<4) // 0x10
9f8b032d 210#define MODE_SF (1<<3) // 0x08 channel on/off
211#define MODE_REPORT (1<<2) // 0x04
212#define MODE_AUTOPAUSE (1<<1) // 0x02
213#define MODE_CDDA (1<<0) // 0x01
214
215/* Status flags */
216#define STATUS_PLAY (1<<7) // 0x80
217#define STATUS_SEEK (1<<6) // 0x40
218#define STATUS_READ (1<<5) // 0x20
219#define STATUS_SHELLOPEN (1<<4) // 0x10
220#define STATUS_UNKNOWN3 (1<<3) // 0x08
4a6b26b8 221#define STATUS_SEEKERROR (1<<2) // 0x04
9f8b032d 222#define STATUS_ROTATING (1<<1) // 0x02
223#define STATUS_ERROR (1<<0) // 0x01
224
e4268a49 225/* Errors */
003cfc63 226#define ERROR_NOTREADY (1<<7) // 0x80
e4268a49 227#define ERROR_INVALIDCMD (1<<6) // 0x40
0b988c8b 228#define ERROR_BAD_ARGNUM (1<<5) // 0x20
229#define ERROR_BAD_ARGVAL (1<<4) // 0x10
4a6b26b8 230#define ERROR_SHELLOPEN (1<<3) // 0x08
9f8b032d 231
ef79bbde
P
232// 1x = 75 sectors per second
233// PSXCLK = 1 sec in the ps
234// so (PSXCLK / 75) = cdr read time (linuzappz)
235#define cdReadTime (PSXCLK / 75)
236
70c1043e 237#define LOCL_INVALID 0xff
752c1c85 238#define SUBQ_FORWARD_SECTORS 2u
70c1043e 239
80f91a20 240enum drive_state {
cc1e8bd4 241 DRIVESTATE_STANDBY = 0, // different from paused
80f91a20 242 DRIVESTATE_LID_OPEN,
243 DRIVESTATE_RESCAN_CD,
244 DRIVESTATE_PREPARE_CD,
e4268a49 245 DRIVESTATE_STOPPED,
cc1e8bd4 246 DRIVESTATE_PAUSED,
247 DRIVESTATE_PLAY_READ,
248 DRIVESTATE_SEEK,
80f91a20 249};
250
38c77d78 251static struct CdrStat cdr_stat;
ef79bbde 252
fffad32e 253static unsigned int msf2sec(const u8 *msf) {
9f8b032d 254 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
255}
256
480e570b 257// cdrPlayReadInterrupt
258#define CDRPLAYREAD_INT(eCycle, isFirst) { \
df717ca9 259 u32 e_ = eCycle; \
d28b54b1 260 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
df717ca9 261 if (isFirst) \
262 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
263 else \
264 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
265 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
9a0a61d2 266 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
9f8b032d 267}
268
ef79bbde 269#define StopReading() { \
f522e63c 270 cdr.Reading = 0; \
271 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
ef79bbde
P
272}
273
274#define StopCdda() { \
f522e63c 275 cdr.Play = FALSE; \
276 cdr.FastForward = 0; \
277 cdr.FastBackward = 0; \
278}
279
280#define SetPlaySeekRead(x, f) { \
281 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
282 x |= f; \
ef79bbde
P
283}
284
0b988c8b 285#define SetResultSize_(size) { \
94c118c3 286 cdr.ResultP = 0; \
ef79bbde
P
287 cdr.ResultC = size; \
288 cdr.ResultReady = 1; \
289}
290
0b988c8b 291#define SetResultSize(size) { \
292 if (cdr.ResultP < cdr.ResultC) \
293 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
294 SetResultSize_(size); \
295}
296
297static void setIrq(u8 irq, int log_cmd)
94c118c3 298{
0b988c8b 299 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
300 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
301
302 cdr.IrqStat = irq;
303 if ((old ^ new_) & new_)
94c118c3 304 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
af93c8be 305
49cebb24 306#ifdef CDR_LOG_CMD
0b988c8b 307 if (cdr.IrqStat)
af93c8be 308 {
309 int i;
0b988c8b 310 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
311 !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
af93c8be 312 for (i = 0; i < cdr.ResultC; i++)
313 SysPrintf("%02x ", cdr.Result[i]);
314 SysPrintf("\n");
315 }
316#endif
94c118c3 317}
9f8b032d 318
80f91a20 319// timing used in this function was taken from tests on real hardware
320// (yes it's slow, but you probably don't want to modify it)
d9a02493 321void cdrLidSeekInterrupt(void)
9f8b032d 322{
10b9bf1e 323 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
871bedc6 324
80f91a20 325 switch (cdr.DriveState) {
326 default:
327 case DRIVESTATE_STANDBY:
f522e63c 328 StopCdda();
4a6b26b8 329 //StopReading();
f522e63c 330 SetPlaySeekRead(cdr.StatP, 0);
9f8b032d 331
08d9d257 332 if (cdra_getStatus(&cdr_stat) == -1)
80f91a20 333 return;
9f8b032d 334
38c77d78 335 if (cdr_stat.Status & STATUS_SHELLOPEN)
9f8b032d 336 {
4d311cb7 337 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
80f91a20 338 cdr.DriveState = DRIVESTATE_LID_OPEN;
9a0a61d2 339 set_event(PSXINT_CDRLID, 0x800);
80f91a20 340 }
341 break;
9f8b032d 342
80f91a20 343 case DRIVESTATE_LID_OPEN:
08d9d257 344 if (cdra_getStatus(&cdr_stat) != 0)
38c77d78 345 cdr_stat.Status &= ~STATUS_SHELLOPEN;
9f8b032d 346
80f91a20 347 // 02, 12, 10
348 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
0123002e 349 int was_reading = cdr.Reading;
8a20e961 350 StopReading();
4a6b26b8 351 SetPlaySeekRead(cdr.StatP, 0);
80f91a20 352 cdr.StatP |= STATUS_SHELLOPEN;
0123002e 353 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
9f8b032d 354
4a6b26b8 355 // IIRC this sometimes doesn't happen on real hw
356 // (when lots of commands are sent?)
8a20e961 357 SetResultSize(2);
358 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
359 cdr.Result[1] = ERROR_SHELLOPEN;
0123002e 360 if (cdr.CmdInProgress || was_reading) {
4a6b26b8 361 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
362 cdr.CmdInProgress = 0;
4a6b26b8 363 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
364 cdr.Result[1] = ERROR_NOTREADY;
4a6b26b8 365 }
8a20e961 366 setIrq(DiskError, 0x1006);
9f8b032d 367
9a0a61d2 368 set_event(PSXINT_CDRLID, cdReadTime * 30);
80f91a20 369 break;
9f8b032d 370 }
80f91a20 371 else if (cdr.StatP & STATUS_ROTATING) {
372 cdr.StatP &= ~STATUS_ROTATING;
373 }
38c77d78 374 else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) {
80f91a20 375 // closed now
376 CheckCdrom();
9f8b032d 377
80f91a20 378 // cdr.StatP STATUS_SHELLOPEN is "sticky"
379 // and is only cleared by CdlNop
9f8b032d 380
80f91a20 381 cdr.DriveState = DRIVESTATE_RESCAN_CD;
9a0a61d2 382 set_event(PSXINT_CDRLID, cdReadTime * 105);
80f91a20 383 break;
384 }
9f8b032d 385
80f91a20 386 // recheck for close
9a0a61d2 387 set_event(PSXINT_CDRLID, cdReadTime * 3);
80f91a20 388 break;
9f8b032d 389
80f91a20 390 case DRIVESTATE_RESCAN_CD:
391 cdr.StatP |= STATUS_ROTATING;
392 cdr.DriveState = DRIVESTATE_PREPARE_CD;
9f8b032d 393
80f91a20 394 // this is very long on real hardware, over 6 seconds
395 // make it a bit faster here...
9a0a61d2 396 set_event(PSXINT_CDRLID, cdReadTime * 150);
80f91a20 397 break;
9f8b032d 398
80f91a20 399 case DRIVESTATE_PREPARE_CD:
871bedc6 400 if (cdr.StatP & STATUS_SEEK) {
401 SetPlaySeekRead(cdr.StatP, 0);
402 cdr.DriveState = DRIVESTATE_STANDBY;
403 }
404 else {
405 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
9a0a61d2 406 set_event(PSXINT_CDRLID, cdReadTime * 26);
871bedc6 407 }
80f91a20 408 break;
9f8b032d 409 }
410}
411
fffad32e 412static void Find_CurTrack(const u8 *time)
413{
414 int current, sect;
9f8b032d 415
fffad32e 416 current = msf2sec(time);
9f8b032d 417
fffad32e 418 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
08d9d257 419 cdra_getTD(cdr.CurTrack + 1, cdr.ResultTD);
420 sect = msf2sec(cdr.ResultTD);
fffad32e 421 if (sect - current >= 150)
9f8b032d 422 break;
9f8b032d 423 }
424}
425
73b29eeb 426static void generate_subq(const u8 *time)
427{
428 unsigned char start[3], next[3];
940f8acc 429 int this_s, start_s, next_s, pregap;
73b29eeb 430 int relative_s;
431
940f8acc 432 if (cdr.CurTrack <= cdr.ResultTN[1])
433 cdra_getTD(cdr.CurTrack, start);
434 else
435 memcpy(start, cdr.SetSectorEnd, 3);
73b29eeb 436 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
437 pregap = 150;
08d9d257 438 cdra_getTD(cdr.CurTrack + 1, next);
73b29eeb 439 }
440 else {
441 // last track - cd size
442 pregap = 0;
08d9d257 443 memcpy(next, cdr.SetSectorEnd, 3);
73b29eeb 444 }
445
446 this_s = msf2sec(time);
08d9d257 447 start_s = msf2sec(start);
448 next_s = msf2sec(next);
73b29eeb 449
450 cdr.TrackChanged = FALSE;
451
940f8acc 452 if (next_s - this_s < pregap && cdr.CurTrack <= cdr.ResultTN[1]) {
73b29eeb 453 cdr.TrackChanged = TRUE;
454 cdr.CurTrack++;
455 start_s = next_s;
456 }
457
458 cdr.subq.Index = 1;
459
460 relative_s = this_s - start_s;
461 if (relative_s < 0) {
462 cdr.subq.Index = 0;
463 relative_s = -relative_s;
464 }
08d9d257 465 lba2msf(relative_s, &cdr.subq.Relative[0],
466 &cdr.subq.Relative[1], &cdr.subq.Relative[2]);
73b29eeb 467
468 cdr.subq.Track = itob(cdr.CurTrack);
940f8acc 469 if (cdr.CurTrack > cdr.ResultTN[1]) // lead-out
470 cdr.subq.Track = 0xaa;
73b29eeb 471 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
472 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
473 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
474 cdr.subq.Absolute[0] = itob(time[0]);
475 cdr.subq.Absolute[1] = itob(time[1]);
476 cdr.subq.Absolute[2] = itob(time[2]);
477}
478
752c1c85 479static int ReadTrack(const u8 *time)
480{
08d9d257 481 int ret;
ef79bbde 482
49cebb24 483 CDR_LOG("ReadTrack *** %02d:%02d:%02d\n", time[0], time[1], time[2]);
ef79bbde 484
08d9d257 485 if (memcmp(cdr.Prev, time, 3) == 0)
70c1043e 486 return 1;
39df67df 487
08d9d257 488 ret = cdra_readTrack(time);
fb2e957d 489 if (ret == 0)
08d9d257 490 memcpy(cdr.Prev, time, 3);
49cebb24 491 else
492 log_unhandled("ReadTrack %02d:%02d:%02d ret %d\n",
493 time[0], time[1], time[2], ret);
08d9d257 494 return ret == 0;
752c1c85 495}
496
497static void UpdateSubq(const u8 *time)
498{
08d9d257 499 int ret = -1, s = MSF2SECT(time[0], time[1], time[2]);
500 struct SubQ subq;
752c1c85 501 u16 crc;
9f8b032d 502
f3746eea 503 if (CheckSBI(s))
752c1c85 504 return;
9f8b032d 505
08d9d257 506 if (cdr.CurTrack == 1)
507 ret = cdra_readSub(time, &subq);
508 if (ret == 0) {
509 crc = calcCrc((u8 *)&subq + 12, 10);
510 if (crc == (((u16)subq.CRC[0] << 8) | subq.CRC[1])) {
511 cdr.subq.Track = subq.TrackNumber;
512 cdr.subq.Index = subq.IndexNumber;
513 memcpy(cdr.subq.Relative, subq.TrackRelativeAddress, 3);
514 memcpy(cdr.subq.Absolute, subq.AbsoluteAddress, 3);
fffad32e 515 }
92ca1ba6 516 else {
752c1c85 517 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
518 time[0], time[1], time[2]);
92ca1ba6 519 }
9f8b032d 520 }
fffad32e 521 else {
73b29eeb 522 generate_subq(time);
9f8b032d 523 }
9f8b032d 524
fffad32e 525 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
526 cdr.subq.Track, cdr.subq.Index,
527 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
528 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
529}
9f8b032d 530
7f457614 531static void cdrPlayInterrupt_Autopause()
532{
bf4df3ba 533 u32 abs_lev_max = 0;
534 boolean abs_lev_chselect;
535 u32 i;
f4627eb3 536
fffad32e 537 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
9930d23d 538 CDR_LOG_I("autopause\n");
7f457614 539
9930d23d 540 SetResultSize(1);
541 cdr.Result[0] = cdr.StatP;
0b988c8b 542 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
7f457614 543
544 StopCdda();
f522e63c 545 SetPlaySeekRead(cdr.StatP, 0);
cc1e8bd4 546 cdr.DriveState = DRIVESTATE_PAUSED;
7f457614 547 }
a6e03490 548 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
549 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
550 {
9930d23d 551 SetResultSize(8);
fffad32e 552 cdr.Result[0] = cdr.StatP;
553 cdr.Result[1] = cdr.subq.Track;
554 cdr.Result[2] = cdr.subq.Index;
bf4df3ba 555
556 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
557
558 /* 8 is a hack. For accuracy, it should be 588. */
559 for (i = 0; i < 8; i++)
560 {
561 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
562 }
563 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
564 abs_lev_max |= abs_lev_chselect << 15;
9b470ab7 565
fffad32e 566 if (cdr.subq.Absolute[2] & 0x10) {
567 cdr.Result[3] = cdr.subq.Relative[0];
568 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
569 cdr.Result[5] = cdr.subq.Relative[2];
7f457614 570 }
fffad32e 571 else {
572 cdr.Result[3] = cdr.subq.Absolute[0];
573 cdr.Result[4] = cdr.subq.Absolute[1];
574 cdr.Result[5] = cdr.subq.Absolute[2];
575 }
bf4df3ba 576 cdr.Result[6] = abs_lev_max >> 0;
577 cdr.Result[7] = abs_lev_max >> 8;
7f457614 578
0b988c8b 579 setIrq(DataReady, 0x1001);
7f457614 580 }
a6e03490 581
582 if (cdr.ReportDelay)
583 cdr.ReportDelay--;
7f457614 584}
585
0aa7361c 586static boolean canDoTurbo(void)
587{
588 u32 c = psxRegs.cycle;
589 return Config.TurboCD && !cdr.RetryDetected && !cdr.AdpcmActive
590 //&& c - psxRegs.intCycle[PSXINT_SPUDMA].sCycle > (u32)cdReadTime * 2
591 && c - psxRegs.intCycle[PSXINT_MDECOUTDMA].sCycle > (u32)cdReadTime * 16;
592}
593
f522e63c 594static int cdrSeekTime(unsigned char *target)
595{
11d2721a 596 int diff = abs((int)msf2sec(cdr.SetSectorPlay) - (int)msf2sec(target));
597 int seekTime = diff * (cdReadTime / 2000);
6ca445e2 598 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
4b4164bb 599 seekTime = MAX_VALUE(seekTime, 20000);
600
11d2721a 601 // sled seek?
602 if (diff >= 7200)
603 seekTime = PSXCLK / 7 + diff * 64;
604 // add *something* as rotation time until the target sector
605 if (cyclesSinceRS >= cdReadTime)
606 seekTime += (8 - ((cyclesSinceRS >> 18) & 7)) * (cdReadTime / 2);
607
6ca445e2 608 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
609 // and then wants some slack time
4c8f1c25 610 if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
6ca445e2 611 seekTime += cdReadTime;
4b4164bb 612
11d2721a 613 //seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
614 CDR_LOG("seek: %02d:%02d:%02d %.2f %.2f (%.2f) st %d di %d\n",
615 target[0], target[1], target[2], (float)seekTime / PSXCLK,
cc1e8bd4 616 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
4c8f1c25 617 cdr.DriveState, diff);
f522e63c 618 return seekTime;
619}
620
4134c627 621static u32 cdrAlignTimingHack(u32 cycles)
622{
623 /*
624 * timing hack for T'ai Fu - Wrath of the Tiger:
625 * The game has a bug where it issues some cdc commands from a low priority
626 * vint handler, however there is a higher priority default bios handler
627 * that acks the vint irq and returns, so game's handler is not reached
628 * (see bios irq handler chains at e004 and the game's irq handling func
629 * at 80036810). For the game to work, vint has to arrive after the bios
630 * vint handler rejects some other irq (of which only cd and rcnt2 are
631 * active), but before the game's handler loop reads I_STAT. The time
632 * window for this is quite small (~1k cycles of so). Apparently this
633 * somehow happens naturally on the real hardware.
688bdb95 634 *
635 * Note: always enforcing this breaks other games like Crash PAL version
636 * (inputs get dropped because bios handler doesn't see interrupts).
4134c627 637 */
e2101d5b 638 u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
4134c627 639 while ((s32)(vint_rel - cycles) < 0)
640 vint_rel += PSXCLK / 60;
641 return vint_rel;
642}
643
38e03f0a 644static void cdrUpdateTransferBuf(const u8 *buf);
d9a02493 645static void cdrReadInterrupt(void);
4ad17db3 646static void cdrPrepCdda(s16 *buf, int samples);
d9a02493 647
752c1c85 648static void msfiAdd(u8 *msfi, u32 count)
649{
650 assert(count < 75);
651 msfi[2] += count;
652 if (msfi[2] >= 75) {
653 msfi[2] -= 75;
654 msfi[1]++;
655 if (msfi[1] == 60) {
656 msfi[1] = 0;
657 msfi[0]++;
658 }
659 }
660}
661
b9a092f5 662static void msfiSub(u8 *msfi, u32 count)
663{
664 assert(count < 75);
665 msfi[2] -= count;
666 if ((s8)msfi[2] < 0) {
667 msfi[2] += 75;
668 msfi[1]--;
669 if ((s8)msfi[1] < 0) {
670 msfi[1] = 60;
671 msfi[0]--;
672 }
673 }
674}
675
0aa7361c 676static int msfiEq(const u8 *a, const u8 *b)
677{
678 return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
679}
680
480e570b 681void cdrPlayReadInterrupt(void)
7f457614 682{
12882b5a 683 // this works but causes instability for timing sensitive games
684#if 0
08d9d257 685 int hit = cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
bdc1bbb5 686 if (!hit && cdr.PhysCdPropagations < 75/2) {
687 // this propagates the real cdrom delays to the emulated game
e0216409 688 CDRPLAYREAD_INT(cdReadTime / 2, 0);
bdc1bbb5 689 cdr.PhysCdPropagations++;
e0216409 690 return;
691 }
12882b5a 692#endif
e7606d0e 693 cdr.LastReadSeekCycles = psxRegs.cycle;
4b4164bb 694
d9a02493 695 if (cdr.Reading) {
696 cdrReadInterrupt();
697 return;
698 }
699
39df67df 700 if (!cdr.Play) return;
7f457614 701
9930d23d 702 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
703 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
8b380e97 704
cc1e8bd4 705 cdr.DriveState = DRIVESTATE_PLAY_READ;
d9a02493 706 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
fffad32e 707 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
9930d23d 708 CDR_LOG_I("end stop\n");
fffad32e 709 StopCdda();
f522e63c 710 SetPlaySeekRead(cdr.StatP, 0);
fffad32e 711 cdr.TrackChanged = TRUE;
cc1e8bd4 712 cdr.DriveState = DRIVESTATE_PAUSED;
fffad32e 713 }
ffd2bcfa 714 else {
08d9d257 715 cdra_readCDDA(cdr.SetSectorPlay, read_buf);
ffd2bcfa 716 }
7f457614 717
0b988c8b 718 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
7f457614 719 cdrPlayInterrupt_Autopause();
720
38b8a211 721 if (cdr.Play && !Config.Cdda) {
4ad17db3 722 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
5c5e6c0c 723 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
f4627eb3 724 }
fffad32e 725
752c1c85 726 msfiAdd(cdr.SetSectorPlay, 1);
240e5daa 727 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 1);
7f457614 728
fffad32e 729 // update for CdlGetlocP/autopause
73b29eeb 730 generate_subq(cdr.SetSectorPlay);
f5450cfb 731
480e570b 732 CDRPLAYREAD_INT(cdReadTime, 0);
bdc1bbb5 733
734 // stop propagation since it breaks streaming
735 cdr.PhysCdPropagations = 0xff;
7f457614 736}
737
a706d361 738static void softReset(void)
739{
08d9d257 740 cdra_getStatus(&cdr_stat);
38c77d78 741 if (cdr_stat.Status & STATUS_SHELLOPEN) {
a706d361 742 cdr.DriveState = DRIVESTATE_LID_OPEN;
743 cdr.StatP = STATUS_SHELLOPEN;
744 }
745 else if (CdromId[0] == '\0') {
746 cdr.DriveState = DRIVESTATE_STOPPED;
747 cdr.StatP = 0;
748 }
749 else {
750 cdr.DriveState = DRIVESTATE_STANDBY;
751 cdr.StatP = STATUS_ROTATING;
752 }
753
754 cdr.FifoOffset = DATA_SIZE; // fifo empty
755 cdr.LocL[0] = LOCL_INVALID;
756 cdr.Mode = MODE_SIZE_2340;
757 cdr.Muted = FALSE;
758 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
759 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
760}
761
8aeb66dc 762#define CMD_PART2 0x100
763#define CMD_WHILE_NOT_READY 0x200
764
d9a02493 765void cdrInterrupt(void) {
e4268a49 766 int start_rotating = 0;
767 int error = 0;
4134c627 768 u32 cycles, seekTime = 0;
7f2576b2 769 u32 second_resp_time = 0;
70c1043e 770 const void *buf;
7f2576b2 771 u8 ParamC;
70c1043e 772 int read_ok;
8aeb66dc 773 u16 not_ready = 0;
0b988c8b 774 u8 IrqStat = Acknowledge;
dcaa32dd 775 u8 DriveStateOld;
7f2576b2 776 u16 Cmd;
2da09dae 777 int i;
ef79bbde 778
0b988c8b 779 if (cdr.IrqStat) {
10b9bf1e 780 CDR_LOG_I("cmd %02x with irqstat %x\n",
0b988c8b 781 cdr.CmdInProgress, cdr.IrqStat);
ef79bbde
P
782 return;
783 }
38e03f0a 784 if (cdr.Irq1Pending) {
785 // hand out the "newest" sector, according to nocash
08d9d257 786 cdrUpdateTransferBuf(cdra_getBuffer());
10b9bf1e 787 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
788 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
789 cdr.CmdInProgress, cdr.Irq1Pending);
38e03f0a 790 SetResultSize(1);
791 cdr.Result[0] = cdr.Irq1Pending;
38e03f0a 792 cdr.Irq1Pending = 0;
0b988c8b 793 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
38e03f0a 794 return;
795 }
ef79bbde 796
e4268a49 797 // default response
798 SetResultSize(1);
799 cdr.Result[0] = cdr.StatP;
e4268a49 800
7f2576b2 801 Cmd = cdr.CmdInProgress;
802 cdr.CmdInProgress = 0;
803 ParamC = cdr.ParamC;
e4268a49 804
7f2576b2 805 if (Cmd < 0x100) {
8aeb66dc 806 cdr.Ctrl &= ~0x80;
7f2576b2 807 cdr.ParamC = 0;
8aeb66dc 808 cdr.Cmd = 0;
809 }
810
811 switch (cdr.DriveState) {
871bedc6 812 case DRIVESTATE_PREPARE_CD:
813 if (Cmd > 2) {
814 // Syphon filter 2 expects commands to work shortly after it sees
815 // STATUS_ROTATING, so give up trying to emulate the startup seq
816 cdr.DriveState = DRIVESTATE_STANDBY;
817 cdr.StatP &= ~STATUS_SEEK;
818 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
819 break;
820 }
821 // fallthrough
8aeb66dc 822 case DRIVESTATE_LID_OPEN:
823 case DRIVESTATE_RESCAN_CD:
8aeb66dc 824 // no disk or busy with the initial scan, allowed cmds are limited
825 not_ready = CMD_WHILE_NOT_READY;
826 break;
7f2576b2 827 }
e4268a49 828
8aeb66dc 829 switch (Cmd | not_ready) {
ef79bbde 830 case CdlNop:
8aeb66dc 831 case CdlNop + CMD_WHILE_NOT_READY:
80f91a20 832 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
833 cdr.StatP &= ~STATUS_SHELLOPEN;
ef79bbde
P
834 break;
835
836 case CdlSetloc:
0123002e 837 case CdlSetloc + CMD_WHILE_NOT_READY: // apparently?
838 if (cdr.StatP & STATUS_SHELLOPEN)
839 // wrong? Driver2 vs Amerzone
840 goto set_error;
2da09dae 841
842 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
843 if (((cdr.Param[0] & 0x0F) > 0x09) || (cdr.Param[0] > 0x99) || ((cdr.Param[1] & 0x0F) > 0x09) || (cdr.Param[1] >= 0x60) || ((cdr.Param[2] & 0x0F) > 0x09) || (cdr.Param[2] >= 0x75))
844 {
ececcc61 845 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
4f38d1f8 846 if (++cdr.errorRetryhack > 100)
847 break;
0b988c8b 848 error = ERROR_BAD_ARGNUM;
2da09dae 849 goto set_error;
850 }
851 else
852 {
08d9d257 853 u8 set_loc[3];
2da09dae 854 for (i = 0; i < 3; i++)
2da09dae 855 set_loc[i] = btoi(cdr.Param[i]);
22af444c 856 if ((msfiEq(cdr.SetSector, set_loc)) //|| msfiEq(cdr.Param, cdr.Transfer))
857 && !cdr.SetlocPending)
858 cdr.RetryDetected++;
859 else
860 cdr.RetryDetected = 0;
2da09dae 861 memcpy(cdr.SetSector, set_loc, 3);
862 cdr.SetSector[3] = 0;
863 cdr.SetlocPending = 1;
4f38d1f8 864 cdr.errorRetryhack = 0;
2da09dae 865 }
ef79bbde
P
866 break;
867
868 case CdlPlay:
030bd76a 869 StopCdda();
f522e63c 870 StopReading();
871
c9c7a925 872 cdr.FastBackward = 0;
873 cdr.FastForward = 0;
874
2d5ca6c0 875 // BIOS CD Player
876 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
877
7f2576b2 878 if (ParamC != 0 && cdr.Param[0] != 0) {
2d5ca6c0 879 int track = btoi( cdr.Param[0] );
880
881 if (track <= cdr.ResultTN[1])
882 cdr.CurTrack = track;
883
884 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
885
08d9d257 886 if (cdra_getTD(cdr.CurTrack, cdr.ResultTD) != -1) {
887 seekTime = cdrSeekTime(cdr.ResultTD);
888 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
2d5ca6c0 889 }
890 }
f522e63c 891 else if (cdr.SetlocPending) {
892 seekTime = cdrSeekTime(cdr.SetSector);
893 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
894 }
895 else {
896 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
897 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
898 }
899 cdr.SetlocPending = 0;
2d5ca6c0 900
9f8b032d 901 /*
902 Rayman: detect track changes
903 - fixes logo freeze
904
905 Twisted Metal 2: skip PREGAP + starting accurate SubQ
906 - plays tracks without retry play
907
908 Wild 9: skip PREGAP + starting accurate SubQ
909 - plays tracks without retry play
910 */
fffad32e 911 Find_CurTrack(cdr.SetSectorPlay);
752c1c85 912 generate_subq(cdr.SetSectorPlay);
70c1043e 913 cdr.LocL[0] = LOCL_INVALID;
752c1c85 914 cdr.SubqForwardSectors = 1;
fffad32e 915 cdr.TrackChanged = FALSE;
5c5e6c0c 916 cdr.FileChannelSelected = 0;
917 cdr.AdpcmActive = 0;
a6e03490 918 cdr.ReportDelay = 60;
b9a092f5 919 cdr.sectorsRead = 0;
9f8b032d 920
f522e63c 921 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
9f8b032d 922
923 // BIOS player - set flag again
924 cdr.Play = TRUE;
cc1e8bd4 925 cdr.DriveState = DRIVESTATE_PLAY_READ;
bdc1bbb5 926 cdr.PhysCdPropagations = 0;
9f8b032d 927
480e570b 928 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
e4268a49 929 start_rotating = 1;
ef79bbde
P
930 break;
931
9f8b032d 932 case CdlForward:
e4268a49 933 // TODO: error 80 if stopped
0b988c8b 934 IrqStat = Complete;
9f8b032d 935
9f8b032d 936 // GameShark CD Player: Calls 2x + Play 2x
c9c7a925 937 cdr.FastForward = 1;
9f8b032d 938 cdr.FastBackward = 0;
ef79bbde
P
939 break;
940
9f8b032d 941 case CdlBackward:
0b988c8b 942 IrqStat = Complete;
9f8b032d 943
9f8b032d 944 // GameShark CD Player: Calls 2x + Play 2x
c9c7a925 945 cdr.FastBackward = 1;
9f8b032d 946 cdr.FastForward = 0;
ef79bbde
P
947 break;
948
9f8b032d 949 case CdlStandby:
e4268a49 950 if (cdr.DriveState != DRIVESTATE_STOPPED) {
0b988c8b 951 error = ERROR_BAD_ARGNUM;
e4268a49 952 goto set_error;
953 }
7f2576b2 954 second_resp_time = cdReadTime * 125 / 2;
e4268a49 955 start_rotating = 1;
956 break;
957
8aeb66dc 958 case CdlStandby + CMD_PART2:
0b988c8b 959 IrqStat = Complete;
ef79bbde
P
960 break;
961
962 case CdlStop:
030bd76a 963 if (cdr.Play) {
964 // grab time for current track
08d9d257 965 cdra_getTD(cdr.CurTrack, cdr.ResultTD);
966 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
030bd76a 967 }
968
969 StopCdda();
970 StopReading();
f522e63c 971 SetPlaySeekRead(cdr.StatP, 0);
972 cdr.StatP &= ~STATUS_ROTATING;
70c1043e 973 cdr.LocL[0] = LOCL_INVALID;
030bd76a 974
7f2576b2 975 second_resp_time = 0x800;
cc1e8bd4 976 if (cdr.DriveState != DRIVESTATE_STOPPED)
7f2576b2 977 second_resp_time = cdReadTime * 30 / 2;
e4268a49 978
979 cdr.DriveState = DRIVESTATE_STOPPED;
e4268a49 980 break;
981
8aeb66dc 982 case CdlStop + CMD_PART2:
0b988c8b 983 IrqStat = Complete;
ef79bbde
P
984 break;
985
986 case CdlPause:
38b8a211 987 if (cdr.AdpcmActive) {
988 cdr.AdpcmActive = 0;
989 cdr.Xa.nsamples = 0;
990 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
991 }
f522e63c 992 StopCdda();
993 StopReading();
b9a092f5 994
995 // how the drive maintains the position while paused is quite
996 // complicated, this is the minimum to make "Bedlam" happy
997 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
998 cdr.sectorsRead = 0;
999
12228917 1000 /*
1631b053 1001 Gundam Battle Assault 2
1002 Hokuto no Ken 2
1003 InuYasha - Feudal Fairy Tale
1004 Dance Dance Revolution Konamix
22af444c 1005 Digimon Rumble Arena
1631b053 1006 ...
12228917 1007 */
f522e63c 1008 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
e3d555e0 1009 {
7f2576b2 1010 second_resp_time = 7000;
e3d555e0 1011 }
1012 else
1013 {
22af444c 1014 second_resp_time = 2100011;
1015 // a hack to try to avoid weird cmd vs irq1 races causing games to retry
1016 second_resp_time += (cdr.RetryDetected & 15) * 100001;
e3d555e0 1017 }
f522e63c 1018 SetPlaySeekRead(cdr.StatP, 0);
dcaa32dd 1019 DriveStateOld = cdr.DriveState;
cc1e8bd4 1020 cdr.DriveState = DRIVESTATE_PAUSED;
dcaa32dd 1021 if (DriveStateOld == DRIVESTATE_SEEK) {
1022 // According to Duckstation this fails, but the
1023 // exact conditions and effects are not clear.
1024 // Moto Racer World Tour seems to rely on this.
1025 // For now assume pause works anyway, just errors out.
1026 error = ERROR_NOTREADY;
1027 goto set_error;
1028 }
ef79bbde
P
1029 break;
1030
8aeb66dc 1031 case CdlPause + CMD_PART2:
0b988c8b 1032 IrqStat = Complete;
ef79bbde
P
1033 break;
1034
b0bd140d 1035 case CdlReset:
8aeb66dc 1036 case CdlReset + CMD_WHILE_NOT_READY:
a706d361 1037 // note: nocash and Duckstation calls this 'Init', but
1038 // the official SDK calls it 'Reset', and so do we
f522e63c 1039 StopCdda();
1040 StopReading();
a706d361 1041 softReset();
8aeb66dc 1042 second_resp_time = not_ready ? 70000 : 4100000;
e4268a49 1043 start_rotating = 1;
1044 break;
1045
8aeb66dc 1046 case CdlReset + CMD_PART2:
1047 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
0b988c8b 1048 IrqStat = Complete;
ef79bbde
P
1049 break;
1050
9f8b032d 1051 case CdlMute:
030bd76a 1052 cdr.Muted = TRUE;
38b8a211 1053 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
ef79bbde
P
1054 break;
1055
9f8b032d 1056 case CdlDemute:
030bd76a 1057 cdr.Muted = FALSE;
38b8a211 1058 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1059 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
ef79bbde
P
1060 break;
1061
9f8b032d 1062 case CdlSetfilter:
5c5e6c0c 1063 cdr.FilterFile = cdr.Param[0];
1064 cdr.FilterChannel = cdr.Param[1];
54d6fbe7 1065 cdr.FileChannelSelected = 0;
9f8b032d 1066 break;
ef79bbde
P
1067
1068 case CdlSetmode:
8aeb66dc 1069 case CdlSetmode + CMD_WHILE_NOT_READY:
49cebb24 1070 if ((cdr.Mode ^ cdr.Param[0]) & MODE_BIT4)
1071 log_unhandled("cdrom: mode4 changed: %02x\n", cdr.Param[0]);
7f2576b2 1072 cdr.Mode = cdr.Param[0];
9f8b032d 1073 break;
ef79bbde 1074
dcd72441 1075 case CdlGetparam:
8aeb66dc 1076 case CdlGetparam + CMD_WHILE_NOT_READY:
dcd72441 1077 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
0b988c8b 1078 SetResultSize_(5);
9f8b032d 1079 cdr.Result[1] = cdr.Mode;
dcd72441 1080 cdr.Result[2] = 0;
5c5e6c0c 1081 cdr.Result[3] = cdr.FilterFile;
1082 cdr.Result[4] = cdr.FilterChannel;
9f8b032d 1083 break;
ef79bbde 1084
9f8b032d 1085 case CdlGetlocL:
70c1043e 1086 if (cdr.LocL[0] == LOCL_INVALID) {
1087 error = 0x80;
1088 goto set_error;
1089 }
0b988c8b 1090 SetResultSize_(8);
70c1043e 1091 memcpy(cdr.Result, cdr.LocL, 8);
9f8b032d 1092 break;
ef79bbde
P
1093
1094 case CdlGetlocP:
0b988c8b 1095 SetResultSize_(8);
fffad32e 1096 memcpy(&cdr.Result, &cdr.subq, 8);
e4268a49 1097 break;
892bd7d4 1098
e4268a49 1099 case CdlReadT: // SetSession?
1100 // really long
7f2576b2 1101 second_resp_time = cdReadTime * 290 / 4;
e4268a49 1102 start_rotating = 1;
1103 break;
1104
8aeb66dc 1105 case CdlReadT + CMD_PART2:
0b988c8b 1106 IrqStat = Complete;
ef79bbde
P
1107 break;
1108
9f8b032d 1109 case CdlGetTN:
08d9d257 1110 if (cdra_getTN(cdr.ResultTN) != 0) {
0b988c8b 1111 assert(0);
9f8b032d 1112 }
0b988c8b 1113 SetResultSize_(3);
1114 cdr.Result[1] = itob(cdr.ResultTN[0]);
1115 cdr.Result[2] = itob(cdr.ResultTN[1]);
9f8b032d 1116 break;
ef79bbde 1117
9f8b032d 1118 case CdlGetTD:
ef79bbde 1119 cdr.Track = btoi(cdr.Param[0]);
08d9d257 1120 if (cdra_getTD(cdr.Track, cdr.ResultTD) != 0) {
0b988c8b 1121 error = ERROR_BAD_ARGVAL;
1122 goto set_error;
ef79bbde 1123 }
0b988c8b 1124 SetResultSize_(3);
08d9d257 1125 cdr.Result[1] = itob(cdr.ResultTD[0]);
0b988c8b 1126 cdr.Result[2] = itob(cdr.ResultTD[1]);
1127 // no sector number
08d9d257 1128 //cdr.Result[3] = itob(cdr.ResultTD[2]);
ef79bbde
P
1129 break;
1130
9f8b032d 1131 case CdlSeekL:
39df67df 1132 case CdlSeekP:
030bd76a 1133 StopCdda();
1134 StopReading();
480e570b 1135 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
9f8b032d 1136
0aa7361c 1137 if (!canDoTurbo())
1138 seekTime = cdrSeekTime(cdr.SetSector);
f522e63c 1139 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
cc1e8bd4 1140 cdr.DriveState = DRIVESTATE_SEEK;
08d9d257 1141 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
240e5daa 1142 cdr.SetSectorPlay[2], 0);
9f8b032d 1143 /*
1144 Crusaders of Might and Magic = 0.5x-4x
1145 - fix cutscene speech start
1146
1147 Eggs of Steel = 2x-?
1148 - fix new game
1149
1150 Medievil = ?-4x
1151 - fix cutscene speech
1152
1153 Rockman X5 = 0.5-4x
1154 - fix capcom logo
1155 */
480e570b 1156 second_resp_time = cdReadTime + seekTime;
e4268a49 1157 start_rotating = 1;
ef79bbde
P
1158 break;
1159
480e570b 1160 case CdlSeekL + CMD_PART2:
1161 case CdlSeekP + CMD_PART2:
1162 SetPlaySeekRead(cdr.StatP, 0);
ca1683d0 1163 cdr.Result[0] = cdr.StatP;
0b988c8b 1164 IrqStat = Complete;
480e570b 1165
1166 Find_CurTrack(cdr.SetSectorPlay);
70c1043e 1167 read_ok = ReadTrack(cdr.SetSectorPlay);
08d9d257 1168 if (read_ok && (buf = cdra_getBuffer()))
70c1043e 1169 memcpy(cdr.LocL, buf, 8);
752c1c85 1170 UpdateSubq(cdr.SetSectorPlay);
cc1e8bd4 1171 cdr.DriveState = DRIVESTATE_STANDBY;
480e570b 1172 cdr.TrackChanged = FALSE;
e7606d0e 1173 cdr.LastReadSeekCycles = psxRegs.cycle;
480e570b 1174 break;
1175
ef79bbde 1176 case CdlTest:
8aeb66dc 1177 case CdlTest + CMD_WHILE_NOT_READY:
9f8b032d 1178 switch (cdr.Param[0]) {
1179 case 0x20: // System Controller ROM Version
0b988c8b 1180 SetResultSize_(4);
ef79bbde
P
1181 memcpy(cdr.Result, Test20, 4);
1182 break;
1183 case 0x22:
0b988c8b 1184 SetResultSize_(8);
ef79bbde
P
1185 memcpy(cdr.Result, Test22, 4);
1186 break;
1187 case 0x23: case 0x24:
0b988c8b 1188 SetResultSize_(8);
ef79bbde
P
1189 memcpy(cdr.Result, Test23, 4);
1190 break;
9f8b032d 1191 }
ef79bbde
P
1192 break;
1193
9f8b032d 1194 case CdlID:
7f2576b2 1195 second_resp_time = 20480;
ef79bbde
P
1196 break;
1197
8aeb66dc 1198 case CdlID + CMD_PART2:
0b988c8b 1199 SetResultSize_(8);
89ec56af 1200 cdr.Result[0] = cdr.StatP;
1201 cdr.Result[1] = 0;
1202 cdr.Result[2] = 0;
1203 cdr.Result[3] = 0;
9f8b032d 1204
d8432250 1205 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
49cebb24 1206 if (cdra_getStatus(&cdr_stat) != 0 ||
1207 cdr_stat.Type == CDRT_UNKNOWN || cdr_stat.Type == 0xff) {
d8432250 1208 cdr.Result[1] = 0xc0;
9f8b032d 1209 }
1210 else {
49cebb24 1211 if (cdr_stat.Type == CDRT_CDDA)
89ec56af 1212 cdr.Result[1] |= 0x10;
1213 if (CdromId[0] == '\0')
9f8b032d 1214 cdr.Result[1] |= 0x80;
9f8b032d 1215 }
89ec56af 1216 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
2f59faba 1217 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1218 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
9f8b032d 1219
2f8f99f5 1220 /* 4-char string in Playstation bios boot screen */
1221 if (Config.SlowBoot == 1)
1222 memcpy(&cdr.Result[4], "PCSX", 4);
1223 else {
1224 cdr.Result[4] = 'S';
1225 cdr.Result[5] = 'C';
1226 cdr.Result[6] = 'E';
1227 if (Config.PsxType == PSX_TYPE_PAL)
1228 cdr.Result[7] = 'E';
1229 else if (CdromId[2] == 'P' || CdromId[2] == 'p')
1230 cdr.Result[7] = 'I';
1231 else
1232 cdr.Result[7] = 'A';
1233 }
0b988c8b 1234 IrqStat = Complete;
ef79bbde
P
1235 break;
1236
b0bd140d 1237 case CdlInit:
8aeb66dc 1238 case CdlInit + CMD_WHILE_NOT_READY:
f522e63c 1239 StopCdda();
1240 StopReading();
1241 SetPlaySeekRead(cdr.StatP, 0);
e4268a49 1242 // yes, it really sets STATUS_SHELLOPEN
1243 cdr.StatP |= STATUS_SHELLOPEN;
1244 cdr.DriveState = DRIVESTATE_RESCAN_CD;
9a0a61d2 1245 set_event(PSXINT_CDRLID, 20480);
e4268a49 1246 start_rotating = 1;
9f8b032d 1247 break;
1248
e4268a49 1249 case CdlGetQ:
8aeb66dc 1250 case CdlGetQ + CMD_WHILE_NOT_READY:
9f8b032d 1251 break;
1252
1253 case CdlReadToc:
8aeb66dc 1254 case CdlReadToc + CMD_WHILE_NOT_READY:
70c1043e 1255 cdr.LocL[0] = LOCL_INVALID;
7f2576b2 1256 second_resp_time = cdReadTime * 180 / 4;
2f8f99f5 1257 if (!Config.HLE && Config.SlowBoot) {
1258 // hack: compensate cdrom being emulated too fast
1259 // and bios finishing before the reverb decays
1260 second_resp_time += cdReadTime * 75*2;
1261 if ((psxRegs.pc >> 28) == 0x0b)
1262 second_resp_time += cdReadTime * 75*3;
1263 }
e4268a49 1264 start_rotating = 1;
ef79bbde
P
1265 break;
1266
8aeb66dc 1267 case CdlReadToc + CMD_PART2:
1268 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
0b988c8b 1269 IrqStat = Complete;
ef79bbde
P
1270 break;
1271
94c118c3 1272 case CdlReadN:
1273 case CdlReadS:
ececcc61 1274 if (cdr.Reading && !cdr.SetlocPending)
1275 break;
1276
f522e63c 1277 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
55b8460a 1278
6637a60f 1279 if (cdr_stat.Type != CDRT_DATA && !(cdr.Mode & MODE_CDDA)) {
49cebb24 1280 error = ERROR_INVALIDCMD;
1281 goto set_error;
1282 }
55b8460a 1283
f522e63c 1284 StopCdda();
1285 if (cdr.SetlocPending) {
1286 seekTime = cdrSeekTime(cdr.SetSector);
1287 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1288 cdr.SetlocPending = 0;
1289 }
030bd76a 1290 cdr.Reading = 1;
5c5e6c0c 1291 cdr.FileChannelSelected = 0;
1292 cdr.AdpcmActive = 0;
ef79bbde 1293
9f8b032d 1294 // Fighting Force 2 - update subq time immediately
1295 // - fixes new game
752c1c85 1296 UpdateSubq(cdr.SetSectorPlay);
70c1043e 1297 cdr.LocL[0] = LOCL_INVALID;
752c1c85 1298 cdr.SubqForwardSectors = 1;
b9a092f5 1299 cdr.sectorsRead = 0;
cc1e8bd4 1300 cdr.DriveState = DRIVESTATE_SEEK;
bdc1bbb5 1301 cdr.PhysCdPropagations = 0;
08d9d257 1302 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
240e5daa 1303 cdr.SetSectorPlay[2], 0);
9f8b032d 1304
509ebde4 1305 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
4134c627 1306 cycles += seekTime;
688bdb95 1307 if (Config.hacks.cdr_read_timing)
1308 cycles = cdrAlignTimingHack(cycles);
0aa7361c 1309 else if (canDoTurbo())
1310 cycles = cdReadTime / 2;
4134c627 1311 CDRPLAYREAD_INT(cycles, 1);
ef79bbde 1312
f522e63c 1313 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
e4268a49 1314 start_rotating = 1;
ef79bbde 1315 break;
70c1043e 1316
1f035e27 1317 case CdlSync:
ef79bbde 1318 default:
e4268a49 1319 error = ERROR_INVALIDCMD;
1320 // FALLTHROUGH
1321
1322 set_error:
0b988c8b 1323 SetResultSize_(2);
e4268a49 1324 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
8aeb66dc 1325 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
0b988c8b 1326 IrqStat = DiskError;
6ca445e2 1327 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
ef79bbde
P
1328 break;
1329 }
1330
e4268a49 1331 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1332 cdr.DriveState = DRIVESTATE_STANDBY;
1333 cdr.StatP |= STATUS_ROTATING;
1334 }
1335
7f2576b2 1336 if (second_resp_time) {
1337 cdr.CmdInProgress = Cmd | 0x100;
9a0a61d2 1338 set_event(PSXINT_CDR, second_resp_time);
7f2576b2 1339 }
1340 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1341 cdr.CmdInProgress = cdr.Cmd;
10b9bf1e 1342 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
7f2576b2 1343 }
1344
0b988c8b 1345 setIrq(IrqStat, Cmd);
ef79bbde
P
1346}
1347
4ad17db3 1348static void cdrPrepCdda(s16 *buf, int samples)
1349{
1350#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1351 int i;
1352 for (i = 0; i < samples; i++) {
1353 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1354 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1355 }
1356#endif
1357}
1358
f5450cfb 1359static void cdrReadInterruptSetResult(unsigned char result)
d9a02493 1360{
0b988c8b 1361 if (cdr.IrqStat) {
10b9bf1e 1362 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
f5450cfb 1363 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
0b988c8b 1364 cdr.CmdInProgress, cdr.IrqStat);
f5450cfb 1365 cdr.Irq1Pending = result;
5082c6c7 1366 // F1 2000 timing hack :(
9d701f80 1367 // compensate for some csum func @80014380 taking too long
d7aae17c 1368 if (!cdr.AdpcmActive)
1369 psxRegs.intCycle[PSXINT_CDREAD].sCycle += cdReadTime / 10;
ef79bbde
P
1370 return;
1371 }
ef79bbde 1372 SetResultSize(1);
f5450cfb 1373 cdr.Result[0] = result;
0b988c8b 1374 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
f5450cfb 1375}
1376
1377static void cdrUpdateTransferBuf(const u8 *buf)
1378{
1379 if (!buf)
1380 return;
1381 memcpy(cdr.Transfer, buf, DATA_SIZE);
1382 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
b9a092f5 1383 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1384 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
742a21f2 1385 if (cdr.FifoOffset < 2048 + 12)
10b9bf1e 1386 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
f5450cfb 1387}
1388
1389static void cdrReadInterrupt(void)
1390{
5c5e6c0c 1391 const struct { u8 file, chan, mode, coding; } *subhdr;
1392 const u8 *buf = NULL;
1393 int deliver_data = 1;
752c1c85 1394 u8 subqPos[3];
70c1043e 1395 int read_ok;
38b8a211 1396 int is_start;
f5450cfb 1397
752c1c85 1398 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1399 msfiAdd(subqPos, cdr.SubqForwardSectors);
1400 UpdateSubq(subqPos);
1401 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1402 cdr.SubqForwardSectors++;
1403 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1404 return;
1405 }
1406
7e6d030e 1407 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1408 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
cc1e8bd4 1409 cdr.DriveState = DRIVESTATE_PLAY_READ;
b9a092f5 1410 cdr.sectorsRead++;
7e6d030e 1411
70c1043e 1412 read_ok = ReadTrack(cdr.SetSectorPlay);
1413 if (read_ok)
08d9d257 1414 buf = cdra_getBuffer();
ef79bbde 1415 if (buf == NULL)
70c1043e 1416 read_ok = 0;
ef79bbde 1417
70c1043e 1418 if (!read_ok) {
8b380e97 1419 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
f5450cfb 1420 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
cc1e8bd4 1421 cdr.DriveState = DRIVESTATE_PAUSED; // ?
ef79bbde
P
1422 return;
1423 }
70c1043e 1424 memcpy(cdr.LocL, buf, 8);
ef79bbde 1425
0b988c8b 1426 if (!cdr.IrqStat && !cdr.Irq1Pending)
f5450cfb 1427 cdrUpdateTransferBuf(buf);
ef79bbde 1428
5c5e6c0c 1429 subhdr = (void *)(buf + 4);
1430 do {
1431 // try to process as adpcm
1432 if (!(cdr.Mode & MODE_STRSND))
1433 break;
1434 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1435 break;
1436 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1437 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1438 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1439 break;
df4bcb0e 1440 if (subhdr->chan & 0x80) { // ?
5c5e6c0c 1441 if (subhdr->chan != 0xff)
1442 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1443 break;
1444 }
1445 if (!cdr.FileChannelSelected) {
1446 cdr.CurFile = subhdr->file;
1447 cdr.CurChannel = subhdr->chan;
1448 cdr.FileChannelSelected = 1;
9f8b032d 1449 }
5c5e6c0c 1450 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1451 break;
9f8b032d 1452
5c5e6c0c 1453 // accepted as adpcm
1454 deliver_data = 0;
1455
1456 if (Config.Xa)
1457 break;
38b8a211 1458 is_start = !cdr.AdpcmActive;
1459 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1460 if (cdr.AdpcmActive)
1461 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
5c5e6c0c 1462 } while (0);
1463
1464 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1465 deliver_data = 0;
49cebb24 1466 if (!(cdr.Mode & MODE_SIZE_2340) && buf[3] != 1 && buf[3] != 2) {
1467 deliver_data = 0; // according to duckstation
1468 CDR_LOG_I("%d:%02d:%02d msf %x:%02x:%02x mode %02x ignored\n",
1469 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
d66df6bb 1470 buf[0], buf[1], buf[2], buf[3]);
1471 }
ef79bbde 1472
f5450cfb 1473 /*
1474 Croc 2: $40 - only FORM1 (*)
1475 Judge Dredd: $C8 - only FORM1 (*)
1476 Sim Theme Park - no adpcm at all (zero)
1477 */
1478
5c5e6c0c 1479 if (deliver_data)
f5450cfb 1480 cdrReadInterruptSetResult(cdr.StatP);
1481
752c1c85 1482 msfiAdd(cdr.SetSectorPlay, 1);
240e5daa 1483 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 0);
9f8b032d 1484
480e570b 1485 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
f5450cfb 1486}
9f8b032d 1487
ef79bbde
P
1488/*
1489cdrRead0:
5c5e6c0c 1490 bit 0,1 - reg index
1491 bit 2 - adpcm active
d5609856 1492 bit 3 - 1 parameter fifo empty
1493 bit 4 - 1 parameter fifo not full
1494 bit 5 - 1 response fifo not empty
1495 bit 6 - 1 data fifo not empty
ef79bbde
P
1496 bit 7 - 1 command being processed
1497*/
1498
1499unsigned char cdrRead0(void) {
d5609856 1500 cdr.Ctrl &= ~0x64;
5c5e6c0c 1501 cdr.Ctrl |= cdr.AdpcmActive << 2;
1502 cdr.Ctrl |= cdr.ResultReady << 5;
d5609856 1503 cdr.Ctrl |= ((signed int)(cdr.FifoOffset - cdr.FifoSize) >> 31) & 0x40;
ef79bbde 1504
ef79bbde
P
1505 cdr.Ctrl |= 0x18;
1506
af93c8be 1507 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
ef79bbde
P
1508
1509 return psxHu8(0x1800) = cdr.Ctrl;
1510}
1511
ef79bbde 1512void cdrWrite0(unsigned char rt) {
1dc68512 1513 CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
8b380e97 1514
50b13be9 1515 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
ef79bbde
P
1516}
1517
1518unsigned char cdrRead1(void) {
94c118c3 1519 if ((cdr.ResultP & 0xf) < cdr.ResultC)
9f8b032d 1520 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
94c118c3 1521 else
ef79bbde 1522 psxHu8(0x1801) = 0;
94c118c3 1523 cdr.ResultP++;
1524 if (cdr.ResultP == cdr.ResultC)
1525 cdr.ResultReady = 0;
1526
1dc68512 1527 CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
8b380e97 1528
ef79bbde
P
1529 return psxHu8(0x1801);
1530}
1531
1532void cdrWrite1(unsigned char rt) {
1dc68512 1533 const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
af93c8be 1534 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
9f8b032d 1535
94c118c3 1536 switch (cdr.Ctrl & 3) {
1537 case 0:
1538 break;
1539 case 3:
53598a71 1540 cdr.AttenuatorRightToRightT = rt;
94c118c3 1541 return;
1542 default:
1543 return;
9f8b032d 1544 }
1545
49cebb24 1546#ifdef CDR_LOG_CMD
10b9bf1e 1547 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
ef79bbde 1548 if (cdr.ParamC) {
af93c8be 1549 int i;
ef79bbde
P
1550 SysPrintf(" Param[%d] = {", cdr.ParamC);
1551 for (i = 0; i < cdr.ParamC; i++)
1552 SysPrintf(" %x,", cdr.Param[i]);
1dc68512 1553 SysPrintf("}");
ef79bbde 1554 }
1dc68512 1555 SysPrintf(" @%08x\n", psxRegs.pc);
ef79bbde
P
1556#endif
1557
2daaaae3 1558 cdr.ResultReady = 0;
94c118c3 1559 cdr.Ctrl |= 0x80;
8b380e97 1560
7f2576b2 1561 if (!cdr.CmdInProgress) {
1562 cdr.CmdInProgress = rt;
1563 // should be something like 12k + controller delays
9a0a61d2 1564 set_event(PSXINT_CDR, 5000);
9f8b032d 1565 }
7f2576b2 1566 else {
10b9bf1e 1567 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1568 rt, cdr.Cmd, cdr.CmdInProgress);
7f2576b2 1569 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1570 cdr.CmdInProgress = rt;
1571 }
1572
1573 cdr.Cmd = rt;
ef79bbde
P
1574}
1575
1576unsigned char cdrRead2(void) {
afaac935 1577 unsigned char ret = cdr.Transfer[0x920];
ef79bbde 1578
742a21f2 1579 if (cdr.FifoOffset < cdr.FifoSize)
79cd4919 1580 ret = cdr.Transfer[cdr.FifoOffset++];
1581 else
10b9bf1e 1582 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
ef79bbde 1583
1dc68512 1584 CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
ef79bbde
P
1585 return ret;
1586}
1587
1588void cdrWrite2(unsigned char rt) {
1dc68512 1589 const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
af93c8be 1590 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
9f8b032d 1591
94c118c3 1592 switch (cdr.Ctrl & 3) {
1593 case 0:
1594 if (cdr.ParamC < 8) // FIXME: size and wrapping
1595 cdr.Param[cdr.ParamC++] = rt;
1596 return;
1597 case 1:
0b988c8b 1598 cdr.IrqMask = rt;
1599 setIrq(cdr.IrqStat, 0x1005);
94c118c3 1600 return;
1601 case 2:
53598a71 1602 cdr.AttenuatorLeftToLeftT = rt;
94c118c3 1603 return;
1604 case 3:
53598a71 1605 cdr.AttenuatorRightToLeftT = rt;
94c118c3 1606 return;
ef79bbde
P
1607 }
1608}
1609
1610unsigned char cdrRead3(void) {
94c118c3 1611 if (cdr.Ctrl & 0x1)
0b988c8b 1612 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
94c118c3 1613 else
0b988c8b 1614 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
8b380e97 1615
1dc68512 1616 CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1617 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
ef79bbde
P
1618 return psxHu8(0x1803);
1619}
1620
1621void cdrWrite3(unsigned char rt) {
1dc68512 1622 const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
38b8a211 1623 u8 ll, lr, rl, rr;
af93c8be 1624 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
056d6759 1625
94c118c3 1626 switch (cdr.Ctrl & 3) {
1627 case 0:
a34a2445 1628 break; // transfer
94c118c3 1629 case 1:
0b988c8b 1630 if (cdr.IrqStat & rt) {
399f666e 1631 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1632 + psxRegs.intCycle[PSXINT_CDR].cycle;
10b9bf1e 1633 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
49cebb24 1634#ifdef CDR_LOG_CMD_ACK
0b988c8b 1635 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1636 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
10b9bf1e 1637 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
7f2576b2 1638#endif
399f666e 1639 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
10b9bf1e 1640 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
399f666e 1641 {
69dc4e5f 1642 s32 c = 2048;
1643 if (cdr.CmdInProgress) {
1644 c = 2048 - (psxRegs.cycle - nextCycle);
1645 c = MAX_VALUE(c, 512);
1646 }
9a0a61d2 1647 set_event(PSXINT_CDR, c);
399f666e 1648 }
38e03f0a 1649 }
0b988c8b 1650 cdr.IrqStat &= ~rt;
a34a2445 1651
1652 if (rt & 0x40)
1653 cdr.ParamC = 0;
1654 return;
94c118c3 1655 case 2:
53598a71 1656 cdr.AttenuatorLeftToRightT = rt;
94c118c3 1657 return;
1658 case 3:
38b8a211 1659 if (rt & 0x01)
1660 log_unhandled("Mute ADPCM?\n");
53598a71 1661 if (rt & 0x20) {
38b8a211 1662 ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1663 rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1664 if (ll == cdr.AttenuatorLeftToLeft &&
1665 lr == cdr.AttenuatorLeftToRight &&
1666 rl == cdr.AttenuatorRightToLeft &&
1667 rr == cdr.AttenuatorRightToRight)
1668 return;
1237304d 1669 cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1670 cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
38b8a211 1671 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1672 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
53598a71 1673 }
94c118c3 1674 return;
9f8b032d 1675 }
056d6759 1676
742a21f2 1677 // test: Viewpoint
1678 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1679 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1680 }
1681 else if (rt & 0x80) {
509ebde4 1682 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
9f8b032d 1683 case MODE_SIZE_2328:
ef79bbde 1684 case 0x00:
79cd4919 1685 cdr.FifoOffset = 12;
742a21f2 1686 cdr.FifoSize = 2048 + 12;
ef79bbde 1687 break;
9f8b032d 1688
1689 case MODE_SIZE_2340:
ef79bbde 1690 default:
79cd4919 1691 cdr.FifoOffset = 0;
742a21f2 1692 cdr.FifoSize = 2340;
ef79bbde
P
1693 break;
1694 }
1695 }
742a21f2 1696 else if (!(rt & 0xc0))
1697 cdr.FifoOffset = DATA_SIZE; // fifo empty
ef79bbde
P
1698}
1699
1700void psxDma3(u32 madr, u32 bcr, u32 chcr) {
0aa7361c 1701 u32 cdsize, max_words, cycles;
c7071c43 1702 int size;
ef79bbde
P
1703 u8 *ptr;
1704
10b9bf1e 1705#if 0
1706 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1707 if (cdr.FifoOffset == 0) {
1708 ptr = cdr.Transfer;
1709 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1710 }
1711 SysPrintf("\n");
1712#endif
ef79bbde 1713
4f329f16 1714 switch (chcr & 0x71000000) {
ef79bbde 1715 case 0x11000000:
b54a1ac7 1716 madr &= ~3;
b4d5a91d 1717 ptr = getDmaRam(madr, &max_words);
7a8d521f 1718 if (ptr == INVALID_PTR) {
79cd4919 1719 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
ef79bbde
P
1720 break;
1721 }
9f8b032d 1722
79cd4919 1723 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1724
9f8b032d 1725 /*
1726 GS CDX: Enhancement CD crash
1727 - Setloc 0:0:0
1728 - CdlPlay
1729 - Spams DMA3 and gets buffer overrun
1730 */
79cd4919 1731 size = DATA_SIZE - cdr.FifoOffset;
c7071c43 1732 if (size > cdsize)
1733 size = cdsize;
b4d5a91d 1734 if (size > max_words * 4)
1735 size = max_words * 4;
c7071c43 1736 if (size > 0)
9f8b032d 1737 {
79cd4919 1738 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1739 cdr.FifoOffset += size;
9f8b032d 1740 }
afaac935 1741 if (size < cdsize) {
6c9db47c 1742 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
afaac935 1743 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1744 }
1745 psxCpu->Clear(madr, cdsize / 4);
9f8b032d 1746
0aa7361c 1747 cycles = (cdsize / 4) * 24;
1748 set_event(PSXINT_CDRDMA, cycles);
4f329f16 1749
1750 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1751 if (chcr & 0x100) {
58ebb94c 1752 HW_DMA3_MADR = SWAPu32(madr + cdsize);
4f329f16 1753 HW_DMA3_BCR &= SWAPu32(0xffff0000);
9f8b032d 1754 }
4f329f16 1755 else {
fc4803bd 1756 // halted
0aa7361c 1757 psxRegs.cycle += cycles - 20;
9f8b032d 1758 }
0aa7361c 1759 if (canDoTurbo() && cdr.Reading && cdr.FifoOffset >= 2048)
1760 CDRPLAYREAD_INT(cycles + 4096, 1);
9f8b032d 1761 return;
1762
ef79bbde 1763 default:
6c9db47c 1764 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
ef79bbde
P
1765 break;
1766 }
1767
1768 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1769 DMA_INTERRUPT(3);
1770}
1771
d9a02493 1772void cdrDmaInterrupt(void)
9f8b032d 1773{
ad418c19 1774 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1775 {
1776 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1777 DMA_INTERRUPT(3);
1778 }
9f8b032d 1779}
1780
fffad32e 1781static void getCdInfo(void)
1782{
08d9d257 1783 cdra_getTN(cdr.ResultTN);
1784 cdra_getTD(0, cdr.SetSectorEnd);
49cebb24 1785 cdra_getStatus(&cdr_stat);
fffad32e 1786}
1787
ef79bbde
P
1788void cdrReset() {
1789 memset(&cdr, 0, sizeof(cdr));
1790 cdr.CurTrack = 1;
5c5e6c0c 1791 cdr.FilterFile = 0;
1792 cdr.FilterChannel = 0;
0b988c8b 1793 cdr.IrqMask = 0x1f;
1794 cdr.IrqStat = NoIntr;
02b1a085 1795
056d6759 1796 // BIOS player - default values
53598a71 1797 cdr.AttenuatorLeftToLeft = 0x80;
1798 cdr.AttenuatorLeftToRight = 0x00;
1799 cdr.AttenuatorRightToLeft = 0x00;
1800 cdr.AttenuatorRightToRight = 0x80;
fffad32e 1801
a706d361 1802 softReset();
fffad32e 1803 getCdInfo();
ef79bbde
P
1804}
1805
496d88d4 1806int cdrFreeze(void *f, int Mode) {
8e1040b6 1807 u32 tmp;
fffad32e 1808 u8 tmpp[3];
ef79bbde 1809
08d9d257 1810 cdr.freeze_ver = 0x63647203;
9f8b032d 1811 gzfreeze(&cdr, sizeof(cdr));
1812
94c118c3 1813 if (Mode == 1) {
1814 cdr.ParamP = cdr.ParamC;
79cd4919 1815 tmp = cdr.FifoOffset;
94c118c3 1816 }
ef79bbde
P
1817
1818 gzfreeze(&tmp, sizeof(tmp));
1819
42f3c512 1820 if (Mode == 0) {
38b8a211 1821 u8 ll = 0, lr = 0, rl = 0, rr = 0;
fffad32e 1822 getCdInfo();
1823
afaac935 1824 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
509ebde4 1825 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
752c1c85 1826 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1827 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
ef79bbde 1828
fffad32e 1829 // read right sub data
08d9d257 1830 memcpy(tmpp, cdr.Prev, sizeof(tmpp));
1831 if (cdr.freeze_ver < 0x63647203) {
1832 tmpp[0] = btoi(tmpp[0]);
1833 tmpp[1] = btoi(tmpp[1]);
1834 tmpp[2] = btoi(tmpp[2]);
1835 }
fb2e957d 1836 cdr.Prev[0] = 0xff;
1837 if (tmpp[0] != 0xff)
1838 ReadTrack(tmpp);
fffad32e 1839
1840 if (cdr.Play) {
55b8460a 1841 if (cdr.freeze_ver < 0x63647202)
1842 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1843
fffad32e 1844 Find_CurTrack(cdr.SetSectorPlay);
fffad32e 1845 }
38b8a211 1846 if (!cdr.Muted)
14f53a56 1847 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToRight,
38b8a211 1848 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1849 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
42f3c512 1850 }
1851
ef79bbde
P
1852 return 0;
1853}
9f8b032d 1854
d9a02493 1855void LidInterrupt(void) {
fffad32e 1856 getCdInfo();
80f91a20 1857 cdrLidSeekInterrupt();
9f8b032d 1858}