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