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