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