cdrom: try more states
[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 static void cdrAttenuate(s16 *buf, int samples, int stereo);
637
638 static void msfiAdd(u8 *msfi, u32 count)
639 {
640         assert(count < 75);
641         msfi[2] += count;
642         if (msfi[2] >= 75) {
643                 msfi[2] -= 75;
644                 msfi[1]++;
645                 if (msfi[1] == 60) {
646                         msfi[1] = 0;
647                         msfi[0]++;
648                 }
649         }
650 }
651
652 static void msfiSub(u8 *msfi, u32 count)
653 {
654         assert(count < 75);
655         msfi[2] -= count;
656         if ((s8)msfi[2] < 0) {
657                 msfi[2] += 75;
658                 msfi[1]--;
659                 if ((s8)msfi[1] < 0) {
660                         msfi[1] = 60;
661                         msfi[0]--;
662                 }
663         }
664 }
665
666 void cdrPlayReadInterrupt(void)
667 {
668         cdr.LastReadSeekCycles = psxRegs.cycle;
669
670         if (cdr.Reading) {
671                 cdrReadInterrupt();
672                 return;
673         }
674
675         if (!cdr.Play) return;
676
677         CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
678                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
679
680         cdr.DriveState = DRIVESTATE_PLAY_READ;
681         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
682         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
683                 CDR_LOG_I("end stop\n");
684                 StopCdda();
685                 SetPlaySeekRead(cdr.StatP, 0);
686                 cdr.TrackChanged = TRUE;
687                 cdr.DriveState = DRIVESTATE_PAUSED;
688         }
689         else {
690                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
691         }
692
693         if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
694                 cdrPlayInterrupt_Autopause();
695
696         if (!cdr.Muted && cdr.Play && !Config.Cdda) {
697                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
698                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
699                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
700         }
701
702         msfiAdd(cdr.SetSectorPlay, 1);
703
704         // update for CdlGetlocP/autopause
705         generate_subq(cdr.SetSectorPlay);
706
707         CDRPLAYREAD_INT(cdReadTime, 0);
708 }
709
710 #define CMD_PART2           0x100
711 #define CMD_WHILE_NOT_READY 0x200
712
713 void cdrInterrupt(void) {
714         int start_rotating = 0;
715         int error = 0;
716         u32 cycles, seekTime = 0;
717         u32 second_resp_time = 0;
718         const void *buf;
719         u8 ParamC;
720         u8 set_loc[3];
721         int read_ok;
722         u16 not_ready = 0;
723         u8 IrqStat = Acknowledge;
724         u16 Cmd;
725         int i;
726
727         if (cdr.IrqStat) {
728                 CDR_LOG_I("cmd %02x with irqstat %x\n",
729                         cdr.CmdInProgress, cdr.IrqStat);
730                 return;
731         }
732         if (cdr.Irq1Pending) {
733                 // hand out the "newest" sector, according to nocash
734                 cdrUpdateTransferBuf(CDR_getBuffer());
735                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
736                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
737                         cdr.CmdInProgress, cdr.Irq1Pending);
738                 SetResultSize(1);
739                 cdr.Result[0] = cdr.Irq1Pending;
740                 cdr.Irq1Pending = 0;
741                 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
742                 return;
743         }
744
745         // default response
746         SetResultSize(1);
747         cdr.Result[0] = cdr.StatP;
748
749         Cmd = cdr.CmdInProgress;
750         cdr.CmdInProgress = 0;
751         ParamC = cdr.ParamC;
752
753         if (Cmd < 0x100) {
754                 cdr.Ctrl &= ~0x80;
755                 cdr.ParamC = 0;
756                 cdr.Cmd = 0;
757         }
758
759         switch (cdr.DriveState) {
760         case DRIVESTATE_PREPARE_CD:
761                 if (Cmd > 2) {
762                         // Syphon filter 2 expects commands to work shortly after it sees
763                         // STATUS_ROTATING, so give up trying to emulate the startup seq
764                         cdr.DriveState = DRIVESTATE_STANDBY;
765                         cdr.StatP &= ~STATUS_SEEK;
766                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
767                         break;
768                 }
769                 // fallthrough
770         case DRIVESTATE_LID_OPEN:
771         case DRIVESTATE_RESCAN_CD:
772                 // no disk or busy with the initial scan, allowed cmds are limited
773                 not_ready = CMD_WHILE_NOT_READY;
774                 break;
775         }
776
777         switch (Cmd | not_ready) {
778                 case CdlNop:
779                 case CdlNop + CMD_WHILE_NOT_READY:
780                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
781                                 cdr.StatP &= ~STATUS_SHELLOPEN;
782                         break;
783
784                 case CdlSetloc:
785                 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
786                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
787
788                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
789                         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))
790                         {
791                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
792                                 if (++cdr.errorRetryhack > 100)
793                                         break;
794                                 error = ERROR_BAD_ARGNUM;
795                                 goto set_error;
796                         }
797                         else
798                         {
799                                 for (i = 0; i < 3; i++)
800                                         set_loc[i] = btoi(cdr.Param[i]);
801                                 memcpy(cdr.SetSector, set_loc, 3);
802                                 cdr.SetSector[3] = 0;
803                                 cdr.SetlocPending = 1;
804                                 cdr.errorRetryhack = 0;
805                         }
806                         break;
807
808                 do_CdlPlay:
809                 case CdlPlay:
810                         StopCdda();
811                         StopReading();
812
813                         cdr.FastBackward = 0;
814                         cdr.FastForward = 0;
815
816                         // BIOS CD Player
817                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
818
819                         if (ParamC != 0 && cdr.Param[0] != 0) {
820                                 int track = btoi( cdr.Param[0] );
821
822                                 if (track <= cdr.ResultTN[1])
823                                         cdr.CurTrack = track;
824
825                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
826
827                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
828                                         for (i = 0; i < 3; i++)
829                                                 set_loc[i] = cdr.ResultTD[2 - i];
830                                         seekTime = cdrSeekTime(set_loc);
831                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
832                                 }
833                         }
834                         else if (cdr.SetlocPending) {
835                                 seekTime = cdrSeekTime(cdr.SetSector);
836                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
837                         }
838                         else {
839                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
840                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
841                         }
842                         cdr.SetlocPending = 0;
843
844                         /*
845                         Rayman: detect track changes
846                         - fixes logo freeze
847
848                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
849                         - plays tracks without retry play
850
851                         Wild 9: skip PREGAP + starting accurate SubQ
852                         - plays tracks without retry play
853                         */
854                         Find_CurTrack(cdr.SetSectorPlay);
855                         generate_subq(cdr.SetSectorPlay);
856                         cdr.LocL[0] = LOCL_INVALID;
857                         cdr.SubqForwardSectors = 1;
858                         cdr.TrackChanged = FALSE;
859                         cdr.FileChannelSelected = 0;
860                         cdr.AdpcmActive = 0;
861                         cdr.ReportDelay = 60;
862                         cdr.sectorsRead = 0;
863
864                         if (!Config.Cdda)
865                                 CDR_play(cdr.SetSectorPlay);
866
867                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
868                         
869                         // BIOS player - set flag again
870                         cdr.Play = TRUE;
871                         cdr.DriveState = DRIVESTATE_PLAY_READ;
872
873                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
874                         start_rotating = 1;
875                         break;
876
877                 case CdlForward:
878                         // TODO: error 80 if stopped
879                         IrqStat = Complete;
880
881                         // GameShark CD Player: Calls 2x + Play 2x
882                         cdr.FastForward = 1;
883                         cdr.FastBackward = 0;
884                         break;
885
886                 case CdlBackward:
887                         IrqStat = Complete;
888
889                         // GameShark CD Player: Calls 2x + Play 2x
890                         cdr.FastBackward = 1;
891                         cdr.FastForward = 0;
892                         break;
893
894                 case CdlStandby:
895                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
896                                 error = ERROR_BAD_ARGNUM;
897                                 goto set_error;
898                         }
899                         cdr.DriveState = DRIVESTATE_STANDBY;
900                         second_resp_time = cdReadTime * 125 / 2;
901                         start_rotating = 1;
902                         break;
903
904                 case CdlStandby + CMD_PART2:
905                         IrqStat = Complete;
906                         break;
907
908                 case CdlStop:
909                         if (cdr.Play) {
910                                 // grab time for current track
911                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
912
913                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
914                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
915                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
916                         }
917
918                         StopCdda();
919                         StopReading();
920                         SetPlaySeekRead(cdr.StatP, 0);
921                         cdr.StatP &= ~STATUS_ROTATING;
922                         cdr.LocL[0] = LOCL_INVALID;
923
924                         second_resp_time = 0x800;
925                         if (cdr.DriveState != DRIVESTATE_STOPPED)
926                                 second_resp_time = cdReadTime * 30 / 2;
927
928                         cdr.DriveState = DRIVESTATE_STOPPED;
929                         break;
930
931                 case CdlStop + CMD_PART2:
932                         IrqStat = Complete;
933                         break;
934
935                 case CdlPause:
936                         StopCdda();
937                         StopReading();
938
939                         // how the drive maintains the position while paused is quite
940                         // complicated, this is the minimum to make "Bedlam" happy
941                         msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
942                         cdr.sectorsRead = 0;
943
944                         /*
945                         Gundam Battle Assault 2: much slower (*)
946                         - Fixes boot, gameplay
947
948                         Hokuto no Ken 2: slower
949                         - Fixes intro + subtitles
950
951                         InuYasha - Feudal Fairy Tale: slower
952                         - Fixes battles
953                         */
954                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
955                          * The timings from Duckstation are based upon hardware tests.
956                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
957                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
958                          * */
959                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
960                         {
961                                 second_resp_time = 7000;
962                         }
963                         else
964                         {
965                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
966                         }
967                         SetPlaySeekRead(cdr.StatP, 0);
968                         cdr.DriveState = DRIVESTATE_PAUSED;
969                         break;
970
971                 case CdlPause + CMD_PART2:
972                         IrqStat = Complete;
973                         break;
974
975                 case CdlReset:
976                 case CdlReset + CMD_WHILE_NOT_READY:
977                         StopCdda();
978                         StopReading();
979                         SetPlaySeekRead(cdr.StatP, 0);
980                         cdr.LocL[0] = LOCL_INVALID;
981                         cdr.Muted = FALSE;
982                         cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
983                         cdr.DriveState = DRIVESTATE_PAUSED;
984                         second_resp_time = not_ready ? 70000 : 4100000;
985                         start_rotating = 1;
986                         break;
987
988                 case CdlReset + CMD_PART2:
989                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
990                         IrqStat = Complete;
991                         break;
992
993                 case CdlMute:
994                         cdr.Muted = TRUE;
995                         break;
996
997                 case CdlDemute:
998                         cdr.Muted = FALSE;
999                         break;
1000
1001                 case CdlSetfilter:
1002                         cdr.FilterFile = cdr.Param[0];
1003                         cdr.FilterChannel = cdr.Param[1];
1004                         cdr.FileChannelSelected = 0;
1005                         break;
1006
1007                 case CdlSetmode:
1008                 case CdlSetmode + CMD_WHILE_NOT_READY:
1009                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1010                         cdr.Mode = cdr.Param[0];
1011                         break;
1012
1013                 case CdlGetparam:
1014                 case CdlGetparam + CMD_WHILE_NOT_READY:
1015                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1016                         SetResultSize_(5);
1017                         cdr.Result[1] = cdr.Mode;
1018                         cdr.Result[2] = 0;
1019                         cdr.Result[3] = cdr.FilterFile;
1020                         cdr.Result[4] = cdr.FilterChannel;
1021                         break;
1022
1023                 case CdlGetlocL:
1024                         if (cdr.LocL[0] == LOCL_INVALID) {
1025                                 error = 0x80;
1026                                 goto set_error;
1027                         }
1028                         SetResultSize_(8);
1029                         memcpy(cdr.Result, cdr.LocL, 8);
1030                         break;
1031
1032                 case CdlGetlocP:
1033                         SetResultSize_(8);
1034                         memcpy(&cdr.Result, &cdr.subq, 8);
1035                         break;
1036
1037                 case CdlReadT: // SetSession?
1038                         // really long
1039                         second_resp_time = cdReadTime * 290 / 4;
1040                         start_rotating = 1;
1041                         break;
1042
1043                 case CdlReadT + CMD_PART2:
1044                         IrqStat = Complete;
1045                         break;
1046
1047                 case CdlGetTN:
1048                         if (CDR_getTN(cdr.ResultTN) == -1) {
1049                                 assert(0);
1050                         }
1051                         SetResultSize_(3);
1052                         cdr.Result[1] = itob(cdr.ResultTN[0]);
1053                         cdr.Result[2] = itob(cdr.ResultTN[1]);
1054                         break;
1055
1056                 case CdlGetTD:
1057                         cdr.Track = btoi(cdr.Param[0]);
1058                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1059                                 error = ERROR_BAD_ARGVAL;
1060                                 goto set_error;
1061                         }
1062                         SetResultSize_(3);
1063                         cdr.Result[1] = itob(cdr.ResultTD[2]);
1064                         cdr.Result[2] = itob(cdr.ResultTD[1]);
1065                         // no sector number
1066                         //cdr.Result[3] = itob(cdr.ResultTD[0]);
1067                         break;
1068
1069                 case CdlSeekL:
1070                 case CdlSeekP:
1071                         StopCdda();
1072                         StopReading();
1073                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1074
1075                         seekTime = cdrSeekTime(cdr.SetSector);
1076                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1077                         cdr.DriveState = DRIVESTATE_SEEK;
1078                         /*
1079                         Crusaders of Might and Magic = 0.5x-4x
1080                         - fix cutscene speech start
1081
1082                         Eggs of Steel = 2x-?
1083                         - fix new game
1084
1085                         Medievil = ?-4x
1086                         - fix cutscene speech
1087
1088                         Rockman X5 = 0.5-4x
1089                         - fix capcom logo
1090                         */
1091                         second_resp_time = cdReadTime + seekTime;
1092                         start_rotating = 1;
1093                         break;
1094
1095                 case CdlSeekL + CMD_PART2:
1096                 case CdlSeekP + CMD_PART2:
1097                         SetPlaySeekRead(cdr.StatP, 0);
1098                         cdr.Result[0] = cdr.StatP;
1099                         IrqStat = Complete;
1100
1101                         Find_CurTrack(cdr.SetSectorPlay);
1102                         read_ok = ReadTrack(cdr.SetSectorPlay);
1103                         if (read_ok && (buf = CDR_getBuffer()))
1104                                 memcpy(cdr.LocL, buf, 8);
1105                         UpdateSubq(cdr.SetSectorPlay);
1106                         cdr.DriveState = DRIVESTATE_STANDBY;
1107                         cdr.TrackChanged = FALSE;
1108                         cdr.LastReadSeekCycles = psxRegs.cycle;
1109                         break;
1110
1111                 case CdlTest:
1112                 case CdlTest + CMD_WHILE_NOT_READY:
1113                         switch (cdr.Param[0]) {
1114                                 case 0x20: // System Controller ROM Version
1115                                         SetResultSize_(4);
1116                                         memcpy(cdr.Result, Test20, 4);
1117                                         break;
1118                                 case 0x22:
1119                                         SetResultSize_(8);
1120                                         memcpy(cdr.Result, Test22, 4);
1121                                         break;
1122                                 case 0x23: case 0x24:
1123                                         SetResultSize_(8);
1124                                         memcpy(cdr.Result, Test23, 4);
1125                                         break;
1126                         }
1127                         break;
1128
1129                 case CdlID:
1130                         second_resp_time = 20480;
1131                         break;
1132
1133                 case CdlID + CMD_PART2:
1134                         SetResultSize_(8);
1135                         cdr.Result[0] = cdr.StatP;
1136                         cdr.Result[1] = 0;
1137                         cdr.Result[2] = 0;
1138                         cdr.Result[3] = 0;
1139
1140                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1141                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1142                                 cdr.Result[1] = 0xc0;
1143                         }
1144                         else {
1145                                 if (stat.Type == 2)
1146                                         cdr.Result[1] |= 0x10;
1147                                 if (CdromId[0] == '\0')
1148                                         cdr.Result[1] |= 0x80;
1149                         }
1150                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1151                         CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1152                                 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1153
1154                         /* This adds the string "PCSX" in Playstation bios boot screen */
1155                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1156                         IrqStat = Complete;
1157                         break;
1158
1159                 case CdlInit:
1160                 case CdlInit + CMD_WHILE_NOT_READY:
1161                         StopCdda();
1162                         StopReading();
1163                         SetPlaySeekRead(cdr.StatP, 0);
1164                         // yes, it really sets STATUS_SHELLOPEN
1165                         cdr.StatP |= STATUS_SHELLOPEN;
1166                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1167                         set_event(PSXINT_CDRLID, 20480);
1168                         start_rotating = 1;
1169                         break;
1170
1171                 case CdlGetQ:
1172                 case CdlGetQ + CMD_WHILE_NOT_READY:
1173                         break;
1174
1175                 case CdlReadToc:
1176                 case CdlReadToc + CMD_WHILE_NOT_READY:
1177                         cdr.LocL[0] = LOCL_INVALID;
1178                         second_resp_time = cdReadTime * 180 / 4;
1179                         start_rotating = 1;
1180                         break;
1181
1182                 case CdlReadToc + CMD_PART2:
1183                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1184                         IrqStat = Complete;
1185                         break;
1186
1187                 case CdlReadN:
1188                 case CdlReadS:
1189                         if (cdr.Reading && !cdr.SetlocPending)
1190                                 break;
1191
1192                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1193
1194                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1195                                 // Read* acts as play for cdda tracks in cdda mode
1196                                 goto do_CdlPlay;
1197
1198                         StopCdda();
1199                         if (cdr.SetlocPending) {
1200                                 seekTime = cdrSeekTime(cdr.SetSector);
1201                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1202                                 cdr.SetlocPending = 0;
1203                         }
1204                         cdr.Reading = 1;
1205                         cdr.FileChannelSelected = 0;
1206                         cdr.AdpcmActive = 0;
1207
1208                         // Fighting Force 2 - update subq time immediately
1209                         // - fixes new game
1210                         UpdateSubq(cdr.SetSectorPlay);
1211                         cdr.LocL[0] = LOCL_INVALID;
1212                         cdr.SubqForwardSectors = 1;
1213                         cdr.sectorsRead = 0;
1214                         cdr.DriveState = DRIVESTATE_SEEK;
1215
1216                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1217                         cycles += seekTime;
1218                         if (Config.hacks.cdr_read_timing)
1219                                 cycles = cdrAlignTimingHack(cycles);
1220                         CDRPLAYREAD_INT(cycles, 1);
1221
1222                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1223                         start_rotating = 1;
1224                         break;
1225
1226                 case CdlSync:
1227                 default:
1228                         error = ERROR_INVALIDCMD;
1229                         // FALLTHROUGH
1230
1231                 set_error:
1232                         SetResultSize_(2);
1233                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1234                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1235                         IrqStat = DiskError;
1236                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1237                         break;
1238         }
1239
1240         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1241                 cdr.DriveState = DRIVESTATE_STANDBY;
1242                 cdr.StatP |= STATUS_ROTATING;
1243         }
1244
1245         if (second_resp_time) {
1246                 cdr.CmdInProgress = Cmd | 0x100;
1247                 set_event(PSXINT_CDR, second_resp_time);
1248         }
1249         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1250                 cdr.CmdInProgress = cdr.Cmd;
1251                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1252         }
1253
1254         setIrq(IrqStat, Cmd);
1255 }
1256
1257 #ifdef HAVE_ARMV7
1258  #define ssat32_to_16(v) \
1259   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1260 #else
1261  #define ssat32_to_16(v) do { \
1262   if (v < -32768) v = -32768; \
1263   else if (v > 32767) v = 32767; \
1264  } while (0)
1265 #endif
1266
1267 static void cdrPrepCdda(s16 *buf, int samples)
1268 {
1269 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1270         int i;
1271         for (i = 0; i < samples; i++) {
1272                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1273                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1274         }
1275 #endif
1276 }
1277
1278 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1279 {
1280         int i, l, r;
1281         int ll = cdr.AttenuatorLeftToLeft;
1282         int lr = cdr.AttenuatorLeftToRight;
1283         int rl = cdr.AttenuatorRightToLeft;
1284         int rr = cdr.AttenuatorRightToRight;
1285
1286         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1287                 return;
1288
1289         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1290                 return;
1291
1292         if (stereo) {
1293                 for (i = 0; i < samples; i++) {
1294                         l = buf[i * 2];
1295                         r = buf[i * 2 + 1];
1296                         l = (l * ll + r * rl) >> 7;
1297                         r = (r * rr + l * lr) >> 7;
1298                         ssat32_to_16(l);
1299                         ssat32_to_16(r);
1300                         buf[i * 2] = l;
1301                         buf[i * 2 + 1] = r;
1302                 }
1303         }
1304         else {
1305                 for (i = 0; i < samples; i++) {
1306                         l = buf[i];
1307                         l = l * (ll + rl) >> 7;
1308                         //r = r * (rr + lr) >> 7;
1309                         ssat32_to_16(l);
1310                         //ssat32_to_16(r);
1311                         buf[i] = l;
1312                 }
1313         }
1314 }
1315
1316 static void cdrReadInterruptSetResult(unsigned char result)
1317 {
1318         if (cdr.IrqStat) {
1319                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1320                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1321                         cdr.CmdInProgress, cdr.IrqStat);
1322                 cdr.Irq1Pending = result;
1323                 return;
1324         }
1325         SetResultSize(1);
1326         cdr.Result[0] = result;
1327         setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1328 }
1329
1330 static void cdrUpdateTransferBuf(const u8 *buf)
1331 {
1332         if (!buf)
1333                 return;
1334         memcpy(cdr.Transfer, buf, DATA_SIZE);
1335         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1336         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1337                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1338         if (cdr.FifoOffset < 2048 + 12)
1339                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1340 }
1341
1342 static void cdrReadInterrupt(void)
1343 {
1344         const struct { u8 file, chan, mode, coding; } *subhdr;
1345         const u8 *buf = NULL;
1346         int deliver_data = 1;
1347         u8 subqPos[3];
1348         int read_ok;
1349
1350         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1351         msfiAdd(subqPos, cdr.SubqForwardSectors);
1352         UpdateSubq(subqPos);
1353         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1354                 cdr.SubqForwardSectors++;
1355                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1356                 return;
1357         }
1358
1359         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1360         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1361         cdr.DriveState = DRIVESTATE_PLAY_READ;
1362         cdr.sectorsRead++;
1363
1364         read_ok = ReadTrack(cdr.SetSectorPlay);
1365         if (read_ok)
1366                 buf = CDR_getBuffer();
1367         if (buf == NULL)
1368                 read_ok = 0;
1369
1370         if (!read_ok) {
1371                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1372                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1373                 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1374                 return;
1375         }
1376         memcpy(cdr.LocL, buf, 8);
1377
1378         if (!cdr.IrqStat && !cdr.Irq1Pending)
1379                 cdrUpdateTransferBuf(buf);
1380
1381         subhdr = (void *)(buf + 4);
1382         do {
1383                 // try to process as adpcm
1384                 if (!(cdr.Mode & MODE_STRSND))
1385                         break;
1386                 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1387                         break;
1388                 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1389                         subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1390                 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1391                         break;
1392                 if (subhdr->chan & 0xe0) { // ?
1393                         if (subhdr->chan != 0xff)
1394                                 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1395                         break;
1396                 }
1397                 if (!cdr.FileChannelSelected) {
1398                         cdr.CurFile = subhdr->file;
1399                         cdr.CurChannel = subhdr->chan;
1400                         cdr.FileChannelSelected = 1;
1401                 }
1402                 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1403                         break;
1404
1405                 // accepted as adpcm
1406                 deliver_data = 0;
1407
1408                 if (Config.Xa)
1409                         break;
1410                 if (!cdr.Muted && cdr.AdpcmActive) {
1411                         cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1412                         SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 0);
1413                 }
1414                 // decode next
1415                 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, !cdr.AdpcmActive);
1416         } while (0);
1417
1418         if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1419                 deliver_data = 0;
1420
1421         /*
1422         Croc 2: $40 - only FORM1 (*)
1423         Judge Dredd: $C8 - only FORM1 (*)
1424         Sim Theme Park - no adpcm at all (zero)
1425         */
1426
1427         if (deliver_data)
1428                 cdrReadInterruptSetResult(cdr.StatP);
1429
1430         msfiAdd(cdr.SetSectorPlay, 1);
1431
1432         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1433 }
1434
1435 /*
1436 cdrRead0:
1437         bit 0,1 - reg index
1438         bit 2 - adpcm active
1439         bit 5 - 1 result ready
1440         bit 6 - 1 dma ready
1441         bit 7 - 1 command being processed
1442 */
1443
1444 unsigned char cdrRead0(void) {
1445         cdr.Ctrl &= ~0x24;
1446         cdr.Ctrl |= cdr.AdpcmActive << 2;
1447         cdr.Ctrl |= cdr.ResultReady << 5;
1448
1449         cdr.Ctrl |= 0x40; // data fifo not empty
1450
1451         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1452         cdr.Ctrl |= 0x18;
1453
1454         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1455
1456         return psxHu8(0x1800) = cdr.Ctrl;
1457 }
1458
1459 void cdrWrite0(unsigned char rt) {
1460         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1461
1462         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1463 }
1464
1465 unsigned char cdrRead1(void) {
1466         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1467                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1468         else
1469                 psxHu8(0x1801) = 0;
1470         cdr.ResultP++;
1471         if (cdr.ResultP == cdr.ResultC)
1472                 cdr.ResultReady = 0;
1473
1474         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1475
1476         return psxHu8(0x1801);
1477 }
1478
1479 void cdrWrite1(unsigned char rt) {
1480         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1481         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1482
1483         switch (cdr.Ctrl & 3) {
1484         case 0:
1485                 break;
1486         case 3:
1487                 cdr.AttenuatorRightToRightT = rt;
1488                 return;
1489         default:
1490                 return;
1491         }
1492
1493 #ifdef CDR_LOG_CMD_IRQ
1494         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1495         if (cdr.ParamC) {
1496                 int i;
1497                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1498                 for (i = 0; i < cdr.ParamC; i++)
1499                         SysPrintf(" %x,", cdr.Param[i]);
1500                 SysPrintf("}\n");
1501         } else {
1502                 SysPrintf("\n");
1503         }
1504 #endif
1505
1506         cdr.ResultReady = 0;
1507         cdr.Ctrl |= 0x80;
1508
1509         if (!cdr.CmdInProgress) {
1510                 cdr.CmdInProgress = rt;
1511                 // should be something like 12k + controller delays
1512                 set_event(PSXINT_CDR, 5000);
1513         }
1514         else {
1515                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1516                         rt, cdr.Cmd, cdr.CmdInProgress);
1517                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1518                         cdr.CmdInProgress = rt;
1519         }
1520
1521         cdr.Cmd = rt;
1522 }
1523
1524 unsigned char cdrRead2(void) {
1525         unsigned char ret = cdr.Transfer[0x920];
1526
1527         if (cdr.FifoOffset < cdr.FifoSize)
1528                 ret = cdr.Transfer[cdr.FifoOffset++];
1529         else
1530                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1531
1532         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1533         return ret;
1534 }
1535
1536 void cdrWrite2(unsigned char rt) {
1537         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1538         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1539
1540         switch (cdr.Ctrl & 3) {
1541         case 0:
1542                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1543                         cdr.Param[cdr.ParamC++] = rt;
1544                 return;
1545         case 1:
1546                 cdr.IrqMask = rt;
1547                 setIrq(cdr.IrqStat, 0x1005);
1548                 return;
1549         case 2:
1550                 cdr.AttenuatorLeftToLeftT = rt;
1551                 return;
1552         case 3:
1553                 cdr.AttenuatorRightToLeftT = rt;
1554                 return;
1555         }
1556 }
1557
1558 unsigned char cdrRead3(void) {
1559         if (cdr.Ctrl & 0x1)
1560                 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1561         else
1562                 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1563
1564         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1565         return psxHu8(0x1803);
1566 }
1567
1568 void cdrWrite3(unsigned char rt) {
1569         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1570         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1571
1572         switch (cdr.Ctrl & 3) {
1573         case 0:
1574                 break; // transfer
1575         case 1:
1576                 if (cdr.IrqStat & rt) {
1577                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1578                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1579                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1580 #ifdef CDR_LOG_CMD_IRQ
1581                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1582                                 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1583                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1584 #endif
1585                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1586                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1587                         {
1588                                 s32 c = 2048;
1589                                 if (cdr.CmdInProgress) {
1590                                         c = 2048 - (psxRegs.cycle - nextCycle);
1591                                         c = MAX_VALUE(c, 512);
1592                                 }
1593                                 set_event(PSXINT_CDR, c);
1594                         }
1595                 }
1596                 cdr.IrqStat &= ~rt;
1597
1598                 if (rt & 0x40)
1599                         cdr.ParamC = 0;
1600                 return;
1601         case 2:
1602                 cdr.AttenuatorLeftToRightT = rt;
1603                 return;
1604         case 3:
1605                 if (rt & 0x20) {
1606                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1607                         CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1608                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1609                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1610                 }
1611                 return;
1612         }
1613
1614         // test: Viewpoint
1615         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1616                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1617         }
1618         else if (rt & 0x80) {
1619                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1620                         case MODE_SIZE_2328:
1621                         case 0x00:
1622                                 cdr.FifoOffset = 12;
1623                                 cdr.FifoSize = 2048 + 12;
1624                                 break;
1625
1626                         case MODE_SIZE_2340:
1627                         default:
1628                                 cdr.FifoOffset = 0;
1629                                 cdr.FifoSize = 2340;
1630                                 break;
1631                 }
1632         }
1633         else if (!(rt & 0xc0))
1634                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1635 }
1636
1637 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1638         u32 cdsize, max_words;
1639         int size;
1640         u8 *ptr;
1641
1642 #if 0
1643         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1644         if (cdr.FifoOffset == 0) {
1645                 ptr = cdr.Transfer;
1646                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1647         }
1648         SysPrintf("\n");
1649 #endif
1650
1651         switch (chcr & 0x71000000) {
1652                 case 0x11000000:
1653                         ptr = getDmaRam(madr, &max_words);
1654                         if (ptr == INVALID_PTR) {
1655                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1656                                 break;
1657                         }
1658
1659                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1660
1661                         /*
1662                         GS CDX: Enhancement CD crash
1663                         - Setloc 0:0:0
1664                         - CdlPlay
1665                         - Spams DMA3 and gets buffer overrun
1666                         */
1667                         size = DATA_SIZE - cdr.FifoOffset;
1668                         if (size > cdsize)
1669                                 size = cdsize;
1670                         if (size > max_words * 4)
1671                                 size = max_words * 4;
1672                         if (size > 0)
1673                         {
1674                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1675                                 cdr.FifoOffset += size;
1676                         }
1677                         if (size < cdsize) {
1678                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1679                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1680                         }
1681                         psxCpu->Clear(madr, cdsize / 4);
1682
1683                         set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1684
1685                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1686                         if (chcr & 0x100) {
1687                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1688                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1689                         }
1690                         else {
1691                                 // halted
1692                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1693                         }
1694                         return;
1695
1696                 default:
1697                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1698                         break;
1699         }
1700
1701         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1702         DMA_INTERRUPT(3);
1703 }
1704
1705 void cdrDmaInterrupt(void)
1706 {
1707         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1708         {
1709                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1710                 DMA_INTERRUPT(3);
1711         }
1712 }
1713
1714 static void getCdInfo(void)
1715 {
1716         u8 tmp;
1717
1718         CDR_getTN(cdr.ResultTN);
1719         CDR_getTD(0, cdr.SetSectorEnd);
1720         tmp = cdr.SetSectorEnd[0];
1721         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1722         cdr.SetSectorEnd[2] = tmp;
1723 }
1724
1725 void cdrReset() {
1726         memset(&cdr, 0, sizeof(cdr));
1727         cdr.CurTrack = 1;
1728         cdr.FilterFile = 0;
1729         cdr.FilterChannel = 0;
1730         cdr.IrqMask = 0x1f;
1731         cdr.IrqStat = NoIntr;
1732         cdr.FifoOffset = DATA_SIZE; // fifo empty
1733
1734         CDR_getStatus(&stat);
1735         if (stat.Status & STATUS_SHELLOPEN) {
1736                 cdr.DriveState = DRIVESTATE_LID_OPEN;
1737                 cdr.StatP = STATUS_SHELLOPEN;
1738         }
1739         else if (CdromId[0] == '\0') {
1740                 cdr.DriveState = DRIVESTATE_STOPPED;
1741                 cdr.StatP = 0;
1742         }
1743         else {
1744                 cdr.DriveState = DRIVESTATE_STANDBY;
1745                 cdr.StatP = STATUS_ROTATING;
1746         }
1747         
1748         // BIOS player - default values
1749         cdr.AttenuatorLeftToLeft = 0x80;
1750         cdr.AttenuatorLeftToRight = 0x00;
1751         cdr.AttenuatorRightToLeft = 0x00;
1752         cdr.AttenuatorRightToRight = 0x80;
1753
1754         getCdInfo();
1755 }
1756
1757 int cdrFreeze(void *f, int Mode) {
1758         u32 tmp;
1759         u8 tmpp[3];
1760
1761         if (Mode == 0 && !Config.Cdda)
1762                 CDR_stop();
1763         
1764         cdr.freeze_ver = 0x63647202;
1765         gzfreeze(&cdr, sizeof(cdr));
1766         
1767         if (Mode == 1) {
1768                 cdr.ParamP = cdr.ParamC;
1769                 tmp = cdr.FifoOffset;
1770         }
1771
1772         gzfreeze(&tmp, sizeof(tmp));
1773
1774         if (Mode == 0) {
1775                 getCdInfo();
1776
1777                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1778                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1779                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1780                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1781
1782                 // read right sub data
1783                 tmpp[0] = btoi(cdr.Prev[0]);
1784                 tmpp[1] = btoi(cdr.Prev[1]);
1785                 tmpp[2] = btoi(cdr.Prev[2]);
1786                 cdr.Prev[0]++;
1787                 ReadTrack(tmpp);
1788
1789                 if (cdr.Play) {
1790                         if (cdr.freeze_ver < 0x63647202)
1791                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1792
1793                         Find_CurTrack(cdr.SetSectorPlay);
1794                         if (!Config.Cdda)
1795                                 CDR_play(cdr.SetSectorPlay);
1796                 }
1797         }
1798
1799         return 0;
1800 }
1801
1802 void LidInterrupt(void) {
1803         getCdInfo();
1804         cdrLidSeekInterrupt();
1805 }