cdrom: delay the missed irq more
[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 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
549         /*
550         * Gameblabla :
551         * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
552         * and was unreliable for that game.
553         * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
554         *
555         * Obviously, this isn't perfect but right now, it should be a bit better.
556         * Games to test this against if you change that setting :
557         * - Driver (titlescreen music delay and retry mission)
558         * - Worms Pinball (Will either not boot or crash in the memory card screen)
559         * - Viewpoint (short pauses if the delay in the ingame music is too long)
560         *
561         * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
562         * However, 1000000 is not enough for Worms Pinball to reliably boot.
563         */
564         if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
565         CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
566         return seekTime;
567 }
568
569 static void cdrUpdateTransferBuf(const u8 *buf);
570 static void cdrReadInterrupt(void);
571 static void cdrPrepCdda(s16 *buf, int samples);
572 static void cdrAttenuate(s16 *buf, int samples, int stereo);
573
574 void cdrPlaySeekReadInterrupt(void)
575 {
576         if (cdr.Reading) {
577                 cdrReadInterrupt();
578                 return;
579         }
580
581         if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
582                 if (cdr.Stat) {
583                         CDR_LOG_I("cdrom: seek stat hack\n");
584                         CDRPLAYSEEKREAD_INT(0x1000, 1);
585                         return;
586                 }
587                 SetResultSize(1);
588                 cdr.StatP |= STATUS_ROTATING;
589                 SetPlaySeekRead(cdr.StatP, 0);
590                 cdr.Result[0] = cdr.StatP;
591                 cdr.Stat = Complete;
592                 setIrq(0x202);
593
594                 Find_CurTrack(cdr.SetSectorPlay);
595                 ReadTrack(cdr.SetSectorPlay);
596                 cdr.TrackChanged = FALSE;
597                 return;
598         }
599
600         if (!cdr.Play) return;
601
602         CDR_LOG( "CDDA - %d:%d:%d\n",
603                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
604
605         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
606         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
607                 StopCdda();
608                 SetPlaySeekRead(cdr.StatP, 0);
609                 cdr.TrackChanged = TRUE;
610         }
611         else {
612                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
613         }
614
615         if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
616                 cdrPlayInterrupt_Autopause();
617
618         if (!cdr.Muted && !Config.Cdda) {
619                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
620                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
621                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
622                 cdr.FirstSector = 0;
623         }
624
625         cdr.SetSectorPlay[2]++;
626         if (cdr.SetSectorPlay[2] == 75) {
627                 cdr.SetSectorPlay[2] = 0;
628                 cdr.SetSectorPlay[1]++;
629                 if (cdr.SetSectorPlay[1] == 60) {
630                         cdr.SetSectorPlay[1] = 0;
631                         cdr.SetSectorPlay[0]++;
632                 }
633         }
634
635         // update for CdlGetlocP/autopause
636         generate_subq(cdr.SetSectorPlay);
637
638         CDRPLAYSEEKREAD_INT(cdReadTime, 0);
639 }
640
641 void cdrInterrupt(void) {
642         int no_busy_error = 0;
643         int start_rotating = 0;
644         int error = 0;
645         unsigned int seekTime = 0;
646         u32 second_resp_time = 0;
647         u8 ParamC;
648         u8 set_loc[3];
649         u16 Cmd;
650         int i;
651
652         if (cdr.Stat) {
653                 CDR_LOG_I("cdrom: cmd %02x with irqstat %x\n", cdr.CmdInProgress, cdr.Stat);
654                 return;
655         }
656         if (cdr.Irq1Pending) {
657                 // hand out the "newest" sector, according to nocash
658                 cdrUpdateTransferBuf(CDR_getBuffer());
659                 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
660                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
661                 SetResultSize(1);
662                 cdr.Result[0] = cdr.Irq1Pending;
663                 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
664                 cdr.Irq1Pending = 0;
665                 setIrq(0x205);
666                 return;
667         }
668
669         cdr.Ctrl &= ~0x80;
670
671         // default response
672         SetResultSize(1);
673         cdr.Result[0] = cdr.StatP;
674         cdr.Stat = Acknowledge;
675
676         Cmd = cdr.CmdInProgress;
677         cdr.CmdInProgress = 0;
678         ParamC = cdr.ParamC;
679
680         if (Cmd < 0x100) {
681                 cdr.Cmd = 0;
682                 cdr.ParamC = 0;
683         }
684
685         switch (Cmd) {
686                 case CdlNop:
687                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
688                                 cdr.StatP &= ~STATUS_SHELLOPEN;
689                         no_busy_error = 1;
690                         break;
691
692                 case CdlSetloc:
693                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
694
695                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
696                         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))
697                         {
698                                 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
699                                 error = ERROR_INVALIDARG;
700                                 goto set_error;
701                         }
702                         else
703                         {
704                                 for (i = 0; i < 3; i++)
705                                         set_loc[i] = btoi(cdr.Param[i]);
706                                 memcpy(cdr.SetSector, set_loc, 3);
707                                 cdr.SetSector[3] = 0;
708                                 cdr.SetlocPending = 1;
709                         }
710                         break;
711
712                 do_CdlPlay:
713                 case CdlPlay:
714                         StopCdda();
715                         StopReading();
716
717                         cdr.FastBackward = 0;
718                         cdr.FastForward = 0;
719
720                         // BIOS CD Player
721                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
722
723                         if (ParamC != 0 && cdr.Param[0] != 0) {
724                                 int track = btoi( cdr.Param[0] );
725
726                                 if (track <= cdr.ResultTN[1])
727                                         cdr.CurTrack = track;
728
729                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
730
731                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
732                                         for (i = 0; i < 3; i++)
733                                                 set_loc[i] = cdr.ResultTD[2 - i];
734                                         seekTime = cdrSeekTime(set_loc);
735                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
736                                 }
737                         }
738                         else if (cdr.SetlocPending) {
739                                 seekTime = cdrSeekTime(cdr.SetSector);
740                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
741                         }
742                         else {
743                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
744                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
745                         }
746                         cdr.SetlocPending = 0;
747
748                         /*
749                         Rayman: detect track changes
750                         - fixes logo freeze
751
752                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
753                         - plays tracks without retry play
754
755                         Wild 9: skip PREGAP + starting accurate SubQ
756                         - plays tracks without retry play
757                         */
758                         Find_CurTrack(cdr.SetSectorPlay);
759                         ReadTrack(cdr.SetSectorPlay);
760                         cdr.TrackChanged = FALSE;
761                         cdr.FirstSector = 1;
762
763                         if (!Config.Cdda)
764                                 CDR_play(cdr.SetSectorPlay);
765
766                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
767                         
768                         // BIOS player - set flag again
769                         cdr.Play = TRUE;
770
771                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
772                         start_rotating = 1;
773                         break;
774
775                 case CdlForward:
776                         // TODO: error 80 if stopped
777                         cdr.Stat = Complete;
778
779                         // GameShark CD Player: Calls 2x + Play 2x
780                         cdr.FastForward = 1;
781                         cdr.FastBackward = 0;
782                         break;
783
784                 case CdlBackward:
785                         cdr.Stat = Complete;
786
787                         // GameShark CD Player: Calls 2x + Play 2x
788                         cdr.FastBackward = 1;
789                         cdr.FastForward = 0;
790                         break;
791
792                 case CdlStandby:
793                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
794                                 error = ERROR_INVALIDARG;
795                                 goto set_error;
796                         }
797                         second_resp_time = cdReadTime * 125 / 2;
798                         start_rotating = 1;
799                         break;
800
801                 case CdlStandby + 0x100:
802                         cdr.Stat = Complete;
803                         break;
804
805                 case CdlStop:
806                         if (cdr.Play) {
807                                 // grab time for current track
808                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
809
810                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
811                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
812                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
813                         }
814
815                         StopCdda();
816                         StopReading();
817                         SetPlaySeekRead(cdr.StatP, 0);
818                         cdr.StatP &= ~STATUS_ROTATING;
819
820                         second_resp_time = 0x800;
821                         if (cdr.DriveState == DRIVESTATE_STANDBY)
822                                 second_resp_time = cdReadTime * 30 / 2;
823
824                         cdr.DriveState = DRIVESTATE_STOPPED;
825                         break;
826
827                 case CdlStop + 0x100:
828                         cdr.Stat = Complete;
829                         break;
830
831                 case CdlPause:
832                         StopCdda();
833                         StopReading();
834                         /*
835                         Gundam Battle Assault 2: much slower (*)
836                         - Fixes boot, gameplay
837
838                         Hokuto no Ken 2: slower
839                         - Fixes intro + subtitles
840
841                         InuYasha - Feudal Fairy Tale: slower
842                         - Fixes battles
843                         */
844                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
845                          * The timings from Duckstation are based upon hardware tests.
846                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
847                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
848                          * */
849                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
850                         {
851                                 second_resp_time = 7000;
852                         }
853                         else
854                         {
855                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * 1000000);
856                         }
857                         SetPlaySeekRead(cdr.StatP, 0);
858                         cdr.Ctrl |= 0x80;
859                         break;
860
861                 case CdlPause + 0x100:
862                         cdr.Stat = Complete;
863                         break;
864
865                 case CdlReset:
866                         StopCdda();
867                         StopReading();
868                         SetPlaySeekRead(cdr.StatP, 0);
869                         cdr.Muted = FALSE;
870                         cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
871                         second_resp_time = 4100000;
872                         no_busy_error = 1;
873                         start_rotating = 1;
874                         break;
875
876                 case CdlReset + 0x100:
877                         cdr.Stat = Complete;
878                         break;
879
880                 case CdlMute:
881                         cdr.Muted = TRUE;
882                         break;
883
884                 case CdlDemute:
885                         cdr.Muted = FALSE;
886                         break;
887
888                 case CdlSetfilter:
889                         cdr.File = cdr.Param[0];
890                         cdr.Channel = cdr.Param[1];
891                         break;
892
893                 case CdlSetmode:
894                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
895                         cdr.Mode = cdr.Param[0];
896                         no_busy_error = 1;
897                         break;
898
899                 case CdlGetparam:
900                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
901                         SetResultSize(5);
902                         cdr.Result[1] = cdr.Mode;
903                         cdr.Result[2] = 0;
904                         cdr.Result[3] = cdr.File;
905                         cdr.Result[4] = cdr.Channel;
906                         no_busy_error = 1;
907                         break;
908
909                 case CdlGetlocL:
910                         SetResultSize(8);
911                         memcpy(cdr.Result, cdr.Transfer, 8);
912                         break;
913
914                 case CdlGetlocP:
915                         SetResultSize(8);
916                         memcpy(&cdr.Result, &cdr.subq, 8);
917                         break;
918
919                 case CdlReadT: // SetSession?
920                         // really long
921                         second_resp_time = cdReadTime * 290 / 4;
922                         start_rotating = 1;
923                         break;
924
925                 case CdlReadT + 0x100:
926                         cdr.Stat = Complete;
927                         break;
928
929                 case CdlGetTN:
930                         SetResultSize(3);
931                         if (CDR_getTN(cdr.ResultTN) == -1) {
932                                 cdr.Stat = DiskError;
933                                 cdr.Result[0] |= STATUS_ERROR;
934                         } else {
935                                 cdr.Stat = Acknowledge;
936                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
937                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
938                         }
939                         break;
940
941                 case CdlGetTD:
942                         cdr.Track = btoi(cdr.Param[0]);
943                         SetResultSize(4);
944                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
945                                 cdr.Stat = DiskError;
946                                 cdr.Result[0] |= STATUS_ERROR;
947                         } else {
948                                 cdr.Stat = Acknowledge;
949                                 cdr.Result[0] = cdr.StatP;
950                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
951                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
952                                 /* According to Nocash's documentation, the function doesn't care about ff.
953                                  * This can be seen also in Mednafen's implementation. */
954                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
955                         }
956                         break;
957
958                 case CdlSeekL:
959                 case CdlSeekP:
960                         StopCdda();
961                         StopReading();
962                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
963
964                         seekTime = cdrSeekTime(cdr.SetSector);
965                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
966                         /*
967                         Crusaders of Might and Magic = 0.5x-4x
968                         - fix cutscene speech start
969
970                         Eggs of Steel = 2x-?
971                         - fix new game
972
973                         Medievil = ?-4x
974                         - fix cutscene speech
975
976                         Rockman X5 = 0.5-4x
977                         - fix capcom logo
978                         */
979                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
980                         start_rotating = 1;
981                         break;
982
983                 case CdlTest:
984                         switch (cdr.Param[0]) {
985                                 case 0x20: // System Controller ROM Version
986                                         SetResultSize(4);
987                                         memcpy(cdr.Result, Test20, 4);
988                                         break;
989                                 case 0x22:
990                                         SetResultSize(8);
991                                         memcpy(cdr.Result, Test22, 4);
992                                         break;
993                                 case 0x23: case 0x24:
994                                         SetResultSize(8);
995                                         memcpy(cdr.Result, Test23, 4);
996                                         break;
997                         }
998                         no_busy_error = 1;
999                         break;
1000
1001                 case CdlID:
1002                         second_resp_time = 20480;
1003                         break;
1004
1005                 case CdlID + 0x100:
1006                         SetResultSize(8);
1007                         cdr.Result[0] = cdr.StatP;
1008                         cdr.Result[1] = 0;
1009                         cdr.Result[2] = 0;
1010                         cdr.Result[3] = 0;
1011
1012                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1013                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1014                                 cdr.Result[1] = 0xc0;
1015                         }
1016                         else {
1017                                 if (stat.Type == 2)
1018                                         cdr.Result[1] |= 0x10;
1019                                 if (CdromId[0] == '\0')
1020                                         cdr.Result[1] |= 0x80;
1021                         }
1022                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1023
1024                         /* This adds the string "PCSX" in Playstation bios boot screen */
1025                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1026                         cdr.Stat = Complete;
1027                         break;
1028
1029                 case CdlInit:
1030                         StopCdda();
1031                         StopReading();
1032                         SetPlaySeekRead(cdr.StatP, 0);
1033                         // yes, it really sets STATUS_SHELLOPEN
1034                         cdr.StatP |= STATUS_SHELLOPEN;
1035                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1036                         CDRLID_INT(20480);
1037                         no_busy_error = 1;
1038                         start_rotating = 1;
1039                         break;
1040
1041                 case CdlGetQ:
1042                         no_busy_error = 1;
1043                         break;
1044
1045                 case CdlReadToc:
1046                         second_resp_time = cdReadTime * 180 / 4;
1047                         no_busy_error = 1;
1048                         start_rotating = 1;
1049                         break;
1050
1051                 case CdlReadToc + 0x100:
1052                         cdr.Stat = Complete;
1053                         no_busy_error = 1;
1054                         break;
1055
1056                 case CdlReadN:
1057                 case CdlReadS:
1058                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1059
1060                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1061                                 // Read* acts as play for cdda tracks in cdda mode
1062                                 goto do_CdlPlay;
1063
1064                         StopCdda();
1065                         if (cdr.SetlocPending) {
1066                                 seekTime = cdrSeekTime(cdr.SetSector);
1067                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1068                                 cdr.SetlocPending = 0;
1069                         }
1070                         cdr.Reading = 1;
1071                         cdr.FirstSector = 1;
1072
1073                         // Fighting Force 2 - update subq time immediately
1074                         // - fixes new game
1075                         ReadTrack(cdr.SetSectorPlay);
1076
1077                         CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
1078
1079                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1080                         start_rotating = 1;
1081                         break;
1082                 case CdlSync:
1083                 default:
1084                         CDR_LOG_I("Invalid command: %02x\n", Cmd);
1085                         error = ERROR_INVALIDCMD;
1086                         // FALLTHROUGH
1087
1088                 set_error:
1089                         SetResultSize(2);
1090                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1091                         cdr.Result[1] = error;
1092                         cdr.Stat = DiskError;
1093                         break;
1094         }
1095
1096         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1097                 cdr.DriveState = DRIVESTATE_STANDBY;
1098                 cdr.StatP |= STATUS_ROTATING;
1099         }
1100
1101         if (!no_busy_error) {
1102                 switch (cdr.DriveState) {
1103                 case DRIVESTATE_LID_OPEN:
1104                 case DRIVESTATE_RESCAN_CD:
1105                 case DRIVESTATE_PREPARE_CD:
1106                         SetResultSize(2);
1107                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1108                         cdr.Result[1] = ERROR_NOTREADY;
1109                         cdr.Stat = DiskError;
1110                         break;
1111                 }
1112         }
1113
1114         if (second_resp_time) {
1115                 cdr.CmdInProgress = Cmd | 0x100;
1116                 CDR_INT(second_resp_time);
1117         }
1118         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1119                 cdr.CmdInProgress = cdr.Cmd;
1120                 CDR_LOG_I("cdrom: cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1121         }
1122
1123         setIrq(Cmd);
1124 }
1125
1126 #ifdef HAVE_ARMV7
1127  #define ssat32_to_16(v) \
1128   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1129 #else
1130  #define ssat32_to_16(v) do { \
1131   if (v < -32768) v = -32768; \
1132   else if (v > 32767) v = 32767; \
1133  } while (0)
1134 #endif
1135
1136 static void cdrPrepCdda(s16 *buf, int samples)
1137 {
1138 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1139         int i;
1140         for (i = 0; i < samples; i++) {
1141                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1142                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1143         }
1144 #endif
1145 }
1146
1147 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1148 {
1149         int i, l, r;
1150         int ll = cdr.AttenuatorLeftToLeft;
1151         int lr = cdr.AttenuatorLeftToRight;
1152         int rl = cdr.AttenuatorRightToLeft;
1153         int rr = cdr.AttenuatorRightToRight;
1154
1155         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1156                 return;
1157
1158         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1159                 return;
1160
1161         if (stereo) {
1162                 for (i = 0; i < samples; i++) {
1163                         l = buf[i * 2];
1164                         r = buf[i * 2 + 1];
1165                         l = (l * ll + r * rl) >> 7;
1166                         r = (r * rr + l * lr) >> 7;
1167                         ssat32_to_16(l);
1168                         ssat32_to_16(r);
1169                         buf[i * 2] = l;
1170                         buf[i * 2 + 1] = r;
1171                 }
1172         }
1173         else {
1174                 for (i = 0; i < samples; i++) {
1175                         l = buf[i];
1176                         l = l * (ll + rl) >> 7;
1177                         //r = r * (rr + lr) >> 7;
1178                         ssat32_to_16(l);
1179                         //ssat32_to_16(r);
1180                         buf[i] = l;
1181                 }
1182         }
1183 }
1184
1185 static void cdrReadInterruptSetResult(unsigned char result)
1186 {
1187         if (cdr.Stat) {
1188                 CDR_LOG_I("cdrom: %d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1189                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1190                         cdr.CmdInProgress, cdr.Stat);
1191                 cdr.Irq1Pending = result;
1192                 return;
1193         }
1194         SetResultSize(1);
1195         cdr.Result[0] = result;
1196         cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1197         setIrq(0x203);
1198 }
1199
1200 static void cdrUpdateTransferBuf(const u8 *buf)
1201 {
1202         if (!buf)
1203                 return;
1204         memcpy(cdr.Transfer, buf, DATA_SIZE);
1205         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1206         CDR_LOG("cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1207         if (cdr.FifoOffset < 2048 + 12)
1208                 CDR_LOG("cdrom: FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1209 }
1210
1211 static void cdrReadInterrupt(void)
1212 {
1213         u8 *buf = NULL, *hdr;
1214
1215         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1216
1217         ReadTrack(cdr.SetSectorPlay);
1218         if (cdr.NoErr)
1219                 buf = CDR_getBuffer();
1220         if (buf == NULL)
1221                 cdr.NoErr = 0;
1222
1223         if (!cdr.NoErr) {
1224                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1225                 memset(cdr.Transfer, 0, DATA_SIZE);
1226                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1227                 return;
1228         }
1229
1230         if (!cdr.Irq1Pending)
1231                 cdrUpdateTransferBuf(buf);
1232
1233         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1234                 hdr = buf + 4;
1235                 // Firemen 2: Multi-XA files - briefings, cutscenes
1236                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1237                         cdr.File = hdr[0];
1238                         cdr.Channel = hdr[1];
1239                 }
1240
1241                 /* Gameblabla 
1242                  * Skips playing on channel 255.
1243                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1244                  * TODO : Check if this is the proper behaviour.
1245                  * */
1246                 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1247                         int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1248                         if (!ret) {
1249                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1250                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1251                                 cdr.FirstSector = 0;
1252                         }
1253                         else cdr.FirstSector = -1;
1254                 }
1255         }
1256
1257         /*
1258         Croc 2: $40 - only FORM1 (*)
1259         Judge Dredd: $C8 - only FORM1 (*)
1260         Sim Theme Park - no adpcm at all (zero)
1261         */
1262
1263         if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1264                 cdrReadInterruptSetResult(cdr.StatP);
1265
1266         cdr.SetSectorPlay[2]++;
1267         if (cdr.SetSectorPlay[2] == 75) {
1268                 cdr.SetSectorPlay[2] = 0;
1269                 cdr.SetSectorPlay[1]++;
1270                 if (cdr.SetSectorPlay[1] == 60) {
1271                         cdr.SetSectorPlay[1] = 0;
1272                         cdr.SetSectorPlay[0]++;
1273                 }
1274         }
1275
1276         if (!cdr.Irq1Pending) {
1277                 // update for CdlGetlocP
1278                 ReadTrack(cdr.SetSectorPlay);
1279         }
1280
1281         CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1282 }
1283
1284 /*
1285 cdrRead0:
1286         bit 0,1 - mode
1287         bit 2 - unknown
1288         bit 3 - unknown
1289         bit 4 - unknown
1290         bit 5 - 1 result ready
1291         bit 6 - 1 dma ready
1292         bit 7 - 1 command being processed
1293 */
1294
1295 unsigned char cdrRead0(void) {
1296         if (cdr.ResultReady)
1297                 cdr.Ctrl |= 0x20;
1298         else
1299                 cdr.Ctrl &= ~0x20;
1300
1301         cdr.Ctrl |= 0x40; // data fifo not empty
1302
1303         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1304         cdr.Ctrl |= 0x18;
1305
1306         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1307
1308         return psxHu8(0x1800) = cdr.Ctrl;
1309 }
1310
1311 void cdrWrite0(unsigned char rt) {
1312         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1313
1314         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1315 }
1316
1317 unsigned char cdrRead1(void) {
1318         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1319                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1320         else
1321                 psxHu8(0x1801) = 0;
1322         cdr.ResultP++;
1323         if (cdr.ResultP == cdr.ResultC)
1324                 cdr.ResultReady = 0;
1325
1326         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1327
1328         return psxHu8(0x1801);
1329 }
1330
1331 void cdrWrite1(unsigned char rt) {
1332         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1333         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1334
1335         switch (cdr.Ctrl & 3) {
1336         case 0:
1337                 break;
1338         case 3:
1339                 cdr.AttenuatorRightToRightT = rt;
1340                 return;
1341         default:
1342                 return;
1343         }
1344
1345 #ifdef CDR_LOG_CMD_IRQ
1346         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1347         if (cdr.ParamC) {
1348                 int i;
1349                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1350                 for (i = 0; i < cdr.ParamC; i++)
1351                         SysPrintf(" %x,", cdr.Param[i]);
1352                 SysPrintf("}\n");
1353         } else {
1354                 SysPrintf("\n");
1355         }
1356 #endif
1357
1358         cdr.ResultReady = 0;
1359         cdr.Ctrl |= 0x80;
1360
1361         if (!cdr.CmdInProgress) {
1362                 cdr.CmdInProgress = rt;
1363                 // should be something like 12k + controller delays
1364                 CDR_INT(5000);
1365         }
1366         else {
1367                 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1368                         rt, cdr.Cmd, cdr.CmdInProgress);
1369                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1370                         cdr.CmdInProgress = rt;
1371         }
1372
1373         cdr.Cmd = rt;
1374 }
1375
1376 unsigned char cdrRead2(void) {
1377         unsigned char ret = 0;
1378
1379         if (cdr.FifoOffset < cdr.FifoSize)
1380                 ret = cdr.Transfer[cdr.FifoOffset++];
1381         else
1382                 CDR_LOG_I("cdrom: read empty fifo (%d)\n", cdr.FifoSize);
1383
1384         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1385         return ret;
1386 }
1387
1388 void cdrWrite2(unsigned char rt) {
1389         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1390         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1391
1392         switch (cdr.Ctrl & 3) {
1393         case 0:
1394                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1395                         cdr.Param[cdr.ParamC++] = rt;
1396                 return;
1397         case 1:
1398                 cdr.Reg2 = rt;
1399                 setIrq(0x204);
1400                 return;
1401         case 2:
1402                 cdr.AttenuatorLeftToLeftT = rt;
1403                 return;
1404         case 3:
1405                 cdr.AttenuatorRightToLeftT = rt;
1406                 return;
1407         }
1408 }
1409
1410 unsigned char cdrRead3(void) {
1411         if (cdr.Ctrl & 0x1)
1412                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1413         else
1414                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1415
1416         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1417         return psxHu8(0x1803);
1418 }
1419
1420 void cdrWrite3(unsigned char rt) {
1421         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1422         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1423
1424         switch (cdr.Ctrl & 3) {
1425         case 0:
1426                 break; // transfer
1427         case 1:
1428                 if (cdr.Stat & rt) {
1429 #ifdef CDR_LOG_CMD_IRQ
1430                         SysPrintf("ack %02x (w %02x)\n", cdr.Stat & rt, rt);
1431 #endif
1432                         if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) &&
1433                             (cdr.CmdInProgress || cdr.Irq1Pending))
1434                                 CDR_INT(2000); // 710+
1435                 }
1436                 cdr.Stat &= ~rt;
1437
1438                 if (rt & 0x40)
1439                         cdr.ParamC = 0;
1440                 return;
1441         case 2:
1442                 cdr.AttenuatorLeftToRightT = rt;
1443                 return;
1444         case 3:
1445                 if (rt & 0x20) {
1446                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1447                         CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1448                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1449                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1450                 }
1451                 return;
1452         }
1453
1454         // test: Viewpoint
1455         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1456                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1457         }
1458         else if (rt & 0x80) {
1459                 switch (cdr.Mode & 0x30) {
1460                         case MODE_SIZE_2328:
1461                         case 0x00:
1462                                 cdr.FifoOffset = 12;
1463                                 cdr.FifoSize = 2048 + 12;
1464                                 break;
1465
1466                         case MODE_SIZE_2340:
1467                         default:
1468                                 cdr.FifoOffset = 0;
1469                                 cdr.FifoSize = 2340;
1470                                 break;
1471                 }
1472         }
1473         else if (!(rt & 0xc0))
1474                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1475 }
1476
1477 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1478         u32 cdsize;
1479         int size;
1480         u8 *ptr;
1481
1482         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1483
1484         switch (chcr & 0x71000000) {
1485                 case 0x11000000:
1486                         ptr = (u8 *)PSXM(madr);
1487                         if (ptr == NULL) {
1488                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1489                                 break;
1490                         }
1491
1492                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1493
1494                         /*
1495                         GS CDX: Enhancement CD crash
1496                         - Setloc 0:0:0
1497                         - CdlPlay
1498                         - Spams DMA3 and gets buffer overrun
1499                         */
1500                         size = DATA_SIZE - cdr.FifoOffset;
1501                         if (size > cdsize)
1502                                 size = cdsize;
1503                         if (size > 0)
1504                         {
1505                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1506                                 cdr.FifoOffset += size;
1507                                 psxCpu->Clear(madr, size / 4);
1508                         }
1509                         if (size < cdsize)
1510                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1511
1512                         CDRDMA_INT((cdsize/4) * 24);
1513
1514                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1515                         if (chcr & 0x100) {
1516                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1517                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1518                         }
1519                         else {
1520                                 // halted
1521                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1522                         }
1523                         return;
1524
1525                 default:
1526                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1527                         break;
1528         }
1529
1530         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1531         DMA_INTERRUPT(3);
1532 }
1533
1534 void cdrDmaInterrupt(void)
1535 {
1536         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1537         {
1538                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1539                 DMA_INTERRUPT(3);
1540         }
1541 }
1542
1543 static void getCdInfo(void)
1544 {
1545         u8 tmp;
1546
1547         CDR_getTN(cdr.ResultTN);
1548         CDR_getTD(0, cdr.SetSectorEnd);
1549         tmp = cdr.SetSectorEnd[0];
1550         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1551         cdr.SetSectorEnd[2] = tmp;
1552 }
1553
1554 void cdrReset() {
1555         memset(&cdr, 0, sizeof(cdr));
1556         cdr.CurTrack = 1;
1557         cdr.File = 1;
1558         cdr.Channel = 1;
1559         cdr.Reg2 = 0x1f;
1560         cdr.Stat = NoIntr;
1561         cdr.DriveState = DRIVESTATE_STANDBY;
1562         cdr.StatP = STATUS_ROTATING;
1563         cdr.FifoOffset = DATA_SIZE; // fifo empty
1564         
1565         // BIOS player - default values
1566         cdr.AttenuatorLeftToLeft = 0x80;
1567         cdr.AttenuatorLeftToRight = 0x00;
1568         cdr.AttenuatorRightToLeft = 0x00;
1569         cdr.AttenuatorRightToRight = 0x80;
1570
1571         getCdInfo();
1572 }
1573
1574 int cdrFreeze(void *f, int Mode) {
1575         u32 tmp;
1576         u8 tmpp[3];
1577
1578         if (Mode == 0 && !Config.Cdda)
1579                 CDR_stop();
1580         
1581         cdr.freeze_ver = 0x63647202;
1582         gzfreeze(&cdr, sizeof(cdr));
1583         
1584         if (Mode == 1) {
1585                 cdr.ParamP = cdr.ParamC;
1586                 tmp = cdr.FifoOffset;
1587         }
1588
1589         gzfreeze(&tmp, sizeof(tmp));
1590
1591         if (Mode == 0) {
1592                 getCdInfo();
1593
1594                 cdr.FifoOffset = tmp;
1595                 cdr.FifoSize = (cdr.Mode & 0x20) ? 2340 : 2048 + 12;
1596
1597                 // read right sub data
1598                 tmpp[0] = btoi(cdr.Prev[0]);
1599                 tmpp[1] = btoi(cdr.Prev[1]);
1600                 tmpp[2] = btoi(cdr.Prev[2]);
1601                 cdr.Prev[0]++;
1602                 ReadTrack(tmpp);
1603
1604                 if (cdr.Play) {
1605                         if (cdr.freeze_ver < 0x63647202)
1606                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1607
1608                         Find_CurTrack(cdr.SetSectorPlay);
1609                         if (!Config.Cdda)
1610                                 CDR_play(cdr.SetSectorPlay);
1611                         if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1612                                 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1613                 }
1614
1615                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1616                         // old versions did not latch Reg2, have to fixup..
1617                         if (cdr.Reg2 == 0) {
1618                                 SysPrintf("cdrom: fixing up old savestate\n");
1619                                 cdr.Reg2 = 7;
1620                         }
1621                         // also did not save Attenuator..
1622                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1623                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1624                         {
1625                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1626                         }
1627                 }
1628         }
1629
1630         return 0;
1631 }
1632
1633 void LidInterrupt(void) {
1634         getCdInfo();
1635         cdrLidSeekInterrupt();
1636 }