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