introduce fractional framerates
[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: much slower (*)
975                         - Fixes boot, gameplay
976
977                         Hokuto no Ken 2: slower
978                         - Fixes intro + subtitles
979
980                         InuYasha - Feudal Fairy Tale: slower
981                         - Fixes battles
982                         */
983                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
984                          * The timings from Duckstation are based upon hardware tests.
985                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
986                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
987                          * */
988                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
989                         {
990                                 second_resp_time = 7000;
991                         }
992                         else
993                         {
994                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
995                         }
996                         SetPlaySeekRead(cdr.StatP, 0);
997                         DriveStateOld = cdr.DriveState;
998                         cdr.DriveState = DRIVESTATE_PAUSED;
999                         if (DriveStateOld == DRIVESTATE_SEEK) {
1000                                 // According to Duckstation this fails, but the
1001                                 // exact conditions and effects are not clear.
1002                                 // Moto Racer World Tour seems to rely on this.
1003                                 // For now assume pause works anyway, just errors out.
1004                                 error = ERROR_NOTREADY;
1005                                 goto set_error;
1006                         }
1007                         break;
1008
1009                 case CdlPause + CMD_PART2:
1010                         IrqStat = Complete;
1011                         break;
1012
1013                 case CdlReset:
1014                 case CdlReset + CMD_WHILE_NOT_READY:
1015                         // note: nocash and Duckstation calls this 'Init', but
1016                         // the official SDK calls it 'Reset', and so do we
1017                         StopCdda();
1018                         StopReading();
1019                         softReset();
1020                         second_resp_time = not_ready ? 70000 : 4100000;
1021                         start_rotating = 1;
1022                         break;
1023
1024                 case CdlReset + CMD_PART2:
1025                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1026                         IrqStat = Complete;
1027                         break;
1028
1029                 case CdlMute:
1030                         cdr.Muted = TRUE;
1031                         SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1032                         break;
1033
1034                 case CdlDemute:
1035                         cdr.Muted = FALSE;
1036                         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1037                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1038                         break;
1039
1040                 case CdlSetfilter:
1041                         cdr.FilterFile = cdr.Param[0];
1042                         cdr.FilterChannel = cdr.Param[1];
1043                         cdr.FileChannelSelected = 0;
1044                         break;
1045
1046                 case CdlSetmode:
1047                 case CdlSetmode + CMD_WHILE_NOT_READY:
1048                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1049                         cdr.Mode = cdr.Param[0];
1050                         break;
1051
1052                 case CdlGetparam:
1053                 case CdlGetparam + CMD_WHILE_NOT_READY:
1054                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1055                         SetResultSize_(5);
1056                         cdr.Result[1] = cdr.Mode;
1057                         cdr.Result[2] = 0;
1058                         cdr.Result[3] = cdr.FilterFile;
1059                         cdr.Result[4] = cdr.FilterChannel;
1060                         break;
1061
1062                 case CdlGetlocL:
1063                         if (cdr.LocL[0] == LOCL_INVALID) {
1064                                 error = 0x80;
1065                                 goto set_error;
1066                         }
1067                         SetResultSize_(8);
1068                         memcpy(cdr.Result, cdr.LocL, 8);
1069                         break;
1070
1071                 case CdlGetlocP:
1072                         SetResultSize_(8);
1073                         memcpy(&cdr.Result, &cdr.subq, 8);
1074                         break;
1075
1076                 case CdlReadT: // SetSession?
1077                         // really long
1078                         second_resp_time = cdReadTime * 290 / 4;
1079                         start_rotating = 1;
1080                         break;
1081
1082                 case CdlReadT + CMD_PART2:
1083                         IrqStat = Complete;
1084                         break;
1085
1086                 case CdlGetTN:
1087                         if (CDR_getTN(cdr.ResultTN) == -1) {
1088                                 assert(0);
1089                         }
1090                         SetResultSize_(3);
1091                         cdr.Result[1] = itob(cdr.ResultTN[0]);
1092                         cdr.Result[2] = itob(cdr.ResultTN[1]);
1093                         break;
1094
1095                 case CdlGetTD:
1096                         cdr.Track = btoi(cdr.Param[0]);
1097                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1098                                 error = ERROR_BAD_ARGVAL;
1099                                 goto set_error;
1100                         }
1101                         SetResultSize_(3);
1102                         cdr.Result[1] = itob(cdr.ResultTD[2]);
1103                         cdr.Result[2] = itob(cdr.ResultTD[1]);
1104                         // no sector number
1105                         //cdr.Result[3] = itob(cdr.ResultTD[0]);
1106                         break;
1107
1108                 case CdlSeekL:
1109                 case CdlSeekP:
1110                         StopCdda();
1111                         StopReading();
1112                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1113
1114                         seekTime = cdrSeekTime(cdr.SetSector);
1115                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1116                         cdr.DriveState = DRIVESTATE_SEEK;
1117                         CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1118                                         cdr.SetSectorPlay[2]);
1119                         /*
1120                         Crusaders of Might and Magic = 0.5x-4x
1121                         - fix cutscene speech start
1122
1123                         Eggs of Steel = 2x-?
1124                         - fix new game
1125
1126                         Medievil = ?-4x
1127                         - fix cutscene speech
1128
1129                         Rockman X5 = 0.5-4x
1130                         - fix capcom logo
1131                         */
1132                         second_resp_time = cdReadTime + seekTime;
1133                         start_rotating = 1;
1134                         break;
1135
1136                 case CdlSeekL + CMD_PART2:
1137                 case CdlSeekP + CMD_PART2:
1138                         SetPlaySeekRead(cdr.StatP, 0);
1139                         cdr.Result[0] = cdr.StatP;
1140                         IrqStat = Complete;
1141
1142                         Find_CurTrack(cdr.SetSectorPlay);
1143                         read_ok = ReadTrack(cdr.SetSectorPlay);
1144                         if (read_ok && (buf = CDR_getBuffer()))
1145                                 memcpy(cdr.LocL, buf, 8);
1146                         UpdateSubq(cdr.SetSectorPlay);
1147                         cdr.DriveState = DRIVESTATE_STANDBY;
1148                         cdr.TrackChanged = FALSE;
1149                         cdr.LastReadSeekCycles = psxRegs.cycle;
1150                         break;
1151
1152                 case CdlTest:
1153                 case CdlTest + CMD_WHILE_NOT_READY:
1154                         switch (cdr.Param[0]) {
1155                                 case 0x20: // System Controller ROM Version
1156                                         SetResultSize_(4);
1157                                         memcpy(cdr.Result, Test20, 4);
1158                                         break;
1159                                 case 0x22:
1160                                         SetResultSize_(8);
1161                                         memcpy(cdr.Result, Test22, 4);
1162                                         break;
1163                                 case 0x23: case 0x24:
1164                                         SetResultSize_(8);
1165                                         memcpy(cdr.Result, Test23, 4);
1166                                         break;
1167                         }
1168                         break;
1169
1170                 case CdlID:
1171                         second_resp_time = 20480;
1172                         break;
1173
1174                 case CdlID + CMD_PART2:
1175                         SetResultSize_(8);
1176                         cdr.Result[0] = cdr.StatP;
1177                         cdr.Result[1] = 0;
1178                         cdr.Result[2] = 0;
1179                         cdr.Result[3] = 0;
1180
1181                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1182                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1183                                 cdr.Result[1] = 0xc0;
1184                         }
1185                         else {
1186                                 if (stat.Type == 2)
1187                                         cdr.Result[1] |= 0x10;
1188                                 if (CdromId[0] == '\0')
1189                                         cdr.Result[1] |= 0x80;
1190                         }
1191                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1192                         CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1193                                 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1194
1195                         /* This adds the string "PCSX" in Playstation bios boot screen */
1196                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1197                         IrqStat = Complete;
1198                         break;
1199
1200                 case CdlInit:
1201                 case CdlInit + CMD_WHILE_NOT_READY:
1202                         StopCdda();
1203                         StopReading();
1204                         SetPlaySeekRead(cdr.StatP, 0);
1205                         // yes, it really sets STATUS_SHELLOPEN
1206                         cdr.StatP |= STATUS_SHELLOPEN;
1207                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1208                         set_event(PSXINT_CDRLID, 20480);
1209                         start_rotating = 1;
1210                         break;
1211
1212                 case CdlGetQ:
1213                 case CdlGetQ + CMD_WHILE_NOT_READY:
1214                         break;
1215
1216                 case CdlReadToc:
1217                 case CdlReadToc + CMD_WHILE_NOT_READY:
1218                         cdr.LocL[0] = LOCL_INVALID;
1219                         second_resp_time = cdReadTime * 180 / 4;
1220                         start_rotating = 1;
1221                         break;
1222
1223                 case CdlReadToc + CMD_PART2:
1224                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1225                         IrqStat = Complete;
1226                         break;
1227
1228                 case CdlReadN:
1229                 case CdlReadS:
1230                         if (cdr.Reading && !cdr.SetlocPending)
1231                                 break;
1232
1233                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1234
1235                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1236                                 // Read* acts as play for cdda tracks in cdda mode
1237                                 goto do_CdlPlay;
1238
1239                         StopCdda();
1240                         if (cdr.SetlocPending) {
1241                                 seekTime = cdrSeekTime(cdr.SetSector);
1242                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1243                                 cdr.SetlocPending = 0;
1244                         }
1245                         cdr.Reading = 1;
1246                         cdr.FileChannelSelected = 0;
1247                         cdr.AdpcmActive = 0;
1248
1249                         // Fighting Force 2 - update subq time immediately
1250                         // - fixes new game
1251                         UpdateSubq(cdr.SetSectorPlay);
1252                         cdr.LocL[0] = LOCL_INVALID;
1253                         cdr.SubqForwardSectors = 1;
1254                         cdr.sectorsRead = 0;
1255                         cdr.DriveState = DRIVESTATE_SEEK;
1256                         CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1257                                         cdr.SetSectorPlay[2]);
1258
1259                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1260                         cycles += seekTime;
1261                         if (Config.hacks.cdr_read_timing)
1262                                 cycles = cdrAlignTimingHack(cycles);
1263                         CDRPLAYREAD_INT(cycles, 1);
1264
1265                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1266                         start_rotating = 1;
1267                         break;
1268
1269                 case CdlSync:
1270                 default:
1271                         error = ERROR_INVALIDCMD;
1272                         // FALLTHROUGH
1273
1274                 set_error:
1275                         SetResultSize_(2);
1276                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1277                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1278                         IrqStat = DiskError;
1279                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1280                         break;
1281         }
1282
1283         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1284                 cdr.DriveState = DRIVESTATE_STANDBY;
1285                 cdr.StatP |= STATUS_ROTATING;
1286         }
1287
1288         if (second_resp_time) {
1289                 cdr.CmdInProgress = Cmd | 0x100;
1290                 set_event(PSXINT_CDR, second_resp_time);
1291         }
1292         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1293                 cdr.CmdInProgress = cdr.Cmd;
1294                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1295         }
1296
1297         setIrq(IrqStat, Cmd);
1298 }
1299
1300 #ifdef HAVE_ARMV7
1301  #define ssat32_to_16(v) \
1302   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1303 #else
1304  #define ssat32_to_16(v) do { \
1305   if (v < -32768) v = -32768; \
1306   else if (v > 32767) v = 32767; \
1307  } while (0)
1308 #endif
1309
1310 static void cdrPrepCdda(s16 *buf, int samples)
1311 {
1312 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1313         int i;
1314         for (i = 0; i < samples; i++) {
1315                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1316                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1317         }
1318 #endif
1319 }
1320
1321 static void cdrReadInterruptSetResult(unsigned char result)
1322 {
1323         if (cdr.IrqStat) {
1324                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1325                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1326                         cdr.CmdInProgress, cdr.IrqStat);
1327                 cdr.Irq1Pending = result;
1328                 return;
1329         }
1330         SetResultSize(1);
1331         cdr.Result[0] = result;
1332         setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1333 }
1334
1335 static void cdrUpdateTransferBuf(const u8 *buf)
1336 {
1337         if (!buf)
1338                 return;
1339         memcpy(cdr.Transfer, buf, DATA_SIZE);
1340         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1341         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1342                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1343         if (cdr.FifoOffset < 2048 + 12)
1344                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1345 }
1346
1347 static void cdrReadInterrupt(void)
1348 {
1349         const struct { u8 file, chan, mode, coding; } *subhdr;
1350         const u8 *buf = NULL;
1351         int deliver_data = 1;
1352         u8 subqPos[3];
1353         int read_ok;
1354         int is_start;
1355
1356         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1357         msfiAdd(subqPos, cdr.SubqForwardSectors);
1358         UpdateSubq(subqPos);
1359         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1360                 cdr.SubqForwardSectors++;
1361                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1362                 return;
1363         }
1364
1365         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1366         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1367         cdr.DriveState = DRIVESTATE_PLAY_READ;
1368         cdr.sectorsRead++;
1369
1370         read_ok = ReadTrack(cdr.SetSectorPlay);
1371         if (read_ok)
1372                 buf = CDR_getBuffer();
1373         if (buf == NULL)
1374                 read_ok = 0;
1375
1376         if (!read_ok) {
1377                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1378                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1379                 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1380                 return;
1381         }
1382         memcpy(cdr.LocL, buf, 8);
1383
1384         if (!cdr.IrqStat && !cdr.Irq1Pending)
1385                 cdrUpdateTransferBuf(buf);
1386
1387         subhdr = (void *)(buf + 4);
1388         do {
1389                 // try to process as adpcm
1390                 if (!(cdr.Mode & MODE_STRSND))
1391                         break;
1392                 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1393                         break;
1394                 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1395                         subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1396                 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1397                         break;
1398                 if (subhdr->chan & 0x80) { // ?
1399                         if (subhdr->chan != 0xff)
1400                                 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1401                         break;
1402                 }
1403                 if (!cdr.FileChannelSelected) {
1404                         cdr.CurFile = subhdr->file;
1405                         cdr.CurChannel = subhdr->chan;
1406                         cdr.FileChannelSelected = 1;
1407                 }
1408                 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1409                         break;
1410
1411                 // accepted as adpcm
1412                 deliver_data = 0;
1413
1414                 if (Config.Xa)
1415                         break;
1416                 is_start = !cdr.AdpcmActive;
1417                 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1418                 if (cdr.AdpcmActive)
1419                         SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1420         } while (0);
1421
1422         if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1423                 deliver_data = 0;
1424
1425         /*
1426         Croc 2: $40 - only FORM1 (*)
1427         Judge Dredd: $C8 - only FORM1 (*)
1428         Sim Theme Park - no adpcm at all (zero)
1429         */
1430
1431         if (deliver_data)
1432                 cdrReadInterruptSetResult(cdr.StatP);
1433
1434         msfiAdd(cdr.SetSectorPlay, 1);
1435         CDR_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
1436
1437         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1438 }
1439
1440 /*
1441 cdrRead0:
1442         bit 0,1 - reg index
1443         bit 2 - adpcm active
1444         bit 5 - 1 result ready
1445         bit 6 - 1 dma ready
1446         bit 7 - 1 command being processed
1447 */
1448
1449 unsigned char cdrRead0(void) {
1450         cdr.Ctrl &= ~0x24;
1451         cdr.Ctrl |= cdr.AdpcmActive << 2;
1452         cdr.Ctrl |= cdr.ResultReady << 5;
1453
1454         cdr.Ctrl |= 0x40; // data fifo not empty
1455
1456         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1457         cdr.Ctrl |= 0x18;
1458
1459         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1460
1461         return psxHu8(0x1800) = cdr.Ctrl;
1462 }
1463
1464 void cdrWrite0(unsigned char rt) {
1465         CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1466
1467         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1468 }
1469
1470 unsigned char cdrRead1(void) {
1471         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1472                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1473         else
1474                 psxHu8(0x1801) = 0;
1475         cdr.ResultP++;
1476         if (cdr.ResultP == cdr.ResultC)
1477                 cdr.ResultReady = 0;
1478
1479         CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1480
1481         return psxHu8(0x1801);
1482 }
1483
1484 void cdrWrite1(unsigned char rt) {
1485         const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1486         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1487
1488         switch (cdr.Ctrl & 3) {
1489         case 0:
1490                 break;
1491         case 3:
1492                 cdr.AttenuatorRightToRightT = rt;
1493                 return;
1494         default:
1495                 return;
1496         }
1497
1498 #ifdef CDR_LOG_CMD_IRQ
1499         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1500         if (cdr.ParamC) {
1501                 int i;
1502                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1503                 for (i = 0; i < cdr.ParamC; i++)
1504                         SysPrintf(" %x,", cdr.Param[i]);
1505                 SysPrintf("}");
1506         }
1507         SysPrintf(" @%08x\n", psxRegs.pc);
1508 #endif
1509
1510         cdr.ResultReady = 0;
1511         cdr.Ctrl |= 0x80;
1512
1513         if (!cdr.CmdInProgress) {
1514                 cdr.CmdInProgress = rt;
1515                 // should be something like 12k + controller delays
1516                 set_event(PSXINT_CDR, 5000);
1517         }
1518         else {
1519                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1520                         rt, cdr.Cmd, cdr.CmdInProgress);
1521                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1522                         cdr.CmdInProgress = rt;
1523         }
1524
1525         cdr.Cmd = rt;
1526 }
1527
1528 unsigned char cdrRead2(void) {
1529         unsigned char ret = cdr.Transfer[0x920];
1530
1531         if (cdr.FifoOffset < cdr.FifoSize)
1532                 ret = cdr.Transfer[cdr.FifoOffset++];
1533         else
1534                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1535
1536         CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1537         return ret;
1538 }
1539
1540 void cdrWrite2(unsigned char rt) {
1541         const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1542         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1543
1544         switch (cdr.Ctrl & 3) {
1545         case 0:
1546                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1547                         cdr.Param[cdr.ParamC++] = rt;
1548                 return;
1549         case 1:
1550                 cdr.IrqMask = rt;
1551                 setIrq(cdr.IrqStat, 0x1005);
1552                 return;
1553         case 2:
1554                 cdr.AttenuatorLeftToLeftT = rt;
1555                 return;
1556         case 3:
1557                 cdr.AttenuatorRightToLeftT = rt;
1558                 return;
1559         }
1560 }
1561
1562 unsigned char cdrRead3(void) {
1563         if (cdr.Ctrl & 0x1)
1564                 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1565         else
1566                 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1567
1568         CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1569                 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1570         return psxHu8(0x1803);
1571 }
1572
1573 void cdrWrite3(unsigned char rt) {
1574         const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1575         u8 ll, lr, rl, rr;
1576         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1577
1578         switch (cdr.Ctrl & 3) {
1579         case 0:
1580                 break; // transfer
1581         case 1:
1582                 if (cdr.IrqStat & rt) {
1583                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1584                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1585                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1586 #ifdef CDR_LOG_CMD_IRQ
1587                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1588                                 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1589                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1590 #endif
1591                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1592                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1593                         {
1594                                 s32 c = 2048;
1595                                 if (cdr.CmdInProgress) {
1596                                         c = 2048 - (psxRegs.cycle - nextCycle);
1597                                         c = MAX_VALUE(c, 512);
1598                                 }
1599                                 set_event(PSXINT_CDR, c);
1600                         }
1601                 }
1602                 cdr.IrqStat &= ~rt;
1603
1604                 if (rt & 0x40)
1605                         cdr.ParamC = 0;
1606                 return;
1607         case 2:
1608                 cdr.AttenuatorLeftToRightT = rt;
1609                 return;
1610         case 3:
1611                 if (rt & 0x01)
1612                         log_unhandled("Mute ADPCM?\n");
1613                 if (rt & 0x20) {
1614                         ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1615                         rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1616                         if (ll == cdr.AttenuatorLeftToLeft &&
1617                             lr == cdr.AttenuatorLeftToRight &&
1618                             rl == cdr.AttenuatorRightToLeft &&
1619                             rr == cdr.AttenuatorRightToRight)
1620                                 return;
1621                         cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1622                         cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1623                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1624                         SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1625                 }
1626                 return;
1627         }
1628
1629         // test: Viewpoint
1630         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1631                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1632         }
1633         else if (rt & 0x80) {
1634                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1635                         case MODE_SIZE_2328:
1636                         case 0x00:
1637                                 cdr.FifoOffset = 12;
1638                                 cdr.FifoSize = 2048 + 12;
1639                                 break;
1640
1641                         case MODE_SIZE_2340:
1642                         default:
1643                                 cdr.FifoOffset = 0;
1644                                 cdr.FifoSize = 2340;
1645                                 break;
1646                 }
1647         }
1648         else if (!(rt & 0xc0))
1649                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1650 }
1651
1652 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1653         u32 cdsize, max_words;
1654         int size;
1655         u8 *ptr;
1656
1657 #if 0
1658         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1659         if (cdr.FifoOffset == 0) {
1660                 ptr = cdr.Transfer;
1661                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1662         }
1663         SysPrintf("\n");
1664 #endif
1665
1666         switch (chcr & 0x71000000) {
1667                 case 0x11000000:
1668                         madr &= ~3;
1669                         ptr = getDmaRam(madr, &max_words);
1670                         if (ptr == INVALID_PTR) {
1671                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1672                                 break;
1673                         }
1674
1675                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1676
1677                         /*
1678                         GS CDX: Enhancement CD crash
1679                         - Setloc 0:0:0
1680                         - CdlPlay
1681                         - Spams DMA3 and gets buffer overrun
1682                         */
1683                         size = DATA_SIZE - cdr.FifoOffset;
1684                         if (size > cdsize)
1685                                 size = cdsize;
1686                         if (size > max_words * 4)
1687                                 size = max_words * 4;
1688                         if (size > 0)
1689                         {
1690                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1691                                 cdr.FifoOffset += size;
1692                         }
1693                         if (size < cdsize) {
1694                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1695                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1696                         }
1697                         psxCpu->Clear(madr, cdsize / 4);
1698
1699                         set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1700
1701                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1702                         if (chcr & 0x100) {
1703                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1704                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1705                         }
1706                         else {
1707                                 // halted
1708                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1709                         }
1710                         return;
1711
1712                 default:
1713                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1714                         break;
1715         }
1716
1717         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1718         DMA_INTERRUPT(3);
1719 }
1720
1721 void cdrDmaInterrupt(void)
1722 {
1723         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1724         {
1725                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1726                 DMA_INTERRUPT(3);
1727         }
1728 }
1729
1730 static void getCdInfo(void)
1731 {
1732         u8 tmp;
1733
1734         CDR_getTN(cdr.ResultTN);
1735         CDR_getTD(0, cdr.SetSectorEnd);
1736         tmp = cdr.SetSectorEnd[0];
1737         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1738         cdr.SetSectorEnd[2] = tmp;
1739 }
1740
1741 void cdrReset() {
1742         memset(&cdr, 0, sizeof(cdr));
1743         cdr.CurTrack = 1;
1744         cdr.FilterFile = 0;
1745         cdr.FilterChannel = 0;
1746         cdr.IrqMask = 0x1f;
1747         cdr.IrqStat = NoIntr;
1748
1749         // BIOS player - default values
1750         cdr.AttenuatorLeftToLeft = 0x80;
1751         cdr.AttenuatorLeftToRight = 0x00;
1752         cdr.AttenuatorRightToLeft = 0x00;
1753         cdr.AttenuatorRightToRight = 0x80;
1754
1755         softReset();
1756         getCdInfo();
1757 }
1758
1759 int cdrFreeze(void *f, int Mode) {
1760         u32 tmp;
1761         u8 tmpp[3];
1762
1763         if (Mode == 0 && !Config.Cdda)
1764                 CDR_stop();
1765         
1766         cdr.freeze_ver = 0x63647202;
1767         gzfreeze(&cdr, sizeof(cdr));
1768         
1769         if (Mode == 1) {
1770                 cdr.ParamP = cdr.ParamC;
1771                 tmp = cdr.FifoOffset;
1772         }
1773
1774         gzfreeze(&tmp, sizeof(tmp));
1775
1776         if (Mode == 0) {
1777                 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1778                 getCdInfo();
1779
1780                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1781                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1782                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1783                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1784
1785                 // read right sub data
1786                 tmpp[0] = btoi(cdr.Prev[0]);
1787                 tmpp[1] = btoi(cdr.Prev[1]);
1788                 tmpp[2] = btoi(cdr.Prev[2]);
1789                 cdr.Prev[0]++;
1790                 ReadTrack(tmpp);
1791
1792                 if (cdr.Play) {
1793                         if (cdr.freeze_ver < 0x63647202)
1794                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1795
1796                         Find_CurTrack(cdr.SetSectorPlay);
1797                         if (!Config.Cdda)
1798                                 CDR_play(cdr.SetSectorPlay);
1799                 }
1800                 if (!cdr.Muted)
1801                         ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToLeft,
1802                         rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1803                 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1804         }
1805
1806         return 0;
1807 }
1808
1809 void LidInterrupt(void) {
1810         getCdInfo();
1811         cdrLidSeekInterrupt();
1812 }