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