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