cdrom: get rid of pTransfer
[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
24#include "cdrom.h"
25#include "ppf.h"
9f8b032d 26#include "psxdma.h"
8f2bb0cb 27#include "arm_features.h"
ef79bbde 28
8b380e97 29/* logging */
30#if 0
31#define CDR_LOG SysPrintf
32#else
33#define CDR_LOG(...)
34#endif
35#if 0
36#define CDR_LOG_I SysPrintf
37#else
38#define CDR_LOG_I(...)
39#endif
40#if 0
41#define CDR_LOG_IO SysPrintf
42#else
43#define CDR_LOG_IO(...)
44#endif
45//#define CDR_LOG_CMD_IRQ
46
af93c8be 47static struct {
7f2576b2 48 // unused members maintain savesate compatibility
49 unsigned char unused0;
50 unsigned char unused1;
af93c8be 51 unsigned char Reg2;
7f2576b2 52 unsigned char unused2;
af93c8be 53 unsigned char Ctrl;
54 unsigned char Stat;
55
56 unsigned char StatP;
57
58 unsigned char Transfer[DATA_SIZE];
59 struct {
60 unsigned char Track;
61 unsigned char Index;
62 unsigned char Relative[3];
63 unsigned char Absolute[3];
64 } subq;
65 unsigned char TrackChanged;
7f2576b2 66 unsigned char unused3[3];
af93c8be 67 unsigned int freeze_ver;
68
69 unsigned char Prev[4];
70 unsigned char Param[8];
71 unsigned char Result[16];
72
73 unsigned char ParamC;
74 unsigned char ParamP;
75 unsigned char ResultC;
76 unsigned char ResultP;
77 unsigned char ResultReady;
78 unsigned char Cmd;
79 unsigned char Readed;
80 unsigned char SetlocPending;
81 u32 Reading;
82
83 unsigned char ResultTN[6];
84 unsigned char ResultTD[4];
85 unsigned char SetSectorPlay[4];
86 unsigned char SetSectorEnd[4];
87 unsigned char SetSector[4];
88 unsigned char Track;
89 boolean Play, Muted;
90 int CurTrack;
91 int Mode, File, Channel;
92 int Reset;
93 int NoErr;
94 int FirstSector;
95
96 xa_decode_t Xa;
97
79cd4919 98 u32 FifoOffset;
af93c8be 99
7f2576b2 100 u16 CmdInProgress;
f5450cfb 101 u8 Irq1Pending;
102 u8 unused5;
7f2576b2 103 u32 unused6;
af93c8be 104
7f2576b2 105 u8 unused7;
af93c8be 106
107 u8 DriveState;
108 u8 FastForward;
109 u8 FastBackward;
7f2576b2 110 u8 unused8;
af93c8be 111
112 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
113 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
114 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
115 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
116} cdr;
f4627eb3 117static s16 read_buf[CD_FRAMESIZE_RAW/2];
ef79bbde
P
118
119/* CD-ROM magic numbers */
1f035e27 120#define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
ef79bbde
P
121#define CdlNop 1
122#define CdlSetloc 2
123#define CdlPlay 3
124#define CdlForward 4
125#define CdlBackward 5
126#define CdlReadN 6
127#define CdlStandby 7
128#define CdlStop 8
129#define CdlPause 9
b0bd140d 130#define CdlReset 10
ef79bbde
P
131#define CdlMute 11
132#define CdlDemute 12
133#define CdlSetfilter 13
134#define CdlSetmode 14
dcd72441 135#define CdlGetparam 15
ef79bbde
P
136#define CdlGetlocL 16
137#define CdlGetlocP 17
138#define CdlReadT 18
139#define CdlGetTN 19
140#define CdlGetTD 20
141#define CdlSeekL 21
142#define CdlSeekP 22
143#define CdlSetclock 23
144#define CdlGetclock 24
145#define CdlTest 25
146#define CdlID 26
147#define CdlReadS 27
b0bd140d 148#define CdlInit 28
e4268a49 149#define CdlGetQ 29
ef79bbde
P
150#define CdlReadToc 30
151
af93c8be 152#ifdef CDR_LOG_CMD_IRQ
153static const char * const CmdName[0x100] = {
ef79bbde
P
154 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
155 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
b0bd140d 156 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
dcd72441 157 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
ef79bbde
P
158 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
159 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
160 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
b0bd140d 161 "CdlInit", NULL, "CDlReadToc", NULL
ef79bbde 162};
af93c8be 163#endif
ef79bbde
P
164
165unsigned char Test04[] = { 0 };
166unsigned char Test05[] = { 0 };
167unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
168unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
169unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
170
9f8b032d 171// cdr.Stat:
172#define NoIntr 0
173#define DataReady 1
174#define Complete 2
175#define Acknowledge 3
176#define DataEnd 4
177#define DiskError 5
178
179/* Modes flags */
180#define MODE_SPEED (1<<7) // 0x80
181#define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
182#define MODE_SIZE_2340 (1<<5) // 0x20
183#define MODE_SIZE_2328 (1<<4) // 0x10
cd0c9f5b 184#define MODE_SIZE_2048 (0<<4) // 0x00
9f8b032d 185#define MODE_SF (1<<3) // 0x08 channel on/off
186#define MODE_REPORT (1<<2) // 0x04
187#define MODE_AUTOPAUSE (1<<1) // 0x02
188#define MODE_CDDA (1<<0) // 0x01
189
190/* Status flags */
191#define STATUS_PLAY (1<<7) // 0x80
192#define STATUS_SEEK (1<<6) // 0x40
193#define STATUS_READ (1<<5) // 0x20
194#define STATUS_SHELLOPEN (1<<4) // 0x10
195#define STATUS_UNKNOWN3 (1<<3) // 0x08
196#define STATUS_UNKNOWN2 (1<<2) // 0x04
197#define STATUS_ROTATING (1<<1) // 0x02
198#define STATUS_ERROR (1<<0) // 0x01
199
e4268a49 200/* Errors */
003cfc63 201#define ERROR_NOTREADY (1<<7) // 0x80
e4268a49 202#define ERROR_INVALIDCMD (1<<6) // 0x40
203#define ERROR_INVALIDARG (1<<5) // 0x20
9f8b032d 204
ef79bbde
P
205// 1x = 75 sectors per second
206// PSXCLK = 1 sec in the ps
207// so (PSXCLK / 75) = cdr read time (linuzappz)
208#define cdReadTime (PSXCLK / 75)
209
80f91a20 210enum drive_state {
f522e63c 211 DRIVESTATE_STANDBY = 0, // pause, play, read
80f91a20 212 DRIVESTATE_LID_OPEN,
213 DRIVESTATE_RESCAN_CD,
214 DRIVESTATE_PREPARE_CD,
e4268a49 215 DRIVESTATE_STOPPED,
80f91a20 216};
217
ef79bbde 218static struct CdrStat stat;
ef79bbde 219
fffad32e 220static unsigned int msf2sec(const u8 *msf) {
9f8b032d 221 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
222}
223
fffad32e 224// for that weird psemu API..
225static unsigned int fsm2sec(const u8 *msf) {
226 return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
227}
228
305c8c93 229static void sec2msf(unsigned int s, u8 *msf) {
9f8b032d 230 msf[0] = s / 75 / 60;
231 s = s - msf[0] * 75 * 60;
232 msf[1] = s / 75;
233 s = s - msf[1] * 75;
234 msf[2] = s;
235}
236
39df67df 237// cdrInterrupt
ef79bbde 238#define CDR_INT(eCycle) { \
d28b54b1 239 psxRegs.interrupt |= (1 << PSXINT_CDR); \
240 psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
241 psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
242 new_dyna_set_event(PSXINT_CDR, eCycle); \
ae602c19 243}
ef79bbde 244
d9a02493 245// cdrPlaySeekReadInterrupt
df717ca9 246#define CDRPLAYSEEKREAD_INT(eCycle, isFirst) { \
247 u32 e_ = eCycle; \
d28b54b1 248 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
df717ca9 249 if (isFirst) \
250 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
251 else \
252 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
253 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
254 new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
ae602c19 255}
ef79bbde 256
39df67df 257// cdrLidSeekInterrupt
9f8b032d 258#define CDRLID_INT(eCycle) { \
259 psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
260 psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
261 psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
262 new_dyna_set_event(PSXINT_CDRLID, eCycle); \
263}
264
ef79bbde 265#define StopReading() { \
f522e63c 266 cdr.Reading = 0; \
267 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
ef79bbde
P
268}
269
270#define StopCdda() { \
f522e63c 271 if (cdr.Play && !Config.Cdda) CDR_stop(); \
272 cdr.Play = FALSE; \
273 cdr.FastForward = 0; \
274 cdr.FastBackward = 0; \
275}
276
277#define SetPlaySeekRead(x, f) { \
278 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
279 x |= f; \
ef79bbde
P
280}
281
282#define SetResultSize(size) { \
94c118c3 283 cdr.ResultP = 0; \
ef79bbde
P
284 cdr.ResultC = size; \
285 cdr.ResultReady = 1; \
286}
287
af93c8be 288static void setIrq(int log_cmd)
94c118c3 289{
290 if (cdr.Stat & cdr.Reg2)
291 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
af93c8be 292
293#ifdef CDR_LOG_CMD_IRQ
7f2576b2 294 if (cdr.Stat)
af93c8be 295 {
296 int i;
297 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
298 !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
299 for (i = 0; i < cdr.ResultC; i++)
300 SysPrintf("%02x ", cdr.Result[i]);
301 SysPrintf("\n");
302 }
303#endif
94c118c3 304}
9f8b032d 305
80f91a20 306// timing used in this function was taken from tests on real hardware
307// (yes it's slow, but you probably don't want to modify it)
d9a02493 308void cdrLidSeekInterrupt(void)
9f8b032d 309{
80f91a20 310 switch (cdr.DriveState) {
311 default:
312 case DRIVESTATE_STANDBY:
f522e63c 313 StopCdda();
314 StopReading();
315 SetPlaySeekRead(cdr.StatP, 0);
9f8b032d 316
80f91a20 317 if (CDR_getStatus(&stat) == -1)
318 return;
9f8b032d 319
80f91a20 320 if (stat.Status & STATUS_SHELLOPEN)
9f8b032d 321 {
80f91a20 322 cdr.DriveState = DRIVESTATE_LID_OPEN;
323 CDRLID_INT(0x800);
324 }
325 break;
9f8b032d 326
80f91a20 327 case DRIVESTATE_LID_OPEN:
328 if (CDR_getStatus(&stat) == -1)
329 stat.Status &= ~STATUS_SHELLOPEN;
9f8b032d 330
80f91a20 331 // 02, 12, 10
332 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
80f91a20 333 cdr.StatP |= STATUS_SHELLOPEN;
9f8b032d 334
80f91a20 335 // could generate error irq here, but real hardware
336 // only sometimes does that
337 // (not done when lots of commands are sent?)
9f8b032d 338
80f91a20 339 CDRLID_INT(cdReadTime * 30);
340 break;
9f8b032d 341 }
80f91a20 342 else if (cdr.StatP & STATUS_ROTATING) {
343 cdr.StatP &= ~STATUS_ROTATING;
344 }
345 else if (!(stat.Status & STATUS_SHELLOPEN)) {
346 // closed now
347 CheckCdrom();
9f8b032d 348
80f91a20 349 // cdr.StatP STATUS_SHELLOPEN is "sticky"
350 // and is only cleared by CdlNop
9f8b032d 351
80f91a20 352 cdr.DriveState = DRIVESTATE_RESCAN_CD;
353 CDRLID_INT(cdReadTime * 105);
354 break;
355 }
9f8b032d 356
80f91a20 357 // recheck for close
358 CDRLID_INT(cdReadTime * 3);
359 break;
9f8b032d 360
80f91a20 361 case DRIVESTATE_RESCAN_CD:
362 cdr.StatP |= STATUS_ROTATING;
363 cdr.DriveState = DRIVESTATE_PREPARE_CD;
9f8b032d 364
80f91a20 365 // this is very long on real hardware, over 6 seconds
366 // make it a bit faster here...
367 CDRLID_INT(cdReadTime * 150);
368 break;
9f8b032d 369
80f91a20 370 case DRIVESTATE_PREPARE_CD:
371 cdr.StatP |= STATUS_SEEK;
9f8b032d 372
80f91a20 373 cdr.DriveState = DRIVESTATE_STANDBY;
374 CDRLID_INT(cdReadTime * 26);
375 break;
9f8b032d 376 }
377}
378
fffad32e 379static void Find_CurTrack(const u8 *time)
380{
381 int current, sect;
9f8b032d 382
fffad32e 383 current = msf2sec(time);
9f8b032d 384
fffad32e 385 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
386 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
387 sect = fsm2sec(cdr.ResultTD);
388 if (sect - current >= 150)
9f8b032d 389 break;
9f8b032d 390 }
391}
392
73b29eeb 393static void generate_subq(const u8 *time)
394{
395 unsigned char start[3], next[3];
396 unsigned int this_s, start_s, next_s, pregap;
397 int relative_s;
398
399 CDR_getTD(cdr.CurTrack, start);
400 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
401 pregap = 150;
402 CDR_getTD(cdr.CurTrack + 1, next);
403 }
404 else {
405 // last track - cd size
406 pregap = 0;
407 next[0] = cdr.SetSectorEnd[2];
408 next[1] = cdr.SetSectorEnd[1];
409 next[2] = cdr.SetSectorEnd[0];
410 }
411
412 this_s = msf2sec(time);
413 start_s = fsm2sec(start);
414 next_s = fsm2sec(next);
415
416 cdr.TrackChanged = FALSE;
417
418 if (next_s - this_s < pregap) {
419 cdr.TrackChanged = TRUE;
420 cdr.CurTrack++;
421 start_s = next_s;
422 }
423
424 cdr.subq.Index = 1;
425
426 relative_s = this_s - start_s;
427 if (relative_s < 0) {
428 cdr.subq.Index = 0;
429 relative_s = -relative_s;
430 }
431 sec2msf(relative_s, cdr.subq.Relative);
432
433 cdr.subq.Track = itob(cdr.CurTrack);
434 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
435 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
436 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
437 cdr.subq.Absolute[0] = itob(time[0]);
438 cdr.subq.Absolute[1] = itob(time[1]);
439 cdr.subq.Absolute[2] = itob(time[2]);
440}
441
442static void ReadTrack(const u8 *time) {
fffad32e 443 unsigned char tmp[3];
444 struct SubQ *subq;
445 u16 crc;
ef79bbde 446
fffad32e 447 tmp[0] = itob(time[0]);
448 tmp[1] = itob(time[1]);
449 tmp[2] = itob(time[2]);
ef79bbde 450
fffad32e 451 if (memcmp(cdr.Prev, tmp, 3) == 0)
452 return;
39df67df 453
fffad32e 454 CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
9f8b032d 455
c9c7a925 456 cdr.NoErr = CDR_readTrack(tmp);
fffad32e 457 memcpy(cdr.Prev, tmp, 3);
9f8b032d 458
fffad32e 459 if (CheckSBI(time))
460 return;
9f8b032d 461
fffad32e 462 subq = (struct SubQ *)CDR_getBufferSub();
73b29eeb 463 if (subq != NULL && cdr.CurTrack == 1) {
fffad32e 464 crc = calcCrc((u8 *)subq + 12, 10);
73b29eeb 465 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
fffad32e 466 cdr.subq.Track = subq->TrackNumber;
467 cdr.subq.Index = subq->IndexNumber;
468 memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
469 memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
fffad32e 470 }
92ca1ba6 471 else {
73b29eeb 472 CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
92ca1ba6 473 tmp[0], tmp[1], tmp[2]);
474 }
9f8b032d 475 }
fffad32e 476 else {
73b29eeb 477 generate_subq(time);
9f8b032d 478 }
9f8b032d 479
fffad32e 480 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
481 cdr.subq.Track, cdr.subq.Index,
482 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
483 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
484}
9f8b032d 485
7f457614 486static void cdrPlayInterrupt_Autopause()
487{
bf4df3ba 488 u32 abs_lev_max = 0;
489 boolean abs_lev_chselect;
490 u32 i;
f4627eb3 491
fffad32e 492 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
7f457614 493 CDR_LOG( "CDDA STOP\n" );
7f457614 494
495 // Magic the Gathering
496 // - looping territory cdda
497
498 // ...?
499 //cdr.ResultReady = 1;
500 //cdr.Stat = DataReady;
501 cdr.Stat = DataEnd;
af93c8be 502 setIrq(0x200);
7f457614 503
504 StopCdda();
f522e63c 505 SetPlaySeekRead(cdr.StatP, 0);
7f457614 506 }
c9c7a925 507 else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
fffad32e 508 cdr.Result[0] = cdr.StatP;
509 cdr.Result[1] = cdr.subq.Track;
510 cdr.Result[2] = cdr.subq.Index;
bf4df3ba 511
512 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
513
514 /* 8 is a hack. For accuracy, it should be 588. */
515 for (i = 0; i < 8; i++)
516 {
517 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
518 }
519 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
520 abs_lev_max |= abs_lev_chselect << 15;
9b470ab7 521
fffad32e 522 if (cdr.subq.Absolute[2] & 0x10) {
523 cdr.Result[3] = cdr.subq.Relative[0];
524 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
525 cdr.Result[5] = cdr.subq.Relative[2];
7f457614 526 }
fffad32e 527 else {
528 cdr.Result[3] = cdr.subq.Absolute[0];
529 cdr.Result[4] = cdr.subq.Absolute[1];
530 cdr.Result[5] = cdr.subq.Absolute[2];
531 }
532
bf4df3ba 533 cdr.Result[6] = abs_lev_max >> 0;
534 cdr.Result[7] = abs_lev_max >> 8;
7f457614 535
536 // Rayman: Logo freeze (resultready + dataready)
537 cdr.ResultReady = 1;
538 cdr.Stat = DataReady;
539
540 SetResultSize(8);
af93c8be 541 setIrq(0x201);
7f457614 542 }
543}
544
f522e63c 545static int cdrSeekTime(unsigned char *target)
546{
547 int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
548 /*
549 * Gameblabla :
550 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
551 * and was unreliable for that game.
552 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
553 *
554 * Obviously, this isn't perfect but right now, it should be a bit better.
555 * Games to test this against if you change that setting :
556 * - Driver (titlescreen music delay and retry mission)
557 * - Worms Pinball (Will either not boot or crash in the memory card screen)
558 * - Viewpoint (short pauses if the delay in the ingame music is too long)
559 *
560 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
561 * However, 1000000 is not enough for Worms Pinball to reliably boot.
562 */
563 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
564 CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
565 return seekTime;
566}
567
d9a02493 568static void cdrReadInterrupt(void);
569
570void cdrPlaySeekReadInterrupt(void)
7f457614 571{
d9a02493 572 if (cdr.Reading) {
573 cdrReadInterrupt();
574 return;
575 }
576
577 if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
e7e33ef2 578 if (cdr.Stat) {
8b380e97 579 CDR_LOG_I("cdrom: seek stat hack\n");
df717ca9 580 CDRPLAYSEEKREAD_INT(0x1000, 1);
e7e33ef2 581 return;
582 }
39df67df 583 SetResultSize(1);
584 cdr.StatP |= STATUS_ROTATING;
d9a02493 585 SetPlaySeekRead(cdr.StatP, 0);
39df67df 586 cdr.Result[0] = cdr.StatP;
f5450cfb 587 cdr.Stat = Complete;
588 setIrq(0x202);
39df67df 589
fffad32e 590 Find_CurTrack(cdr.SetSectorPlay);
73b29eeb 591 ReadTrack(cdr.SetSectorPlay);
fffad32e 592 cdr.TrackChanged = FALSE;
d9a02493 593 return;
39df67df 594 }
595
596 if (!cdr.Play) return;
7f457614 597
7f457614 598 CDR_LOG( "CDDA - %d:%d:%d\n",
599 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
8b380e97 600
d9a02493 601 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
fffad32e 602 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
603 StopCdda();
f522e63c 604 SetPlaySeekRead(cdr.StatP, 0);
fffad32e 605 cdr.TrackChanged = TRUE;
606 }
ffd2bcfa 607 else {
608 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
609 }
7f457614 610
7f2576b2 611 if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
7f457614 612 cdrPlayInterrupt_Autopause();
613
9cf79034 614 if (!cdr.Muted && !Config.Cdda) {
ecd502e1 615 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
9cf79034 616 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
617 cdr.FirstSector = 0;
f4627eb3 618 }
fffad32e 619
7f457614 620 cdr.SetSectorPlay[2]++;
621 if (cdr.SetSectorPlay[2] == 75) {
622 cdr.SetSectorPlay[2] = 0;
623 cdr.SetSectorPlay[1]++;
624 if (cdr.SetSectorPlay[1] == 60) {
625 cdr.SetSectorPlay[1] = 0;
626 cdr.SetSectorPlay[0]++;
627 }
628 }
629
fffad32e 630 // update for CdlGetlocP/autopause
73b29eeb 631 generate_subq(cdr.SetSectorPlay);
f5450cfb 632
633 CDRPLAYSEEKREAD_INT(cdReadTime, 0);
7f457614 634}
635
d9a02493 636void cdrInterrupt(void) {
e4268a49 637 int no_busy_error = 0;
638 int start_rotating = 0;
639 int error = 0;
fbf19ce6 640 unsigned int seekTime = 0;
7f2576b2 641 u32 second_resp_time = 0;
642 u8 ParamC;
2da09dae 643 u8 set_loc[3];
7f2576b2 644 u16 Cmd;
2da09dae 645 int i;
ef79bbde
P
646
647 if (cdr.Stat) {
7f2576b2 648 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
ef79bbde
P
649 return;
650 }
651
ef79bbde
P
652 cdr.Ctrl &= ~0x80;
653
e4268a49 654 // default response
655 SetResultSize(1);
656 cdr.Result[0] = cdr.StatP;
657 cdr.Stat = Acknowledge;
658
7f2576b2 659 Cmd = cdr.CmdInProgress;
660 cdr.CmdInProgress = 0;
661 ParamC = cdr.ParamC;
e4268a49 662
7f2576b2 663 if (Cmd < 0x100) {
664 cdr.Cmd = 0;
665 cdr.ParamC = 0;
666 }
e4268a49 667
7f2576b2 668 switch (Cmd) {
ef79bbde 669 case CdlNop:
80f91a20 670 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
671 cdr.StatP &= ~STATUS_SHELLOPEN;
e4268a49 672 no_busy_error = 1;
ef79bbde
P
673 break;
674
675 case CdlSetloc:
2da09dae 676 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
677
678 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
679 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))
680 {
681 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
682 error = ERROR_INVALIDARG;
683 goto set_error;
684 }
685 else
686 {
687 for (i = 0; i < 3; i++)
2da09dae 688 set_loc[i] = btoi(cdr.Param[i]);
2da09dae 689 memcpy(cdr.SetSector, set_loc, 3);
690 cdr.SetSector[3] = 0;
691 cdr.SetlocPending = 1;
692 }
ef79bbde
P
693 break;
694
55b8460a 695 do_CdlPlay:
ef79bbde 696 case CdlPlay:
030bd76a 697 StopCdda();
f522e63c 698 StopReading();
699
c9c7a925 700 cdr.FastBackward = 0;
701 cdr.FastForward = 0;
702
2d5ca6c0 703 // BIOS CD Player
704 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
705
7f2576b2 706 if (ParamC != 0 && cdr.Param[0] != 0) {
2d5ca6c0 707 int track = btoi( cdr.Param[0] );
708
709 if (track <= cdr.ResultTN[1])
710 cdr.CurTrack = track;
711
712 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
713
714 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
f522e63c 715 for (i = 0; i < 3; i++)
716 set_loc[i] = cdr.ResultTD[2 - i];
717 seekTime = cdrSeekTime(set_loc);
718 memcpy(cdr.SetSectorPlay, set_loc, 3);
2d5ca6c0 719 }
720 }
f522e63c 721 else if (cdr.SetlocPending) {
722 seekTime = cdrSeekTime(cdr.SetSector);
723 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
724 }
725 else {
726 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
727 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
728 }
729 cdr.SetlocPending = 0;
2d5ca6c0 730
9f8b032d 731 /*
732 Rayman: detect track changes
733 - fixes logo freeze
734
735 Twisted Metal 2: skip PREGAP + starting accurate SubQ
736 - plays tracks without retry play
737
738 Wild 9: skip PREGAP + starting accurate SubQ
739 - plays tracks without retry play
740 */
fffad32e 741 Find_CurTrack(cdr.SetSectorPlay);
73b29eeb 742 ReadTrack(cdr.SetSectorPlay);
fffad32e 743 cdr.TrackChanged = FALSE;
9cf79034 744 cdr.FirstSector = 1;
9f8b032d 745
2d5ca6c0 746 if (!Config.Cdda)
747 CDR_play(cdr.SetSectorPlay);
9f8b032d 748
f522e63c 749 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
9f8b032d 750
751 // BIOS player - set flag again
752 cdr.Play = TRUE;
753
df717ca9 754 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
e4268a49 755 start_rotating = 1;
ef79bbde
P
756 break;
757
9f8b032d 758 case CdlForward:
e4268a49 759 // TODO: error 80 if stopped
9f8b032d 760 cdr.Stat = Complete;
761
9f8b032d 762 // GameShark CD Player: Calls 2x + Play 2x
c9c7a925 763 cdr.FastForward = 1;
9f8b032d 764 cdr.FastBackward = 0;
ef79bbde
P
765 break;
766
9f8b032d 767 case CdlBackward:
9f8b032d 768 cdr.Stat = Complete;
769
9f8b032d 770 // GameShark CD Player: Calls 2x + Play 2x
c9c7a925 771 cdr.FastBackward = 1;
9f8b032d 772 cdr.FastForward = 0;
ef79bbde
P
773 break;
774
9f8b032d 775 case CdlStandby:
e4268a49 776 if (cdr.DriveState != DRIVESTATE_STOPPED) {
777 error = ERROR_INVALIDARG;
778 goto set_error;
779 }
7f2576b2 780 second_resp_time = cdReadTime * 125 / 2;
e4268a49 781 start_rotating = 1;
782 break;
783
784 case CdlStandby + 0x100:
ef79bbde
P
785 cdr.Stat = Complete;
786 break;
787
788 case CdlStop:
030bd76a 789 if (cdr.Play) {
790 // grab time for current track
791 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
792
793 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
794 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
795 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
796 }
797
798 StopCdda();
799 StopReading();
f522e63c 800 SetPlaySeekRead(cdr.StatP, 0);
801 cdr.StatP &= ~STATUS_ROTATING;
030bd76a 802
7f2576b2 803 second_resp_time = 0x800;
e4268a49 804 if (cdr.DriveState == DRIVESTATE_STANDBY)
7f2576b2 805 second_resp_time = cdReadTime * 30 / 2;
e4268a49 806
807 cdr.DriveState = DRIVESTATE_STOPPED;
e4268a49 808 break;
809
810 case CdlStop + 0x100:
ef79bbde 811 cdr.Stat = Complete;
ef79bbde
P
812 break;
813
814 case CdlPause:
f522e63c 815 StopCdda();
816 StopReading();
12228917 817 /*
818 Gundam Battle Assault 2: much slower (*)
819 - Fixes boot, gameplay
820
821 Hokuto no Ken 2: slower
822 - Fixes intro + subtitles
823
824 InuYasha - Feudal Fairy Tale: slower
825 - Fixes battles
826 */
4e70ea5a 827 /* Gameblabla - Tightening the timings (as taken from Duckstation).
828 * The timings from Duckstation are based upon hardware tests.
829 * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
830 * seems to be timing sensitive as it can depend on the CPU's clock speed.
831 * */
f522e63c 832 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
e3d555e0 833 {
7f2576b2 834 second_resp_time = 7000;
e3d555e0 835 }
836 else
837 {
7f2576b2 838 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
e3d555e0 839 }
f522e63c 840 SetPlaySeekRead(cdr.StatP, 0);
ef79bbde
P
841 cdr.Ctrl |= 0x80;
842 break;
843
e4268a49 844 case CdlPause + 0x100:
9f8b032d 845 cdr.Stat = Complete;
ef79bbde
P
846 break;
847
b0bd140d 848 case CdlReset:
f522e63c 849 StopCdda();
850 StopReading();
851 SetPlaySeekRead(cdr.StatP, 0);
b0bd140d 852 cdr.Muted = FALSE;
853 cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
7f2576b2 854 second_resp_time = 4100000;
e4268a49 855 no_busy_error = 1;
856 start_rotating = 1;
857 break;
858
b0bd140d 859 case CdlReset + 0x100:
9f8b032d 860 cdr.Stat = Complete;
ef79bbde
P
861 break;
862
9f8b032d 863 case CdlMute:
030bd76a 864 cdr.Muted = TRUE;
ef79bbde
P
865 break;
866
9f8b032d 867 case CdlDemute:
030bd76a 868 cdr.Muted = FALSE;
ef79bbde
P
869 break;
870
9f8b032d 871 case CdlSetfilter:
030bd76a 872 cdr.File = cdr.Param[0];
873 cdr.Channel = cdr.Param[1];
9f8b032d 874 break;
ef79bbde
P
875
876 case CdlSetmode:
7f2576b2 877 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
878 cdr.Mode = cdr.Param[0];
e4268a49 879 no_busy_error = 1;
9f8b032d 880 break;
ef79bbde 881
dcd72441 882 case CdlGetparam:
883 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
884 SetResultSize(5);
9f8b032d 885 cdr.Result[1] = cdr.Mode;
dcd72441 886 cdr.Result[2] = 0;
887 cdr.Result[3] = cdr.File;
888 cdr.Result[4] = cdr.Channel;
e4268a49 889 no_busy_error = 1;
9f8b032d 890 break;
ef79bbde 891
9f8b032d 892 case CdlGetlocL:
ef79bbde 893 SetResultSize(8);
e4268a49 894 memcpy(cdr.Result, cdr.Transfer, 8);
9f8b032d 895 break;
ef79bbde
P
896
897 case CdlGetlocP:
fffad32e 898 SetResultSize(8);
899 memcpy(&cdr.Result, &cdr.subq, 8);
e4268a49 900 break;
892bd7d4 901
e4268a49 902 case CdlReadT: // SetSession?
903 // really long
7f2576b2 904 second_resp_time = cdReadTime * 290 / 4;
e4268a49 905 start_rotating = 1;
906 break;
907
908 case CdlReadT + 0x100:
909 cdr.Stat = Complete;
ef79bbde
P
910 break;
911
9f8b032d 912 case CdlGetTN:
ef79bbde 913 SetResultSize(3);
9f8b032d 914 if (CDR_getTN(cdr.ResultTN) == -1) {
ef79bbde 915 cdr.Stat = DiskError;
9f8b032d 916 cdr.Result[0] |= STATUS_ERROR;
917 } else {
918 cdr.Stat = Acknowledge;
919 cdr.Result[1] = itob(cdr.ResultTN[0]);
920 cdr.Result[2] = itob(cdr.ResultTN[1]);
921 }
922 break;
ef79bbde 923
9f8b032d 924 case CdlGetTD:
ef79bbde
P
925 cdr.Track = btoi(cdr.Param[0]);
926 SetResultSize(4);
ef79bbde
P
927 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
928 cdr.Stat = DiskError;
9f8b032d 929 cdr.Result[0] |= STATUS_ERROR;
ef79bbde
P
930 } else {
931 cdr.Stat = Acknowledge;
932 cdr.Result[0] = cdr.StatP;
933 cdr.Result[1] = itob(cdr.ResultTD[2]);
934 cdr.Result[2] = itob(cdr.ResultTD[1]);
cfeb7cab 935 /* According to Nocash's documentation, the function doesn't care about ff.
936 * This can be seen also in Mednafen's implementation. */
937 //cdr.Result[3] = itob(cdr.ResultTD[0]);
ef79bbde
P
938 }
939 break;
940
9f8b032d 941 case CdlSeekL:
39df67df 942 case CdlSeekP:
030bd76a 943 StopCdda();
944 StopReading();
f522e63c 945 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
9f8b032d 946
f522e63c 947 seekTime = cdrSeekTime(cdr.SetSector);
948 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
9f8b032d 949 /*
950 Crusaders of Might and Magic = 0.5x-4x
951 - fix cutscene speech start
952
953 Eggs of Steel = 2x-?
954 - fix new game
955
956 Medievil = ?-4x
957 - fix cutscene speech
958
959 Rockman X5 = 0.5-4x
960 - fix capcom logo
961 */
df717ca9 962 CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
e4268a49 963 start_rotating = 1;
ef79bbde
P
964 break;
965
966 case CdlTest:
9f8b032d 967 switch (cdr.Param[0]) {
968 case 0x20: // System Controller ROM Version
ef79bbde
P
969 SetResultSize(4);
970 memcpy(cdr.Result, Test20, 4);
971 break;
972 case 0x22:
973 SetResultSize(8);
974 memcpy(cdr.Result, Test22, 4);
975 break;
976 case 0x23: case 0x24:
977 SetResultSize(8);
978 memcpy(cdr.Result, Test23, 4);
979 break;
9f8b032d 980 }
e4268a49 981 no_busy_error = 1;
ef79bbde
P
982 break;
983
9f8b032d 984 case CdlID:
7f2576b2 985 second_resp_time = 20480;
ef79bbde
P
986 break;
987
e4268a49 988 case CdlID + 0x100:
ef79bbde 989 SetResultSize(8);
89ec56af 990 cdr.Result[0] = cdr.StatP;
991 cdr.Result[1] = 0;
992 cdr.Result[2] = 0;
993 cdr.Result[3] = 0;
9f8b032d 994
d8432250 995 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
996 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
997 cdr.Result[1] = 0xc0;
9f8b032d 998 }
999 else {
89ec56af 1000 if (stat.Type == 2)
1001 cdr.Result[1] |= 0x10;
1002 if (CdromId[0] == '\0')
9f8b032d 1003 cdr.Result[1] |= 0x80;
9f8b032d 1004 }
89ec56af 1005 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
9f8b032d 1006
d0ea0d8a 1007 /* This adds the string "PCSX" in Playstation bios boot screen */
1008 memcpy((char *)&cdr.Result[4], "PCSX", 4);
ef79bbde
P
1009 cdr.Stat = Complete;
1010 break;
1011
b0bd140d 1012 case CdlInit:
f522e63c 1013 StopCdda();
1014 StopReading();
1015 SetPlaySeekRead(cdr.StatP, 0);
e4268a49 1016 // yes, it really sets STATUS_SHELLOPEN
1017 cdr.StatP |= STATUS_SHELLOPEN;
1018 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1019 CDRLID_INT(20480);
1020 no_busy_error = 1;
1021 start_rotating = 1;
9f8b032d 1022 break;
1023
e4268a49 1024 case CdlGetQ:
6360a61b 1025 no_busy_error = 1;
9f8b032d 1026 break;
1027
1028 case CdlReadToc:
7f2576b2 1029 second_resp_time = cdReadTime * 180 / 4;
e4268a49 1030 no_busy_error = 1;
1031 start_rotating = 1;
ef79bbde
P
1032 break;
1033
e4268a49 1034 case CdlReadToc + 0x100:
9f8b032d 1035 cdr.Stat = Complete;
e4268a49 1036 no_busy_error = 1;
ef79bbde
P
1037 break;
1038
94c118c3 1039 case CdlReadN:
1040 case CdlReadS:
f522e63c 1041 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
55b8460a 1042
1043 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1044 // Read* acts as play for cdda tracks in cdda mode
1045 goto do_CdlPlay;
1046
f522e63c 1047 StopCdda();
1048 if (cdr.SetlocPending) {
1049 seekTime = cdrSeekTime(cdr.SetSector);
1050 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1051 cdr.SetlocPending = 0;
1052 }
030bd76a 1053 cdr.Reading = 1;
1054 cdr.FirstSector = 1;
ef79bbde 1055
9f8b032d 1056 // Fighting Force 2 - update subq time immediately
1057 // - fixes new game
55b8460a 1058 ReadTrack(cdr.SetSectorPlay);
9f8b032d 1059
df717ca9 1060 CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
ef79bbde 1061
f522e63c 1062 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
e4268a49 1063 start_rotating = 1;
ef79bbde 1064 break;
1f035e27 1065 case CdlSync:
ef79bbde 1066 default:
7f2576b2 1067 CDR_LOG_I("Invalid command: %02x\n", Cmd);
e4268a49 1068 error = ERROR_INVALIDCMD;
1069 // FALLTHROUGH
1070
1071 set_error:
1072 SetResultSize(2);
1073 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1074 cdr.Result[1] = error;
1075 cdr.Stat = DiskError;
ef79bbde
P
1076 break;
1077 }
1078
e4268a49 1079 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1080 cdr.DriveState = DRIVESTATE_STANDBY;
1081 cdr.StatP |= STATUS_ROTATING;
1082 }
1083
1084 if (!no_busy_error) {
80f91a20 1085 switch (cdr.DriveState) {
1086 case DRIVESTATE_LID_OPEN:
1087 case DRIVESTATE_RESCAN_CD:
1088 case DRIVESTATE_PREPARE_CD:
1089 SetResultSize(2);
1090 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
003cfc63 1091 cdr.Result[1] = ERROR_NOTREADY;
80f91a20 1092 cdr.Stat = DiskError;
1093 break;
1094 }
1095 }
9f8b032d 1096
7f2576b2 1097 if (second_resp_time) {
1098 cdr.CmdInProgress = Cmd | 0x100;
1099 CDR_INT(second_resp_time);
1100 }
1101 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1102 cdr.CmdInProgress = cdr.Cmd;
1103 CDR_LOG_I("cdrom: cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
7f2576b2 1104 }
1105
1106 setIrq(Cmd);
ef79bbde
P
1107}
1108
8f2bb0cb 1109#ifdef HAVE_ARMV7
53598a71 1110 #define ssat32_to_16(v) \
1111 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1112#else
1113 #define ssat32_to_16(v) do { \
1114 if (v < -32768) v = -32768; \
1115 else if (v > 32767) v = 32767; \
1116 } while (0)
1117#endif
1118
1119void cdrAttenuate(s16 *buf, int samples, int stereo)
1120{
1121 int i, l, r;
1122 int ll = cdr.AttenuatorLeftToLeft;
1123 int lr = cdr.AttenuatorLeftToRight;
1124 int rl = cdr.AttenuatorRightToLeft;
1125 int rr = cdr.AttenuatorRightToRight;
1126
1127 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1128 return;
1129
1130 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1131 return;
1132
1133 if (stereo) {
1134 for (i = 0; i < samples; i++) {
1135 l = buf[i * 2];
1136 r = buf[i * 2 + 1];
1137 l = (l * ll + r * rl) >> 7;
1138 r = (r * rr + l * lr) >> 7;
1139 ssat32_to_16(l);
1140 ssat32_to_16(r);
1141 buf[i * 2] = l;
1142 buf[i * 2 + 1] = r;
1143 }
1144 }
1145 else {
1146 for (i = 0; i < samples; i++) {
1147 l = buf[i];
1148 l = l * (ll + rl) >> 7;
1149 //r = r * (rr + lr) >> 7;
1150 ssat32_to_16(l);
1151 //ssat32_to_16(r);
1152 buf[i] = l;
1153 }
1154 }
1155}
1156
f5450cfb 1157static void cdrReadInterruptSetResult(unsigned char result)
d9a02493 1158{
7f2576b2 1159 if (cdr.Stat) {
f5450cfb 1160 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1161 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1162 cdr.CmdInProgress, cdr.Stat);
1163 cdr.Irq1Pending = result;
ef79bbde
P
1164 return;
1165 }
ef79bbde 1166 SetResultSize(1);
f5450cfb 1167 cdr.Result[0] = result;
1168 cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1169 setIrq(0x203);
1170}
1171
1172static void cdrUpdateTransferBuf(const u8 *buf)
1173{
1174 if (!buf)
1175 return;
1176 memcpy(cdr.Transfer, buf, DATA_SIZE);
1177 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1178 CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1179 cdr.Readed = 0;
1180}
1181
1182static void cdrReadInterrupt(void)
1183{
1184 u8 *buf = NULL, *hdr;
1185
f522e63c 1186 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
ef79bbde 1187
55b8460a 1188 ReadTrack(cdr.SetSectorPlay);
f5450cfb 1189 if (cdr.NoErr)
1190 buf = CDR_getBuffer();
ef79bbde 1191 if (buf == NULL)
c9c7a925 1192 cdr.NoErr = 0;
ef79bbde 1193
c9c7a925 1194 if (!cdr.NoErr) {
8b380e97 1195 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
ef79bbde 1196 memset(cdr.Transfer, 0, DATA_SIZE);
f5450cfb 1197 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
ef79bbde
P
1198 return;
1199 }
1200
f5450cfb 1201 if (!cdr.Irq1Pending)
1202 cdrUpdateTransferBuf(buf);
ef79bbde 1203
9f8b032d 1204 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
f5450cfb 1205 hdr = buf + 4;
9f8b032d 1206 // Firemen 2: Multi-XA files - briefings, cutscenes
1207 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
f5450cfb 1208 cdr.File = hdr[0];
1209 cdr.Channel = hdr[1];
9f8b032d 1210 }
1211
e73e384c 1212 /* Gameblabla
1213 * Skips playing on channel 255.
1214 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1215 * TODO : Check if this is the proper behaviour.
1216 * */
f5450cfb 1217 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1218 int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
ef79bbde 1219 if (!ret) {
53598a71 1220 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
9cf79034 1221 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
ef79bbde
P
1222 cdr.FirstSector = 0;
1223 }
1224 else cdr.FirstSector = -1;
1225 }
1226 }
1227
f5450cfb 1228 /*
1229 Croc 2: $40 - only FORM1 (*)
1230 Judge Dredd: $C8 - only FORM1 (*)
1231 Sim Theme Park - no adpcm at all (zero)
1232 */
1233
1234 if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1235 cdrReadInterruptSetResult(cdr.StatP);
1236
55b8460a 1237 cdr.SetSectorPlay[2]++;
1238 if (cdr.SetSectorPlay[2] == 75) {
1239 cdr.SetSectorPlay[2] = 0;
1240 cdr.SetSectorPlay[1]++;
1241 if (cdr.SetSectorPlay[1] == 60) {
1242 cdr.SetSectorPlay[1] = 0;
1243 cdr.SetSectorPlay[0]++;
9f8b032d 1244 }
1245 }
1246
f5450cfb 1247 if (!cdr.Irq1Pending) {
1248 // update for CdlGetlocP
1249 ReadTrack(cdr.SetSectorPlay);
1250 }
9f8b032d 1251
df717ca9 1252 CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
f5450cfb 1253}
9f8b032d 1254
f5450cfb 1255static void doMissedIrqs(void)
1256{
1257 if (cdr.Irq1Pending)
1258 {
1259 // hand out the "newest" sector, according to nocash
1260 cdrUpdateTransferBuf(CDR_getBuffer());
1261 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
1262 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1263 SetResultSize(1);
1264 cdr.Result[0] = cdr.Irq1Pending;
1265 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
1266 cdr.Irq1Pending = 0;
1267 setIrq(0x205);
1268 return;
ef79bbde 1269 }
f5450cfb 1270 if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) && cdr.CmdInProgress)
1271 CDR_INT(256);
ef79bbde
P
1272}
1273
1274/*
1275cdrRead0:
94c118c3 1276 bit 0,1 - mode
ef79bbde
P
1277 bit 2 - unknown
1278 bit 3 - unknown
1279 bit 4 - unknown
1280 bit 5 - 1 result ready
1281 bit 6 - 1 dma ready
1282 bit 7 - 1 command being processed
1283*/
1284
1285unsigned char cdrRead0(void) {
1286 if (cdr.ResultReady)
1287 cdr.Ctrl |= 0x20;
1288 else
1289 cdr.Ctrl &= ~0x20;
1290
7f2576b2 1291 cdr.Ctrl |= 0x40; // data fifo not empty
ef79bbde
P
1292
1293 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1294 cdr.Ctrl |= 0x18;
1295
af93c8be 1296 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
ef79bbde
P
1297
1298 return psxHu8(0x1800) = cdr.Ctrl;
1299}
1300
ef79bbde 1301void cdrWrite0(unsigned char rt) {
af93c8be 1302 CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
8b380e97 1303
50b13be9 1304 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
ef79bbde
P
1305}
1306
1307unsigned char cdrRead1(void) {
94c118c3 1308 if ((cdr.ResultP & 0xf) < cdr.ResultC)
9f8b032d 1309 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
94c118c3 1310 else
ef79bbde 1311 psxHu8(0x1801) = 0;
94c118c3 1312 cdr.ResultP++;
1313 if (cdr.ResultP == cdr.ResultC)
1314 cdr.ResultReady = 0;
1315
af93c8be 1316 CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
8b380e97 1317
ef79bbde
P
1318 return psxHu8(0x1801);
1319}
1320
1321void cdrWrite1(unsigned char rt) {
af93c8be 1322 const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1323 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
9f8b032d 1324
94c118c3 1325 switch (cdr.Ctrl & 3) {
1326 case 0:
1327 break;
1328 case 3:
53598a71 1329 cdr.AttenuatorRightToRightT = rt;
94c118c3 1330 return;
1331 default:
1332 return;
9f8b032d 1333 }
1334
8b380e97 1335#ifdef CDR_LOG_CMD_IRQ
89ec56af 1336 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
ef79bbde 1337 if (cdr.ParamC) {
af93c8be 1338 int i;
ef79bbde
P
1339 SysPrintf(" Param[%d] = {", cdr.ParamC);
1340 for (i = 0; i < cdr.ParamC; i++)
1341 SysPrintf(" %x,", cdr.Param[i]);
1342 SysPrintf("}\n");
1343 } else {
1344 SysPrintf("\n");
1345 }
1346#endif
1347
2daaaae3 1348 cdr.ResultReady = 0;
94c118c3 1349 cdr.Ctrl |= 0x80;
8b380e97 1350
7f2576b2 1351 if (!cdr.CmdInProgress) {
1352 cdr.CmdInProgress = rt;
1353 // should be something like 12k + controller delays
1354 CDR_INT(5000);
9f8b032d 1355 }
7f2576b2 1356 else {
1357 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1358 rt, cdr.Cmd, cdr.CmdInProgress);
1359 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1360 cdr.CmdInProgress = rt;
1361 }
1362
1363 cdr.Cmd = rt;
ef79bbde
P
1364}
1365
1366unsigned char cdrRead2(void) {
79cd4919 1367 unsigned char ret = 0;
ef79bbde 1368
79cd4919 1369 if (cdr.Readed && cdr.FifoOffset < DATA_SIZE)
1370 ret = cdr.Transfer[cdr.FifoOffset++];
1371 else
1372 CDR_LOG_I("cdrom: read empty fifo\n");
ef79bbde 1373
af93c8be 1374 CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
ef79bbde
P
1375 return ret;
1376}
1377
1378void cdrWrite2(unsigned char rt) {
af93c8be 1379 const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1380 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
9f8b032d 1381
94c118c3 1382 switch (cdr.Ctrl & 3) {
1383 case 0:
1384 if (cdr.ParamC < 8) // FIXME: size and wrapping
1385 cdr.Param[cdr.ParamC++] = rt;
1386 return;
1387 case 1:
1388 cdr.Reg2 = rt;
af93c8be 1389 setIrq(0x204);
94c118c3 1390 return;
1391 case 2:
53598a71 1392 cdr.AttenuatorLeftToLeftT = rt;
94c118c3 1393 return;
1394 case 3:
53598a71 1395 cdr.AttenuatorRightToLeftT = rt;
94c118c3 1396 return;
ef79bbde
P
1397 }
1398}
1399
1400unsigned char cdrRead3(void) {
94c118c3 1401 if (cdr.Ctrl & 0x1)
1402 psxHu8(0x1803) = cdr.Stat | 0xE0;
1403 else
1404 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
8b380e97 1405
af93c8be 1406 CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
ef79bbde
P
1407 return psxHu8(0x1803);
1408}
1409
1410void cdrWrite3(unsigned char rt) {
af93c8be 1411 const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1412 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
056d6759 1413
94c118c3 1414 switch (cdr.Ctrl & 3) {
1415 case 0:
a34a2445 1416 break; // transfer
94c118c3 1417 case 1:
7f2576b2 1418#ifdef CDR_LOG_CMD_IRQ
1419 if (cdr.Stat & rt)
1420 SysPrintf("ack %02x\n", cdr.Stat & rt);
1421#endif
a34a2445 1422 cdr.Stat &= ~rt;
1423
1424 if (rt & 0x40)
1425 cdr.ParamC = 0;
f5450cfb 1426 doMissedIrqs();
a34a2445 1427 return;
94c118c3 1428 case 2:
53598a71 1429 cdr.AttenuatorLeftToRightT = rt;
94c118c3 1430 return;
1431 case 3:
53598a71 1432 if (rt & 0x20) {
1433 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
7f2576b2 1434 CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
53598a71 1435 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1436 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1437 }
94c118c3 1438 return;
9f8b032d 1439 }
056d6759 1440
94c118c3 1441 if ((rt & 0x80) && cdr.Readed == 0) {
ef79bbde 1442 cdr.Readed = 1;
ef79bbde
P
1443
1444 switch (cdr.Mode & 0x30) {
9f8b032d 1445 case MODE_SIZE_2328:
ef79bbde 1446 case 0x00:
79cd4919 1447 cdr.FifoOffset = 12;
ef79bbde 1448 break;
9f8b032d 1449
1450 case MODE_SIZE_2340:
ef79bbde 1451 default:
79cd4919 1452 cdr.FifoOffset = 0;
ef79bbde
P
1453 break;
1454 }
1455 }
1456}
1457
1458void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1459 u32 cdsize;
c7071c43 1460 int size;
ef79bbde
P
1461 u8 *ptr;
1462
ef79bbde 1463 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
ef79bbde
P
1464
1465 switch (chcr) {
1466 case 0x11000000:
1467 case 0x11400100:
1468 if (cdr.Readed == 0) {
f5450cfb 1469 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NOT READY\n");
ef79bbde
P
1470 break;
1471 }
ef79bbde
P
1472 ptr = (u8 *)PSXM(madr);
1473 if (ptr == NULL) {
79cd4919 1474 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
ef79bbde
P
1475 break;
1476 }
9f8b032d 1477
79cd4919 1478 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1479
9f8b032d 1480 /*
1481 GS CDX: Enhancement CD crash
1482 - Setloc 0:0:0
1483 - CdlPlay
1484 - Spams DMA3 and gets buffer overrun
1485 */
79cd4919 1486 size = DATA_SIZE - cdr.FifoOffset;
c7071c43 1487 if (size > cdsize)
1488 size = cdsize;
1489 if (size > 0)
9f8b032d 1490 {
79cd4919 1491 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1492 cdr.FifoOffset += size;
1493 psxCpu->Clear(madr, size / 4);
9f8b032d 1494 }
1495
9f8b032d 1496 if( chcr == 0x11400100 ) {
58ebb94c 1497 HW_DMA3_MADR = SWAPu32(madr + cdsize);
9f8b032d 1498 CDRDMA_INT( (cdsize/4) / 4 );
1499 }
1500 else if( chcr == 0x11000000 ) {
fc4803bd 1501 // CDRDMA_INT( (cdsize/4) * 1 );
1502 // halted
1503 psxRegs.cycle += (cdsize/4) * 24/2;
1504 CDRDMA_INT(16);
9f8b032d 1505 }
1506 return;
1507
ef79bbde 1508 default:
ef79bbde 1509 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
ef79bbde
P
1510 break;
1511 }
1512
1513 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1514 DMA_INTERRUPT(3);
1515}
1516
d9a02493 1517void cdrDmaInterrupt(void)
9f8b032d 1518{
ad418c19 1519 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1520 {
1521 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1522 DMA_INTERRUPT(3);
1523 }
9f8b032d 1524}
1525
fffad32e 1526static void getCdInfo(void)
1527{
1528 u8 tmp;
1529
1530 CDR_getTN(cdr.ResultTN);
1531 CDR_getTD(0, cdr.SetSectorEnd);
1532 tmp = cdr.SetSectorEnd[0];
1533 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1534 cdr.SetSectorEnd[2] = tmp;
1535}
1536
ef79bbde
P
1537void cdrReset() {
1538 memset(&cdr, 0, sizeof(cdr));
1539 cdr.CurTrack = 1;
1540 cdr.File = 1;
1541 cdr.Channel = 1;
a2d3ccb8 1542 cdr.Reg2 = 0x1f;
1543 cdr.Stat = NoIntr;
80f91a20 1544 cdr.DriveState = DRIVESTATE_STANDBY;
22bbabf6 1545 cdr.StatP = STATUS_ROTATING;
79cd4919 1546 cdr.FifoOffset = DATA_SIZE; // fifo empty
c9c7a925 1547
056d6759 1548 // BIOS player - default values
53598a71 1549 cdr.AttenuatorLeftToLeft = 0x80;
1550 cdr.AttenuatorLeftToRight = 0x00;
1551 cdr.AttenuatorRightToLeft = 0x00;
1552 cdr.AttenuatorRightToRight = 0x80;
fffad32e 1553
1554 getCdInfo();
ef79bbde
P
1555}
1556
496d88d4 1557int cdrFreeze(void *f, int Mode) {
8e1040b6 1558 u32 tmp;
fffad32e 1559 u8 tmpp[3];
ef79bbde 1560
fffad32e 1561 if (Mode == 0 && !Config.Cdda)
1562 CDR_stop();
9f8b032d 1563
55b8460a 1564 cdr.freeze_ver = 0x63647202;
9f8b032d 1565 gzfreeze(&cdr, sizeof(cdr));
1566
94c118c3 1567 if (Mode == 1) {
1568 cdr.ParamP = cdr.ParamC;
79cd4919 1569 tmp = cdr.FifoOffset;
94c118c3 1570 }
ef79bbde
P
1571
1572 gzfreeze(&tmp, sizeof(tmp));
1573
42f3c512 1574 if (Mode == 0) {
fffad32e 1575 getCdInfo();
1576
79cd4919 1577 cdr.FifoOffset = tmp;
ef79bbde 1578
fffad32e 1579 // read right sub data
f84d4864
DS
1580 tmpp[0] = btoi(cdr.Prev[0]);
1581 tmpp[1] = btoi(cdr.Prev[1]);
1582 tmpp[2] = btoi(cdr.Prev[2]);
fffad32e 1583 cdr.Prev[0]++;
73b29eeb 1584 ReadTrack(tmpp);
fffad32e 1585
1586 if (cdr.Play) {
55b8460a 1587 if (cdr.freeze_ver < 0x63647202)
1588 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1589
fffad32e 1590 Find_CurTrack(cdr.SetSectorPlay);
1591 if (!Config.Cdda)
1592 CDR_play(cdr.SetSectorPlay);
d9a02493 1593 if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
df717ca9 1594 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
fffad32e 1595 }
c69642c8 1596
1597 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1598 // old versions did not latch Reg2, have to fixup..
1599 if (cdr.Reg2 == 0) {
1600 SysPrintf("cdrom: fixing up old savestate\n");
1601 cdr.Reg2 = 7;
1602 }
0f2dee0f 1603 // also did not save Attenuator..
1604 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1605 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1606 {
1607 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1608 }
c69642c8 1609 }
42f3c512 1610 }
1611
ef79bbde
P
1612 return 0;
1613}
9f8b032d 1614
d9a02493 1615void LidInterrupt(void) {
fffad32e 1616 getCdInfo();
80f91a20 1617 cdrLidSeekInterrupt();
9f8b032d 1618}