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