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