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