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