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