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