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