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