Fix CdlGetTD according to Mednafen's implementation.
[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
719
720 Hokuto no Ken 2: slower
721 - Fixes intro + subtitles
722
723 InuYasha - Feudal Fairy Tale: slower
724 - Fixes battles
725 */
e4268a49 726 AddIrqQueue(CdlPause + 0x100, cdReadTime * 3);
ef79bbde
P
727 cdr.Ctrl |= 0x80;
728 break;
729
e4268a49 730 case CdlPause + 0x100:
9f8b032d 731 cdr.StatP &= ~STATUS_READ;
ef79bbde 732 cdr.Result[0] = cdr.StatP;
9f8b032d 733 cdr.Stat = Complete;
d2e271b2 734 cdr.RErr = 1;
ef79bbde
P
735 break;
736
8399ca33 737 case CdlReset:
738 cdr.Muted = FALSE;
739 cdr.Mode = 0x20; /* Needed for This is Football 2, Pooh's Party and possibly others. */
740 AddIrqQueue(CdlReset + 0x100, 4100000);
e4268a49 741 no_busy_error = 1;
742 start_rotating = 1;
743 break;
744
8399ca33 745 case CdlReset + 0x100:
9f8b032d 746 cdr.Stat = Complete;
d2e271b2 747 cdr.RErr = 1;
ef79bbde
P
748 break;
749
9f8b032d 750 case CdlMute:
030bd76a 751 cdr.Muted = TRUE;
ef79bbde
P
752 break;
753
9f8b032d 754 case CdlDemute:
030bd76a 755 cdr.Muted = FALSE;
ef79bbde
P
756 break;
757
9f8b032d 758 case CdlSetfilter:
030bd76a 759 cdr.File = cdr.Param[0];
760 cdr.Channel = cdr.Param[1];
9f8b032d 761 break;
ef79bbde
P
762
763 case CdlSetmode:
e4268a49 764 no_busy_error = 1;
9f8b032d 765 break;
ef79bbde 766
95465b97 767 case CdlGetparam:
768 SetResultSize(5);
9f8b032d 769 cdr.Result[1] = cdr.Mode;
95465b97 770 cdr.Result[2] = 0;
771 cdr.Result[3] = cdr.File;
772 cdr.Result[4] = cdr.Channel;
e4268a49 773 no_busy_error = 1;
9f8b032d 774 break;
ef79bbde 775
9f8b032d 776 case CdlGetlocL:
ef79bbde 777 SetResultSize(8);
e4268a49 778 memcpy(cdr.Result, cdr.Transfer, 8);
9f8b032d 779 break;
ef79bbde
P
780
781 case CdlGetlocP:
fffad32e 782 SetResultSize(8);
783 memcpy(&cdr.Result, &cdr.subq, 8);
ef79bbde 784
892bd7d4 785 if (!cdr.Play && !cdr.Reading)
786 cdr.Result[1] = 0; // HACK?
e4268a49 787 break;
892bd7d4 788
e4268a49 789 case CdlReadT: // SetSession?
790 // really long
791 AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
792 start_rotating = 1;
793 break;
794
795 case CdlReadT + 0x100:
796 cdr.Stat = Complete;
d2e271b2 797 cdr.RErr = 1;
ef79bbde
P
798 break;
799
9f8b032d 800 case CdlGetTN:
ef79bbde 801 SetResultSize(3);
9f8b032d 802 if (CDR_getTN(cdr.ResultTN) == -1) {
ef79bbde 803 cdr.Stat = DiskError;
9f8b032d 804 cdr.Result[0] |= STATUS_ERROR;
805 } else {
806 cdr.Stat = Acknowledge;
807 cdr.Result[1] = itob(cdr.ResultTN[0]);
808 cdr.Result[2] = itob(cdr.ResultTN[1]);
809 }
810 break;
ef79bbde 811
9f8b032d 812 case CdlGetTD:
ef79bbde
P
813 cdr.Track = btoi(cdr.Param[0]);
814 SetResultSize(4);
ef79bbde
P
815 if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
816 cdr.Stat = DiskError;
9f8b032d 817 cdr.Result[0] |= STATUS_ERROR;
ef79bbde
P
818 } else {
819 cdr.Stat = Acknowledge;
820 cdr.Result[0] = cdr.StatP;
821 cdr.Result[1] = itob(cdr.ResultTD[2]);
822 cdr.Result[2] = itob(cdr.ResultTD[1]);
a2c864cc 823 /* According to Nocash's documentation, the function doesn't care about ff.
824 * This can be seen also in Mednafen's implementation. */
825 //cdr.Result[3] = itob(cdr.ResultTD[0]);
ef79bbde
P
826 }
827 break;
828
9f8b032d 829 case CdlSeekL:
39df67df 830 case CdlSeekP:
030bd76a 831 StopCdda();
832 StopReading();
9f8b032d 833 cdr.StatP |= STATUS_SEEK;
9f8b032d 834
835 /*
836 Crusaders of Might and Magic = 0.5x-4x
837 - fix cutscene speech start
838
839 Eggs of Steel = 2x-?
840 - fix new game
841
842 Medievil = ?-4x
843 - fix cutscene speech
844
845 Rockman X5 = 0.5-4x
846 - fix capcom logo
847 */
39df67df 848 CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
892bd7d4 849 cdr.Seeked = SEEK_PENDING;
e4268a49 850 start_rotating = 1;
ef79bbde
P
851 break;
852
853 case CdlTest:
9f8b032d 854 switch (cdr.Param[0]) {
855 case 0x20: // System Controller ROM Version
ef79bbde
P
856 SetResultSize(4);
857 memcpy(cdr.Result, Test20, 4);
858 break;
859 case 0x22:
860 SetResultSize(8);
861 memcpy(cdr.Result, Test22, 4);
862 break;
863 case 0x23: case 0x24:
864 SetResultSize(8);
865 memcpy(cdr.Result, Test23, 4);
866 break;
9f8b032d 867 }
e4268a49 868 no_busy_error = 1;
ef79bbde
P
869 break;
870
9f8b032d 871 case CdlID:
e4268a49 872 AddIrqQueue(CdlID + 0x100, 20480);
ef79bbde
P
873 break;
874
e4268a49 875 case CdlID + 0x100:
ef79bbde 876 SetResultSize(8);
89ec56af 877 cdr.Result[0] = cdr.StatP;
878 cdr.Result[1] = 0;
879 cdr.Result[2] = 0;
880 cdr.Result[3] = 0;
9f8b032d 881
d8432250 882 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
883 if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
884 cdr.Result[1] = 0xc0;
9f8b032d 885 }
886 else {
89ec56af 887 if (stat.Type == 2)
888 cdr.Result[1] |= 0x10;
889 if (CdromId[0] == '\0')
9f8b032d 890 cdr.Result[1] |= 0x80;
9f8b032d 891 }
89ec56af 892 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
9f8b032d 893
3e42b115 894 /* This adds the string "PCSX" in Playstation bios boot screen */
895 memcpy((char *)&cdr.Result[4], "PCSX", 4);
ef79bbde 896 cdr.Stat = Complete;
d2e271b2 897 cdr.RErr = 1;
ef79bbde
P
898 break;
899
8399ca33 900 case CdlInit:
e4268a49 901 // yes, it really sets STATUS_SHELLOPEN
902 cdr.StatP |= STATUS_SHELLOPEN;
903 cdr.DriveState = DRIVESTATE_RESCAN_CD;
904 CDRLID_INT(20480);
905 no_busy_error = 1;
906 start_rotating = 1;
9f8b032d 907 break;
908
e4268a49 909 case CdlGetQ:
e1a78bc4 910 no_busy_error = 1;
9f8b032d 911 break;
912
913 case CdlReadToc:
e4268a49 914 AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
915 no_busy_error = 1;
916 start_rotating = 1;
ef79bbde
P
917 break;
918
e4268a49 919 case CdlReadToc + 0x100:
9f8b032d 920 cdr.Stat = Complete;
d2e271b2 921 cdr.RErr = 1;
e4268a49 922 no_busy_error = 1;
ef79bbde
P
923 break;
924
94c118c3 925 case CdlReadN:
926 case CdlReadS:
55b8460a 927 if (cdr.SetlocPending) {
fbf19ce6 928 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(cdr.SetSector)) * (cdReadTime / 200);
d2e271b2 929 /*
930 * Gameblabla :
931 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
932 * and was unreliable for that game.
933 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
934 *
935 * Obviously, this isn't perfect but right now, it should be a bit better.
936 * Games to test this against if you change that setting :
937 * - Driver (titlescreen music delay and retry mission)
938 * - Worms Pinball (Will either not boot or crash in the memory card screen)
939 * - Viewpoint (short pauses if the delay in the ingame music is too long)
940 *
941 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
942 * However, 1000000 is not enough for Worms Pinball to reliably boot.
943 */
944 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
55b8460a 945 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
946 cdr.SetlocPending = 0;
068a6133 947 cdr.m_locationChanged = TRUE;
55b8460a 948 }
949 Find_CurTrack(cdr.SetSectorPlay);
950
951 if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
952 // Read* acts as play for cdda tracks in cdda mode
953 goto do_CdlPlay;
954
030bd76a 955 cdr.Reading = 1;
956 cdr.FirstSector = 1;
ef79bbde 957
9f8b032d 958 // Fighting Force 2 - update subq time immediately
959 // - fixes new game
55b8460a 960 ReadTrack(cdr.SetSectorPlay);
9f8b032d 961
962
963 // Crusaders of Might and Magic - update getlocl now
964 // - fixes cutscene speech
965 {
966 u8 *buf = CDR_getBuffer();
a48ae3a7 967 if (buf != NULL)
968 memcpy(cdr.Transfer, buf, 8);
9f8b032d 969 }
39df67df 970
9f8b032d 971 /*
972 Duke Nukem: Land of the Babes - seek then delay read for one frame
973 - fixes cutscenes
914722b2 974 C-12 - Final Resistance - doesn't like seek
9f8b032d 975 */
976
d2e271b2 977 /*
978 By nicolasnoble from PCSX Redux :
979 "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now.
980 For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect
981 the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK
982 flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game
983 tries to protect itself against errors by preventing from issuing a GetLocP while it knows the
984 last status was "seek". But this makes the logic just softlock as it'll never get a notification
985 about the fact the drive is done seeking and the read actually started.
986 In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is
987 done right away. It's rather when it's done seeking, and the read has actually started. This probably
988 requires a bit more work to make sure seek delays are processed properly.
989 Checked with a few games, this seems to work fine."
990
991 Gameblabla additional notes :
992 This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error.
993 */
994 cdr.StatP |= STATUS_READ;
995 cdr.StatP &= ~STATUS_SEEK;
996 CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
ef79bbde 997
9f8b032d 998 cdr.Result[0] = cdr.StatP;
e4268a49 999 start_rotating = 1;
ef79bbde
P
1000 break;
1001
f96ca921 1002 case CdlSync:
ef79bbde 1003 default:
e4268a49 1004 CDR_LOG_I("Invalid command: %02x\n", Irq);
1005 error = ERROR_INVALIDCMD;
1006 // FALLTHROUGH
1007
1008 set_error:
1009 SetResultSize(2);
1010 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1011 cdr.Result[1] = error;
1012 cdr.Stat = DiskError;
ef79bbde
P
1013 break;
1014 }
1015
e4268a49 1016 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1017 cdr.DriveState = DRIVESTATE_STANDBY;
1018 cdr.StatP |= STATUS_ROTATING;
1019 }
1020
1021 if (!no_busy_error) {
80f91a20 1022 switch (cdr.DriveState) {
1023 case DRIVESTATE_LID_OPEN:
1024 case DRIVESTATE_RESCAN_CD:
1025 case DRIVESTATE_PREPARE_CD:
1026 SetResultSize(2);
1027 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
003cfc63 1028 cdr.Result[1] = ERROR_NOTREADY;
80f91a20 1029 cdr.Stat = DiskError;
1030 break;
1031 }
1032 }
9f8b032d 1033
e4268a49 1034finish:
94c118c3 1035 setIrq();
e4268a49 1036 cdr.ParamC = 0;
ef79bbde 1037
8b380e97 1038#ifdef CDR_LOG_CMD_IRQ
e4268a49 1039 {
1040 int i;
1041 SysPrintf("CDR IRQ %d cmd %02x stat %02x: ",
1042 !!(cdr.Stat & cdr.Reg2), Irq, cdr.Stat);
1043 for (i = 0; i < cdr.ResultC; i++)
1044 SysPrintf("%02x ", cdr.Result[i]);
1045 SysPrintf("\n");
1046 }
ef79bbde
P
1047#endif
1048}
1049
8f2bb0cb 1050#ifdef HAVE_ARMV7
53598a71 1051 #define ssat32_to_16(v) \
1052 asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1053#else
1054 #define ssat32_to_16(v) do { \
1055 if (v < -32768) v = -32768; \
1056 else if (v > 32767) v = 32767; \
1057 } while (0)
1058#endif
1059
1060void cdrAttenuate(s16 *buf, int samples, int stereo)
1061{
1062 int i, l, r;
1063 int ll = cdr.AttenuatorLeftToLeft;
1064 int lr = cdr.AttenuatorLeftToRight;
1065 int rl = cdr.AttenuatorRightToLeft;
1066 int rr = cdr.AttenuatorRightToRight;
1067
1068 if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1069 return;
1070
1071 if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1072 return;
1073
1074 if (stereo) {
1075 for (i = 0; i < samples; i++) {
1076 l = buf[i * 2];
1077 r = buf[i * 2 + 1];
1078 l = (l * ll + r * rl) >> 7;
1079 r = (r * rr + l * lr) >> 7;
1080 ssat32_to_16(l);
1081 ssat32_to_16(r);
1082 buf[i * 2] = l;
1083 buf[i * 2 + 1] = r;
1084 }
1085 }
1086 else {
1087 for (i = 0; i < samples; i++) {
1088 l = buf[i];
1089 l = l * (ll + rl) >> 7;
1090 //r = r * (rr + lr) >> 7;
1091 ssat32_to_16(l);
1092 //ssat32_to_16(r);
1093 buf[i] = l;
1094 }
1095 }
1096}
1097
ef79bbde
P
1098void cdrReadInterrupt() {
1099 u8 *buf;
1100
1101 if (!cdr.Reading)
1102 return;
1103
9f8b032d 1104 if (cdr.Irq || cdr.Stat) {
8b380e97 1105 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
e7e33ef2 1106 CDREAD_INT(0x1000);
ef79bbde
P
1107 return;
1108 }
1109
9f8b032d 1110 cdr.OCUP = 1;
ef79bbde 1111 SetResultSize(1);
9f8b032d 1112 cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1113 cdr.StatP &= ~STATUS_SEEK;
1114 cdr.Result[0] = cdr.StatP;
39df67df 1115 cdr.Seeked = SEEK_DONE;
ef79bbde 1116
55b8460a 1117 ReadTrack(cdr.SetSectorPlay);
ef79bbde
P
1118
1119 buf = CDR_getBuffer();
1120 if (buf == NULL)
d2e271b2 1121 cdr.RErr = 0;
ef79bbde 1122
d2e271b2 1123 if (cdr.RErr == 0) {
8b380e97 1124 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
ef79bbde
P
1125 memset(cdr.Transfer, 0, DATA_SIZE);
1126 cdr.Stat = DiskError;
9f8b032d 1127 cdr.Result[0] |= STATUS_ERROR;
ef79bbde
P
1128 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1129 return;
1130 }
1131
1132 memcpy(cdr.Transfer, buf, DATA_SIZE);
1133 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1134
ef79bbde 1135
8b380e97 1136 CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
ef79bbde 1137
9f8b032d 1138 if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1139 // Firemen 2: Multi-XA files - briefings, cutscenes
1140 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1141 cdr.File = cdr.Transfer[4 + 0];
1142 cdr.Channel = cdr.Transfer[4 + 1];
1143 }
1144
0e0ad585 1145 /* Gameblabla
1146 * Skips playing on channel 255.
1147 * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1148 * TODO : Check if this is the proper behaviour.
1149 * */
cd0c9f5b 1150 if((cdr.Transfer[4 + 2] & 0x4) &&
9f8b032d 1151 (cdr.Transfer[4 + 1] == cdr.Channel) &&
0e0ad585 1152 (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
ef79bbde 1153 int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
ef79bbde 1154 if (!ret) {
53598a71 1155 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
ef79bbde
P
1156 SPU_playADPCMchannel(&cdr.Xa);
1157 cdr.FirstSector = 0;
1158 }
1159 else cdr.FirstSector = -1;
1160 }
1161 }
1162
55b8460a 1163 cdr.SetSectorPlay[2]++;
1164 if (cdr.SetSectorPlay[2] == 75) {
1165 cdr.SetSectorPlay[2] = 0;
1166 cdr.SetSectorPlay[1]++;
1167 if (cdr.SetSectorPlay[1] == 60) {
1168 cdr.SetSectorPlay[1] = 0;
1169 cdr.SetSectorPlay[0]++;
9f8b032d 1170 }
1171 }
1172
1173 cdr.Readed = 0;
1174
068a6133 1175 uint32_t delay = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
1176 if (cdr.m_locationChanged) {
1177 CDREAD_INT(delay * 30);
1178 cdr.m_locationChanged = FALSE;
1179 } else {
1180 CDREAD_INT(delay);
1181 }
9f8b032d 1182
1183 /*
1184 Croc 2: $40 - only FORM1 (*)
1185 Judge Dredd: $C8 - only FORM1 (*)
1186 Sim Theme Park - no adpcm at all (zero)
1187 */
1188
a34a2445 1189 if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
9f8b032d 1190 cdr.Stat = DataReady;
a34a2445 1191 setIrq();
ef79bbde 1192 }
9f8b032d 1193
fffad32e 1194 // update for CdlGetlocP
55b8460a 1195 ReadTrack(cdr.SetSectorPlay);
ef79bbde
P
1196}
1197
1198/*
1199cdrRead0:
94c118c3 1200 bit 0,1 - mode
ef79bbde
P
1201 bit 2 - unknown
1202 bit 3 - unknown
1203 bit 4 - unknown
1204 bit 5 - 1 result ready
1205 bit 6 - 1 dma ready
1206 bit 7 - 1 command being processed
1207*/
1208
1209unsigned char cdrRead0(void) {
1210 if (cdr.ResultReady)
1211 cdr.Ctrl |= 0x20;
1212 else
1213 cdr.Ctrl &= ~0x20;
1214
1215 if (cdr.OCUP)
1216 cdr.Ctrl |= 0x40;
1217// else
1218// cdr.Ctrl &= ~0x40;
1219
1220 // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1221 cdr.Ctrl |= 0x18;
1222
8b380e97 1223 CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl);
ef79bbde
P
1224
1225 return psxHu8(0x1800) = cdr.Ctrl;
1226}
1227
ef79bbde 1228void cdrWrite0(unsigned char rt) {
8b380e97 1229 CDR_LOG_IO("cdr w0: %02x\n", rt);
1230
50b13be9 1231 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
ef79bbde
P
1232}
1233
1234unsigned char cdrRead1(void) {
94c118c3 1235 if ((cdr.ResultP & 0xf) < cdr.ResultC)
9f8b032d 1236 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
94c118c3 1237 else
ef79bbde 1238 psxHu8(0x1801) = 0;
94c118c3 1239 cdr.ResultP++;
1240 if (cdr.ResultP == cdr.ResultC)
1241 cdr.ResultReady = 0;
1242
8b380e97 1243 CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801));
1244
ef79bbde
P
1245 return psxHu8(0x1801);
1246}
1247
1248void cdrWrite1(unsigned char rt) {
305c8c93 1249 u8 set_loc[3];
ef79bbde
P
1250 int i;
1251
8b380e97 1252 CDR_LOG_IO("cdr w1: %02x\n", rt);
9f8b032d 1253
94c118c3 1254 switch (cdr.Ctrl & 3) {
1255 case 0:
1256 break;
1257 case 3:
53598a71 1258 cdr.AttenuatorRightToRightT = rt;
94c118c3 1259 return;
1260 default:
1261 return;
9f8b032d 1262 }
1263
9f8b032d 1264 cdr.Cmd = rt;
ef79bbde
P
1265 cdr.OCUP = 0;
1266
8b380e97 1267#ifdef CDR_LOG_CMD_IRQ
89ec56af 1268 SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
ef79bbde
P
1269 if (cdr.ParamC) {
1270 SysPrintf(" Param[%d] = {", cdr.ParamC);
1271 for (i = 0; i < cdr.ParamC; i++)
1272 SysPrintf(" %x,", cdr.Param[i]);
1273 SysPrintf("}\n");
1274 } else {
1275 SysPrintf("\n");
1276 }
1277#endif
1278
2daaaae3 1279 cdr.ResultReady = 0;
94c118c3 1280 cdr.Ctrl |= 0x80;
1281 // cdr.Stat = NoIntr;
1282 AddIrqQueue(cdr.Cmd, 0x800);
2daaaae3 1283
9f8b032d 1284 switch (cdr.Cmd) {
94c118c3 1285 case CdlSetloc:
ee01b3d3 1286 CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
39df67df 1287
ee01b3d3 1288 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
1289 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))
1290 {
1291 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
1292 }
1293 else
1294 {
1295 for (i = 0; i < 3; i++)
1296 {
1297 set_loc[i] = btoi(cdr.Param[i]);
1298 }
39df67df 1299
ee01b3d3 1300 i = msf2sec(cdr.SetSectorPlay);
1301 i = abs(i - msf2sec(set_loc));
1302 if (i > 16)
1303 cdr.Seeked = SEEK_PENDING;
1304
1305 memcpy(cdr.SetSector, set_loc, 3);
1306 cdr.SetSector[3] = 0;
1307 cdr.SetlocPending = 1;
1308 }
94c118c3 1309 break;
ef79bbde 1310
94c118c3 1311 case CdlReadN:
030bd76a 1312 case CdlReadS:
94c118c3 1313 case CdlPause:
9f8b032d 1314 StopCdda();
1315 StopReading();
9f8b032d 1316 break;
1317
94c118c3 1318 case CdlInit:
8399ca33 1319 case CdlReset:
39df67df 1320 cdr.Seeked = SEEK_DONE;
9f8b032d 1321 StopCdda();
1322 StopReading();
94c118c3 1323 break;
9f8b032d 1324
ef79bbde 1325 case CdlSetmode:
9f8b032d 1326 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
8b380e97 1327
ef79bbde 1328 cdr.Mode = cdr.Param[0];
9f8b032d 1329
1330 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1331 // - fixes choppy movie sound
1332 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1333 StopCdda();
ef79bbde 1334 break;
9f8b032d 1335 }
ef79bbde
P
1336}
1337
1338unsigned char cdrRead2(void) {
1339 unsigned char ret;
1340
1341 if (cdr.Readed == 0) {
1342 ret = 0;
1343 } else {
8e1040b6 1344 ret = *pTransfer++;
ef79bbde
P
1345 }
1346
8b380e97 1347 CDR_LOG_IO("cdr r2: %02x\n", ret);
ef79bbde
P
1348 return ret;
1349}
1350
1351void cdrWrite2(unsigned char rt) {
8b380e97 1352 CDR_LOG_IO("cdr w2: %02x\n", rt);
9f8b032d 1353
94c118c3 1354 switch (cdr.Ctrl & 3) {
1355 case 0:
1356 if (cdr.ParamC < 8) // FIXME: size and wrapping
1357 cdr.Param[cdr.ParamC++] = rt;
1358 return;
1359 case 1:
1360 cdr.Reg2 = rt;
1361 setIrq();
1362 return;
1363 case 2:
53598a71 1364 cdr.AttenuatorLeftToLeftT = rt;
94c118c3 1365 return;
1366 case 3:
53598a71 1367 cdr.AttenuatorRightToLeftT = rt;
94c118c3 1368 return;
ef79bbde
P
1369 }
1370}
1371
1372unsigned char cdrRead3(void) {
94c118c3 1373 if (cdr.Ctrl & 0x1)
1374 psxHu8(0x1803) = cdr.Stat | 0xE0;
1375 else
1376 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
8b380e97 1377
1378 CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803));
ef79bbde
P
1379 return psxHu8(0x1803);
1380}
1381
1382void cdrWrite3(unsigned char rt) {
8b380e97 1383 CDR_LOG_IO("cdr w3: %02x\n", rt);
056d6759 1384
94c118c3 1385 switch (cdr.Ctrl & 3) {
1386 case 0:
a34a2445 1387 break; // transfer
94c118c3 1388 case 1:
a34a2445 1389 cdr.Stat &= ~rt;
1390
1391 if (rt & 0x40)
1392 cdr.ParamC = 0;
1393 return;
94c118c3 1394 case 2:
53598a71 1395 cdr.AttenuatorLeftToRightT = rt;
94c118c3 1396 return;
1397 case 3:
53598a71 1398 if (rt & 0x20) {
1399 memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1400 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1401 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1402 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1403 }
94c118c3 1404 return;
9f8b032d 1405 }
056d6759 1406
94c118c3 1407 if ((rt & 0x80) && cdr.Readed == 0) {
ef79bbde 1408 cdr.Readed = 1;
8e1040b6 1409 pTransfer = cdr.Transfer;
ef79bbde
P
1410
1411 switch (cdr.Mode & 0x30) {
9f8b032d 1412 case MODE_SIZE_2328:
ef79bbde 1413 case 0x00:
8e1040b6 1414 pTransfer += 12;
ef79bbde 1415 break;
9f8b032d 1416
1417 case MODE_SIZE_2340:
8e1040b6 1418 pTransfer += 0;
9f8b032d 1419 break;
1420
ef79bbde
P
1421 default:
1422 break;
1423 }
1424 }
1425}
1426
1427void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1428 u32 cdsize;
c7071c43 1429 int size;
ef79bbde
P
1430 u8 *ptr;
1431
ef79bbde 1432 CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
ef79bbde
P
1433
1434 switch (chcr) {
1435 case 0x11000000:
1436 case 0x11400100:
1437 if (cdr.Readed == 0) {
ef79bbde 1438 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
ef79bbde
P
1439 break;
1440 }
1441
1442 cdsize = (bcr & 0xffff) * 4;
1443
9f8b032d 1444 // Ape Escape: bcr = 0001 / 0000
1445 // - fix boot
1446 if( cdsize == 0 )
1447 {
cd0c9f5b 1448 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
9f8b032d 1449 case MODE_SIZE_2340: cdsize = 2340; break;
cd0c9f5b 1450 case MODE_SIZE_2328: cdsize = 2328; break;
1451 default:
1452 case MODE_SIZE_2048: cdsize = 2048; break;
9f8b032d 1453 }
1454 }
1455
1456
ef79bbde
P
1457 ptr = (u8 *)PSXM(madr);
1458 if (ptr == NULL) {
ef79bbde 1459 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
ef79bbde
P
1460 break;
1461 }
9f8b032d 1462
1463 /*
1464 GS CDX: Enhancement CD crash
1465 - Setloc 0:0:0
1466 - CdlPlay
1467 - Spams DMA3 and gets buffer overrun
1468 */
8e1040b6 1469 size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
c7071c43 1470 if (size > cdsize)
1471 size = cdsize;
1472 if (size > 0)
9f8b032d 1473 {
8e1040b6 1474 memcpy(ptr, pTransfer, size);
9f8b032d 1475 }
1476
ef79bbde 1477 psxCpu->Clear(madr, cdsize / 4);
8e1040b6 1478 pTransfer += cdsize;
9f8b032d 1479
9f8b032d 1480 if( chcr == 0x11400100 ) {
58ebb94c 1481 HW_DMA3_MADR = SWAPu32(madr + cdsize);
9f8b032d 1482 CDRDMA_INT( (cdsize/4) / 4 );
1483 }
1484 else if( chcr == 0x11000000 ) {
fc4803bd 1485 // CDRDMA_INT( (cdsize/4) * 1 );
1486 // halted
1487 psxRegs.cycle += (cdsize/4) * 24/2;
1488 CDRDMA_INT(16);
9f8b032d 1489 }
1490 return;
1491
ef79bbde 1492 default:
ef79bbde 1493 CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
ef79bbde
P
1494 break;
1495 }
1496
1497 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1498 DMA_INTERRUPT(3);
1499}
1500
9f8b032d 1501void cdrDmaInterrupt()
1502{
ad418c19 1503 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1504 {
1505 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1506 DMA_INTERRUPT(3);
1507 }
9f8b032d 1508}
1509
fffad32e 1510static void getCdInfo(void)
1511{
1512 u8 tmp;
1513
1514 CDR_getTN(cdr.ResultTN);
1515 CDR_getTD(0, cdr.SetSectorEnd);
1516 tmp = cdr.SetSectorEnd[0];
1517 cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1518 cdr.SetSectorEnd[2] = tmp;
1519}
1520
ef79bbde
P
1521void cdrReset() {
1522 memset(&cdr, 0, sizeof(cdr));
1523 cdr.CurTrack = 1;
1524 cdr.File = 1;
1525 cdr.Channel = 1;
a2d3ccb8 1526 cdr.Reg2 = 0x1f;
1527 cdr.Stat = NoIntr;
80f91a20 1528 cdr.DriveState = DRIVESTATE_STANDBY;
22bbabf6 1529 cdr.StatP = STATUS_ROTATING;
8e1040b6 1530 pTransfer = cdr.Transfer;
068a6133 1531 cdr.SetlocPending = 0;
1532 cdr.m_locationChanged = FALSE;
056d6759 1533
1534 // BIOS player - default values
53598a71 1535 cdr.AttenuatorLeftToLeft = 0x80;
1536 cdr.AttenuatorLeftToRight = 0x00;
1537 cdr.AttenuatorRightToLeft = 0x00;
1538 cdr.AttenuatorRightToRight = 0x80;
fffad32e 1539
1540 getCdInfo();
ef79bbde
P
1541}
1542
496d88d4 1543int cdrFreeze(void *f, int Mode) {
8e1040b6 1544 u32 tmp;
fffad32e 1545 u8 tmpp[3];
ef79bbde 1546
fffad32e 1547 if (Mode == 0 && !Config.Cdda)
1548 CDR_stop();
9f8b032d 1549
55b8460a 1550 cdr.freeze_ver = 0x63647202;
9f8b032d 1551 gzfreeze(&cdr, sizeof(cdr));
1552
94c118c3 1553 if (Mode == 1) {
1554 cdr.ParamP = cdr.ParamC;
8e1040b6 1555 tmp = pTransfer - cdr.Transfer;
94c118c3 1556 }
ef79bbde
P
1557
1558 gzfreeze(&tmp, sizeof(tmp));
1559
42f3c512 1560 if (Mode == 0) {
fffad32e 1561 getCdInfo();
1562
8e1040b6 1563 pTransfer = cdr.Transfer + tmp;
ef79bbde 1564
fffad32e 1565 // read right sub data
0204c39a
DS
1566 tmpp[0] = btoi(cdr.Prev[0]);
1567 tmpp[1] = btoi(cdr.Prev[1]);
1568 tmpp[2] = btoi(cdr.Prev[2]);
fffad32e 1569 cdr.Prev[0]++;
73b29eeb 1570 ReadTrack(tmpp);
fffad32e 1571
1572 if (cdr.Play) {
55b8460a 1573 if (cdr.freeze_ver < 0x63647202)
1574 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1575
fffad32e 1576 Find_CurTrack(cdr.SetSectorPlay);
1577 if (!Config.Cdda)
1578 CDR_play(cdr.SetSectorPlay);
1579 }
c69642c8 1580
1581 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1582 // old versions did not latch Reg2, have to fixup..
1583 if (cdr.Reg2 == 0) {
1584 SysPrintf("cdrom: fixing up old savestate\n");
1585 cdr.Reg2 = 7;
1586 }
0f2dee0f 1587 // also did not save Attenuator..
1588 if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1589 | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1590 {
1591 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1592 }
c69642c8 1593 }
42f3c512 1594 }
1595
ef79bbde
P
1596 return 0;
1597}
9f8b032d 1598
1599void LidInterrupt() {
fffad32e 1600 getCdInfo();
fffad32e 1601 StopCdda();
80f91a20 1602 cdrLidSeekInterrupt();
9f8b032d 1603}