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