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