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