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