cdrom: attempt to improve reset
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
18  ***************************************************************************/
19
20 /*
21 * Handles all CD-ROM registers and functions.
22 */
23
24 #include <assert.h>
25 #include "cdrom.h"
26 #include "misc.h"
27 #include "ppf.h"
28 #include "psxdma.h"
29 #include "psxevents.h"
30 #include "arm_features.h"
31
32 /* logging */
33 #if 0
34 #define CDR_LOG SysPrintf
35 #else
36 #define CDR_LOG(...)
37 #endif
38 #if 0
39 #define CDR_LOG_I SysPrintf
40 #else
41 #define CDR_LOG_I(fmt, ...) \
42         log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
43 #endif
44 #if 0
45 #define CDR_LOG_IO SysPrintf
46 #else
47 #define CDR_LOG_IO(...)
48 #endif
49 //#define CDR_LOG_CMD_IRQ
50
51 static struct {
52         // unused members maintain savesate compatibility
53         unsigned char unused0;
54         unsigned char unused1;
55         unsigned char IrqMask;
56         unsigned char unused2;
57         unsigned char Ctrl;
58         unsigned char IrqStat;
59
60         unsigned char StatP;
61
62         unsigned char Transfer[DATA_SIZE];
63         struct {
64                 unsigned char Track;
65                 unsigned char Index;
66                 unsigned char Relative[3];
67                 unsigned char Absolute[3];
68         } subq;
69         unsigned char TrackChanged;
70         unsigned char ReportDelay;
71         unsigned char unused3;
72         unsigned short sectorsRead;
73         unsigned int  freeze_ver;
74
75         unsigned char Prev[4];
76         unsigned char Param[8];
77         unsigned char Result[16];
78
79         unsigned char ParamC;
80         unsigned char ParamP;
81         unsigned char ResultC;
82         unsigned char ResultP;
83         unsigned char ResultReady;
84         unsigned char Cmd;
85         unsigned char SubqForwardSectors;
86         unsigned char SetlocPending;
87         u32 Reading;
88
89         unsigned char ResultTN[6];
90         unsigned char ResultTD[4];
91         unsigned char SetSectorPlay[4];
92         unsigned char SetSectorEnd[4];
93         unsigned char SetSector[4];
94         unsigned char Track;
95         boolean Play, Muted;
96         int CurTrack;
97         unsigned char Mode;
98         unsigned char FileChannelSelected;
99         unsigned char CurFile, CurChannel;
100         int FilterFile, FilterChannel;
101         unsigned char LocL[8];
102         int unused4;
103
104         xa_decode_t Xa;
105
106         u16 FifoOffset;
107         u16 FifoSize;
108
109         u16 CmdInProgress;
110         u8 Irq1Pending;
111         u8 AdpcmActive;
112         u32 LastReadSeekCycles;
113
114         u8 unused7;
115
116         u8 DriveState; // enum drive_state
117         u8 FastForward;
118         u8 FastBackward;
119         u8 errorRetryhack;
120
121         u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
122         u8 AttenuatorRightToRight, AttenuatorRightToLeft;
123         u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
124         u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
125 } cdr;
126 static s16 read_buf[CD_FRAMESIZE_RAW/2];
127
128 /* CD-ROM magic numbers */
129 #define CdlSync        0  /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
130 #define CdlNop         1
131 #define CdlSetloc      2
132 #define CdlPlay        3
133 #define CdlForward     4
134 #define CdlBackward    5
135 #define CdlReadN       6
136 #define CdlStandby     7
137 #define CdlStop        8
138 #define CdlPause       9
139 #define CdlReset       10
140 #define CdlMute        11
141 #define CdlDemute      12
142 #define CdlSetfilter   13
143 #define CdlSetmode     14
144 #define CdlGetparam    15
145 #define CdlGetlocL     16
146 #define CdlGetlocP     17
147 #define CdlReadT       18
148 #define CdlGetTN       19
149 #define CdlGetTD       20
150 #define CdlSeekL       21
151 #define CdlSeekP       22
152 #define CdlSetclock    23
153 #define CdlGetclock    24
154 #define CdlTest        25
155 #define CdlID          26
156 #define CdlReadS       27
157 #define CdlInit        28
158 #define CdlGetQ        29
159 #define CdlReadToc     30
160
161 #ifdef CDR_LOG_CMD_IRQ
162 static const char * const CmdName[0x100] = {
163     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
164     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
165     "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
166     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
167     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
168     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
169     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
170     "CdlInit",     NULL,           "CDlReadToc", NULL
171 };
172 #endif
173
174 unsigned char Test04[] = { 0 };
175 unsigned char Test05[] = { 0 };
176 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
177 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
178 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
179
180 // cdr.IrqStat:
181 #define NoIntr          0
182 #define DataReady       1
183 #define Complete        2
184 #define Acknowledge     3
185 #define DataEnd         4
186 #define DiskError       5
187
188 /* Modes flags */
189 #define MODE_SPEED       (1<<7) // 0x80
190 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
191 #define MODE_SIZE_2340   (1<<5) // 0x20
192 #define MODE_SIZE_2328   (1<<4) // 0x10
193 #define MODE_SIZE_2048   (0<<4) // 0x00
194 #define MODE_SF          (1<<3) // 0x08 channel on/off
195 #define MODE_REPORT      (1<<2) // 0x04
196 #define MODE_AUTOPAUSE   (1<<1) // 0x02
197 #define MODE_CDDA        (1<<0) // 0x01
198
199 /* Status flags */
200 #define STATUS_PLAY      (1<<7) // 0x80
201 #define STATUS_SEEK      (1<<6) // 0x40
202 #define STATUS_READ      (1<<5) // 0x20
203 #define STATUS_SHELLOPEN (1<<4) // 0x10
204 #define STATUS_UNKNOWN3  (1<<3) // 0x08
205 #define STATUS_SEEKERROR (1<<2) // 0x04
206 #define STATUS_ROTATING  (1<<1) // 0x02
207 #define STATUS_ERROR     (1<<0) // 0x01
208
209 /* Errors */
210 #define ERROR_NOTREADY   (1<<7) // 0x80
211 #define ERROR_INVALIDCMD (1<<6) // 0x40
212 #define ERROR_BAD_ARGNUM (1<<5) // 0x20
213 #define ERROR_BAD_ARGVAL (1<<4) // 0x10
214 #define ERROR_SHELLOPEN  (1<<3) // 0x08
215
216 // 1x = 75 sectors per second
217 // PSXCLK = 1 sec in the ps
218 // so (PSXCLK / 75) = cdr read time (linuzappz)
219 #define cdReadTime (PSXCLK / 75)
220
221 #define LOCL_INVALID 0xff
222 #define SUBQ_FORWARD_SECTORS 2u
223
224 enum drive_state {
225         DRIVESTATE_STANDBY = 0, // different from paused
226         DRIVESTATE_LID_OPEN,
227         DRIVESTATE_RESCAN_CD,
228         DRIVESTATE_PREPARE_CD,
229         DRIVESTATE_STOPPED,
230         DRIVESTATE_PAUSED,
231         DRIVESTATE_PLAY_READ,
232         DRIVESTATE_SEEK,
233 };
234
235 static struct CdrStat stat;
236
237 static unsigned int msf2sec(const u8 *msf) {
238         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
239 }
240
241 // for that weird psemu API..
242 static unsigned int fsm2sec(const u8 *msf) {
243         return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
244 }
245
246 static void sec2msf(unsigned int s, u8 *msf) {
247         msf[0] = s / 75 / 60;
248         s = s - msf[0] * 75 * 60;
249         msf[1] = s / 75;
250         s = s - msf[1] * 75;
251         msf[2] = s;
252 }
253
254 // cdrPlayReadInterrupt
255 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
256         u32 e_ = eCycle; \
257         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
258         if (isFirst) \
259                 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
260         else \
261                 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
262         psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
263         set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
264 }
265
266 #define StopReading() { \
267         cdr.Reading = 0; \
268         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
269 }
270
271 #define StopCdda() { \
272         if (cdr.Play && !Config.Cdda) CDR_stop(); \
273         cdr.Play = FALSE; \
274         cdr.FastForward = 0; \
275         cdr.FastBackward = 0; \
276 }
277
278 #define SetPlaySeekRead(x, f) { \
279         x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
280         x |= f; \
281 }
282
283 #define SetResultSize_(size) { \
284         cdr.ResultP = 0; \
285         cdr.ResultC = size; \
286         cdr.ResultReady = 1; \
287 }
288
289 #define SetResultSize(size) { \
290         if (cdr.ResultP < cdr.ResultC) \
291                 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
292         SetResultSize_(size); \
293 }
294
295 static void setIrq(u8 irq, int log_cmd)
296 {
297         u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
298         u8 new_ = irq & cdr.IrqMask ? 1 : 0;
299
300         cdr.IrqStat = irq;
301         if ((old ^ new_) & new_)
302                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
303
304 #ifdef CDR_LOG_CMD_IRQ
305         if (cdr.IrqStat)
306         {
307                 int i;
308                 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
309                         !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
310                 for (i = 0; i < cdr.ResultC; i++)
311                         SysPrintf("%02x ", cdr.Result[i]);
312                 SysPrintf("\n");
313         }
314 #endif
315 }
316
317 // timing used in this function was taken from tests on real hardware
318 // (yes it's slow, but you probably don't want to modify it)
319 void cdrLidSeekInterrupt(void)
320 {
321         CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
322
323         switch (cdr.DriveState) {
324         default:
325         case DRIVESTATE_STANDBY:
326                 StopCdda();
327                 //StopReading();
328                 SetPlaySeekRead(cdr.StatP, 0);
329
330                 if (CDR_getStatus(&stat) == -1)
331                         return;
332
333                 if (stat.Status & STATUS_SHELLOPEN)
334                 {
335                         memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
336                         cdr.DriveState = DRIVESTATE_LID_OPEN;
337                         set_event(PSXINT_CDRLID, 0x800);
338                 }
339                 break;
340
341         case DRIVESTATE_LID_OPEN:
342                 if (CDR_getStatus(&stat) == -1)
343                         stat.Status &= ~STATUS_SHELLOPEN;
344
345                 // 02, 12, 10
346                 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
347                         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         cdr.LastReadSeekCycles = psxRegs.cycle;
664
665         if (cdr.Reading) {
666                 cdrReadInterrupt();
667                 return;
668         }
669
670         if (!cdr.Play) return;
671
672         CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
673                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
674
675         cdr.DriveState = DRIVESTATE_PLAY_READ;
676         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
677         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
678                 CDR_LOG_I("end stop\n");
679                 StopCdda();
680                 SetPlaySeekRead(cdr.StatP, 0);
681                 cdr.TrackChanged = TRUE;
682                 cdr.DriveState = DRIVESTATE_PAUSED;
683         }
684         else {
685                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
686         }
687
688         if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
689                 cdrPlayInterrupt_Autopause();
690
691         if (cdr.Play && !Config.Cdda) {
692                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
693                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
694         }
695
696         msfiAdd(cdr.SetSectorPlay, 1);
697
698         // update for CdlGetlocP/autopause
699         generate_subq(cdr.SetSectorPlay);
700
701         CDRPLAYREAD_INT(cdReadTime, 0);
702 }
703
704 static void softReset(void)
705 {
706         CDR_getStatus(&stat);
707         if (stat.Status & STATUS_SHELLOPEN) {
708                 cdr.DriveState = DRIVESTATE_LID_OPEN;
709                 cdr.StatP = STATUS_SHELLOPEN;
710         }
711         else if (CdromId[0] == '\0') {
712                 cdr.DriveState = DRIVESTATE_STOPPED;
713                 cdr.StatP = 0;
714         }
715         else {
716                 cdr.DriveState = DRIVESTATE_STANDBY;
717                 cdr.StatP = STATUS_ROTATING;
718         }
719
720         cdr.FifoOffset = DATA_SIZE; // fifo empty
721         cdr.LocL[0] = LOCL_INVALID;
722         cdr.Mode = MODE_SIZE_2340;
723         cdr.Muted = FALSE;
724         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
725                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
726 }
727
728 #define CMD_PART2           0x100
729 #define CMD_WHILE_NOT_READY 0x200
730
731 void cdrInterrupt(void) {
732         int start_rotating = 0;
733         int error = 0;
734         u32 cycles, seekTime = 0;
735         u32 second_resp_time = 0;
736         const void *buf;
737         u8 ParamC;
738         u8 set_loc[3];
739         int read_ok;
740         u16 not_ready = 0;
741         u8 IrqStat = Acknowledge;
742         u8 DriveStateOld;
743         u16 Cmd;
744         int i;
745
746         if (cdr.IrqStat) {
747                 CDR_LOG_I("cmd %02x with irqstat %x\n",
748                         cdr.CmdInProgress, cdr.IrqStat);
749                 return;
750         }
751         if (cdr.Irq1Pending) {
752                 // hand out the "newest" sector, according to nocash
753                 cdrUpdateTransferBuf(CDR_getBuffer());
754                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
755                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
756                         cdr.CmdInProgress, cdr.Irq1Pending);
757                 SetResultSize(1);
758                 cdr.Result[0] = cdr.Irq1Pending;
759                 cdr.Irq1Pending = 0;
760                 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
761                 return;
762         }
763
764         // default response
765         SetResultSize(1);
766         cdr.Result[0] = cdr.StatP;
767
768         Cmd = cdr.CmdInProgress;
769         cdr.CmdInProgress = 0;
770         ParamC = cdr.ParamC;
771
772         if (Cmd < 0x100) {
773                 cdr.Ctrl &= ~0x80;
774                 cdr.ParamC = 0;
775                 cdr.Cmd = 0;
776         }
777
778         switch (cdr.DriveState) {
779         case DRIVESTATE_PREPARE_CD:
780                 if (Cmd > 2) {
781                         // Syphon filter 2 expects commands to work shortly after it sees
782                         // STATUS_ROTATING, so give up trying to emulate the startup seq
783                         cdr.DriveState = DRIVESTATE_STANDBY;
784                         cdr.StatP &= ~STATUS_SEEK;
785                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
786                         break;
787                 }
788                 // fallthrough
789         case DRIVESTATE_LID_OPEN:
790         case DRIVESTATE_RESCAN_CD:
791                 // no disk or busy with the initial scan, allowed cmds are limited
792                 not_ready = CMD_WHILE_NOT_READY;
793                 break;
794         }
795
796         switch (Cmd | not_ready) {
797                 case CdlNop:
798                 case CdlNop + CMD_WHILE_NOT_READY:
799                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
800                                 cdr.StatP &= ~STATUS_SHELLOPEN;
801                         break;
802
803                 case CdlSetloc:
804                 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
805                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
806
807                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
808                         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))
809                         {
810                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
811                                 if (++cdr.errorRetryhack > 100)
812                                         break;
813                                 error = ERROR_BAD_ARGNUM;
814                                 goto set_error;
815                         }
816                         else
817                         {
818                                 for (i = 0; i < 3; i++)
819                                         set_loc[i] = btoi(cdr.Param[i]);
820                                 memcpy(cdr.SetSector, set_loc, 3);
821                                 cdr.SetSector[3] = 0;
822                                 cdr.SetlocPending = 1;
823                                 cdr.errorRetryhack = 0;
824                         }
825                         break;
826
827                 do_CdlPlay:
828                 case CdlPlay:
829                         StopCdda();
830                         StopReading();
831
832                         cdr.FastBackward = 0;
833                         cdr.FastForward = 0;
834
835                         // BIOS CD Player
836                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
837
838                         if (ParamC != 0 && cdr.Param[0] != 0) {
839                                 int track = btoi( cdr.Param[0] );
840
841                                 if (track <= cdr.ResultTN[1])
842                                         cdr.CurTrack = track;
843
844                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
845
846                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
847                                         for (i = 0; i < 3; i++)
848                                                 set_loc[i] = cdr.ResultTD[2 - i];
849                                         seekTime = cdrSeekTime(set_loc);
850                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
851                                 }
852                         }
853                         else if (cdr.SetlocPending) {
854                                 seekTime = cdrSeekTime(cdr.SetSector);
855                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
856                         }
857                         else {
858                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
859                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
860                         }
861                         cdr.SetlocPending = 0;
862
863                         /*
864                         Rayman: detect track changes
865                         - fixes logo freeze
866
867                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
868                         - plays tracks without retry play
869
870                         Wild 9: skip PREGAP + starting accurate SubQ
871                         - plays tracks without retry play
872                         */
873                         Find_CurTrack(cdr.SetSectorPlay);
874                         generate_subq(cdr.SetSectorPlay);
875                         cdr.LocL[0] = LOCL_INVALID;
876                         cdr.SubqForwardSectors = 1;
877                         cdr.TrackChanged = FALSE;
878                         cdr.FileChannelSelected = 0;
879                         cdr.AdpcmActive = 0;
880                         cdr.ReportDelay = 60;
881                         cdr.sectorsRead = 0;
882
883                         if (!Config.Cdda)
884                                 CDR_play(cdr.SetSectorPlay);
885
886                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
887                         
888                         // BIOS player - set flag again
889                         cdr.Play = TRUE;
890                         cdr.DriveState = DRIVESTATE_PLAY_READ;
891
892                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
893                         start_rotating = 1;
894                         break;
895
896                 case CdlForward:
897                         // TODO: error 80 if stopped
898                         IrqStat = Complete;
899
900                         // GameShark CD Player: Calls 2x + Play 2x
901                         cdr.FastForward = 1;
902                         cdr.FastBackward = 0;
903                         break;
904
905                 case CdlBackward:
906                         IrqStat = Complete;
907
908                         // GameShark CD Player: Calls 2x + Play 2x
909                         cdr.FastBackward = 1;
910                         cdr.FastForward = 0;
911                         break;
912
913                 case CdlStandby:
914                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
915                                 error = ERROR_BAD_ARGNUM;
916                                 goto set_error;
917                         }
918                         cdr.DriveState = DRIVESTATE_STANDBY;
919                         second_resp_time = cdReadTime * 125 / 2;
920                         start_rotating = 1;
921                         break;
922
923                 case CdlStandby + CMD_PART2:
924                         IrqStat = Complete;
925                         break;
926
927                 case CdlStop:
928                         if (cdr.Play) {
929                                 // grab time for current track
930                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
931
932                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
933                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
934                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
935                         }
936
937                         StopCdda();
938                         StopReading();
939                         SetPlaySeekRead(cdr.StatP, 0);
940                         cdr.StatP &= ~STATUS_ROTATING;
941                         cdr.LocL[0] = LOCL_INVALID;
942
943                         second_resp_time = 0x800;
944                         if (cdr.DriveState != DRIVESTATE_STOPPED)
945                                 second_resp_time = cdReadTime * 30 / 2;
946
947                         cdr.DriveState = DRIVESTATE_STOPPED;
948                         break;
949
950                 case CdlStop + CMD_PART2:
951                         IrqStat = Complete;
952                         break;
953
954                 case CdlPause:
955                         if (cdr.AdpcmActive) {
956                                 cdr.AdpcmActive = 0;
957                                 cdr.Xa.nsamples = 0;
958                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
959                         }
960                         StopCdda();
961                         StopReading();
962
963                         // how the drive maintains the position while paused is quite
964                         // complicated, this is the minimum to make "Bedlam" happy
965                         msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
966                         cdr.sectorsRead = 0;
967
968                         /*
969                         Gundam Battle Assault 2: much slower (*)
970                         - Fixes boot, gameplay
971
972                         Hokuto no Ken 2: slower
973                         - Fixes intro + subtitles
974
975                         InuYasha - Feudal Fairy Tale: slower
976                         - Fixes battles
977                         */
978                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
979                          * The timings from Duckstation are based upon hardware tests.
980                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
981                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
982                          * */
983                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
984                         {
985                                 second_resp_time = 7000;
986                         }
987                         else
988                         {
989                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
990                         }
991                         SetPlaySeekRead(cdr.StatP, 0);
992                         DriveStateOld = cdr.DriveState;
993                         cdr.DriveState = DRIVESTATE_PAUSED;
994                         if (DriveStateOld == DRIVESTATE_SEEK) {
995                                 // According to Duckstation this fails, but the
996                                 // exact conditions and effects are not clear.
997                                 // Moto Racer World Tour seems to rely on this.
998                                 // For now assume pause works anyway, just errors out.
999                                 error = ERROR_NOTREADY;
1000                                 goto set_error;
1001                         }
1002                         break;
1003
1004                 case CdlPause + CMD_PART2:
1005                         IrqStat = Complete;
1006                         break;
1007
1008                 case CdlReset:
1009                 case CdlReset + CMD_WHILE_NOT_READY:
1010                         // note: nocash and Duckstation calls this 'Init', but
1011                         // the official SDK calls it 'Reset', and so do we
1012                         StopCdda();
1013                         StopReading();
1014                         softReset();
1015                         second_resp_time = not_ready ? 70000 : 4100000;
1016                         start_rotating = 1;
1017                         break;
1018
1019                 case CdlReset + CMD_PART2:
1020                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1021                         IrqStat = Complete;
1022                         break;
1023
1024                 case CdlMute:
1025                         cdr.Muted = TRUE;
1026                         SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1027                         break;
1028
1029                 case CdlDemute:
1030                         cdr.Muted = FALSE;
1031                         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1032                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1033                         break;
1034
1035                 case CdlSetfilter:
1036                         cdr.FilterFile = cdr.Param[0];
1037                         cdr.FilterChannel = cdr.Param[1];
1038                         cdr.FileChannelSelected = 0;
1039                         break;
1040
1041                 case CdlSetmode:
1042                 case CdlSetmode + CMD_WHILE_NOT_READY:
1043                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1044                         cdr.Mode = cdr.Param[0];
1045                         break;
1046
1047                 case CdlGetparam:
1048                 case CdlGetparam + CMD_WHILE_NOT_READY:
1049                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1050                         SetResultSize_(5);
1051                         cdr.Result[1] = cdr.Mode;
1052                         cdr.Result[2] = 0;
1053                         cdr.Result[3] = cdr.FilterFile;
1054                         cdr.Result[4] = cdr.FilterChannel;
1055                         break;
1056
1057                 case CdlGetlocL:
1058                         if (cdr.LocL[0] == LOCL_INVALID) {
1059                                 error = 0x80;
1060                                 goto set_error;
1061                         }
1062                         SetResultSize_(8);
1063                         memcpy(cdr.Result, cdr.LocL, 8);
1064                         break;
1065
1066                 case CdlGetlocP:
1067                         SetResultSize_(8);
1068                         memcpy(&cdr.Result, &cdr.subq, 8);
1069                         break;
1070
1071                 case CdlReadT: // SetSession?
1072                         // really long
1073                         second_resp_time = cdReadTime * 290 / 4;
1074                         start_rotating = 1;
1075                         break;
1076
1077                 case CdlReadT + CMD_PART2:
1078                         IrqStat = Complete;
1079                         break;
1080
1081                 case CdlGetTN:
1082                         if (CDR_getTN(cdr.ResultTN) == -1) {
1083                                 assert(0);
1084                         }
1085                         SetResultSize_(3);
1086                         cdr.Result[1] = itob(cdr.ResultTN[0]);
1087                         cdr.Result[2] = itob(cdr.ResultTN[1]);
1088                         break;
1089
1090                 case CdlGetTD:
1091                         cdr.Track = btoi(cdr.Param[0]);
1092                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1093                                 error = ERROR_BAD_ARGVAL;
1094                                 goto set_error;
1095                         }
1096                         SetResultSize_(3);
1097                         cdr.Result[1] = itob(cdr.ResultTD[2]);
1098                         cdr.Result[2] = itob(cdr.ResultTD[1]);
1099                         // no sector number
1100                         //cdr.Result[3] = itob(cdr.ResultTD[0]);
1101                         break;
1102
1103                 case CdlSeekL:
1104                 case CdlSeekP:
1105                         StopCdda();
1106                         StopReading();
1107                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1108
1109                         seekTime = cdrSeekTime(cdr.SetSector);
1110                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1111                         cdr.DriveState = DRIVESTATE_SEEK;
1112                         /*
1113                         Crusaders of Might and Magic = 0.5x-4x
1114                         - fix cutscene speech start
1115
1116                         Eggs of Steel = 2x-?
1117                         - fix new game
1118
1119                         Medievil = ?-4x
1120                         - fix cutscene speech
1121
1122                         Rockman X5 = 0.5-4x
1123                         - fix capcom logo
1124                         */
1125                         second_resp_time = cdReadTime + seekTime;
1126                         start_rotating = 1;
1127                         break;
1128
1129                 case CdlSeekL + CMD_PART2:
1130                 case CdlSeekP + CMD_PART2:
1131                         SetPlaySeekRead(cdr.StatP, 0);
1132                         cdr.Result[0] = cdr.StatP;
1133                         IrqStat = Complete;
1134
1135                         Find_CurTrack(cdr.SetSectorPlay);
1136                         read_ok = ReadTrack(cdr.SetSectorPlay);
1137                         if (read_ok && (buf = CDR_getBuffer()))
1138                                 memcpy(cdr.LocL, buf, 8);
1139                         UpdateSubq(cdr.SetSectorPlay);
1140                         cdr.DriveState = DRIVESTATE_STANDBY;
1141                         cdr.TrackChanged = FALSE;
1142                         cdr.LastReadSeekCycles = psxRegs.cycle;
1143                         break;
1144
1145                 case CdlTest:
1146                 case CdlTest + CMD_WHILE_NOT_READY:
1147                         switch (cdr.Param[0]) {
1148                                 case 0x20: // System Controller ROM Version
1149                                         SetResultSize_(4);
1150                                         memcpy(cdr.Result, Test20, 4);
1151                                         break;
1152                                 case 0x22:
1153                                         SetResultSize_(8);
1154                                         memcpy(cdr.Result, Test22, 4);
1155                                         break;
1156                                 case 0x23: case 0x24:
1157                                         SetResultSize_(8);
1158                                         memcpy(cdr.Result, Test23, 4);
1159                                         break;
1160                         }
1161                         break;
1162
1163                 case CdlID:
1164                         second_resp_time = 20480;
1165                         break;
1166
1167                 case CdlID + CMD_PART2:
1168                         SetResultSize_(8);
1169                         cdr.Result[0] = cdr.StatP;
1170                         cdr.Result[1] = 0;
1171                         cdr.Result[2] = 0;
1172                         cdr.Result[3] = 0;
1173
1174                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1175                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1176                                 cdr.Result[1] = 0xc0;
1177                         }
1178                         else {
1179                                 if (stat.Type == 2)
1180                                         cdr.Result[1] |= 0x10;
1181                                 if (CdromId[0] == '\0')
1182                                         cdr.Result[1] |= 0x80;
1183                         }
1184                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1185                         CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1186                                 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1187
1188                         /* This adds the string "PCSX" in Playstation bios boot screen */
1189                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1190                         IrqStat = Complete;
1191                         break;
1192
1193                 case CdlInit:
1194                 case CdlInit + CMD_WHILE_NOT_READY:
1195                         StopCdda();
1196                         StopReading();
1197                         SetPlaySeekRead(cdr.StatP, 0);
1198                         // yes, it really sets STATUS_SHELLOPEN
1199                         cdr.StatP |= STATUS_SHELLOPEN;
1200                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1201                         set_event(PSXINT_CDRLID, 20480);
1202                         start_rotating = 1;
1203                         break;
1204
1205                 case CdlGetQ:
1206                 case CdlGetQ + CMD_WHILE_NOT_READY:
1207                         break;
1208
1209                 case CdlReadToc:
1210                 case CdlReadToc + CMD_WHILE_NOT_READY:
1211                         cdr.LocL[0] = LOCL_INVALID;
1212                         second_resp_time = cdReadTime * 180 / 4;
1213                         start_rotating = 1;
1214                         break;
1215
1216                 case CdlReadToc + CMD_PART2:
1217                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1218                         IrqStat = Complete;
1219                         break;
1220
1221                 case CdlReadN:
1222                 case CdlReadS:
1223                         if (cdr.Reading && !cdr.SetlocPending)
1224                                 break;
1225
1226                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1227
1228                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1229                                 // Read* acts as play for cdda tracks in cdda mode
1230                                 goto do_CdlPlay;
1231
1232                         StopCdda();
1233                         if (cdr.SetlocPending) {
1234                                 seekTime = cdrSeekTime(cdr.SetSector);
1235                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1236                                 cdr.SetlocPending = 0;
1237                         }
1238                         cdr.Reading = 1;
1239                         cdr.FileChannelSelected = 0;
1240                         cdr.AdpcmActive = 0;
1241
1242                         // Fighting Force 2 - update subq time immediately
1243                         // - fixes new game
1244                         UpdateSubq(cdr.SetSectorPlay);
1245                         cdr.LocL[0] = LOCL_INVALID;
1246                         cdr.SubqForwardSectors = 1;
1247                         cdr.sectorsRead = 0;
1248                         cdr.DriveState = DRIVESTATE_SEEK;
1249
1250                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1251                         cycles += seekTime;
1252                         if (Config.hacks.cdr_read_timing)
1253                                 cycles = cdrAlignTimingHack(cycles);
1254                         CDRPLAYREAD_INT(cycles, 1);
1255
1256                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1257                         start_rotating = 1;
1258                         break;
1259
1260                 case CdlSync:
1261                 default:
1262                         error = ERROR_INVALIDCMD;
1263                         // FALLTHROUGH
1264
1265                 set_error:
1266                         SetResultSize_(2);
1267                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1268                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1269                         IrqStat = DiskError;
1270                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1271                         break;
1272         }
1273
1274         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1275                 cdr.DriveState = DRIVESTATE_STANDBY;
1276                 cdr.StatP |= STATUS_ROTATING;
1277         }
1278
1279         if (second_resp_time) {
1280                 cdr.CmdInProgress = Cmd | 0x100;
1281                 set_event(PSXINT_CDR, second_resp_time);
1282         }
1283         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1284                 cdr.CmdInProgress = cdr.Cmd;
1285                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1286         }
1287
1288         setIrq(IrqStat, Cmd);
1289 }
1290
1291 #ifdef HAVE_ARMV7
1292  #define ssat32_to_16(v) \
1293   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1294 #else
1295  #define ssat32_to_16(v) do { \
1296   if (v < -32768) v = -32768; \
1297   else if (v > 32767) v = 32767; \
1298  } while (0)
1299 #endif
1300
1301 static void cdrPrepCdda(s16 *buf, int samples)
1302 {
1303 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1304         int i;
1305         for (i = 0; i < samples; i++) {
1306                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1307                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1308         }
1309 #endif
1310 }
1311
1312 static void cdrReadInterruptSetResult(unsigned char result)
1313 {
1314         if (cdr.IrqStat) {
1315                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1316                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1317                         cdr.CmdInProgress, cdr.IrqStat);
1318                 cdr.Irq1Pending = result;
1319                 return;
1320         }
1321         SetResultSize(1);
1322         cdr.Result[0] = result;
1323         setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1324 }
1325
1326 static void cdrUpdateTransferBuf(const u8 *buf)
1327 {
1328         if (!buf)
1329                 return;
1330         memcpy(cdr.Transfer, buf, DATA_SIZE);
1331         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1332         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1333                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1334         if (cdr.FifoOffset < 2048 + 12)
1335                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1336 }
1337
1338 static void cdrReadInterrupt(void)
1339 {
1340         const struct { u8 file, chan, mode, coding; } *subhdr;
1341         const u8 *buf = NULL;
1342         int deliver_data = 1;
1343         u8 subqPos[3];
1344         int read_ok;
1345         int is_start;
1346
1347         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1348         msfiAdd(subqPos, cdr.SubqForwardSectors);
1349         UpdateSubq(subqPos);
1350         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1351                 cdr.SubqForwardSectors++;
1352                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1353                 return;
1354         }
1355
1356         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1357         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1358         cdr.DriveState = DRIVESTATE_PLAY_READ;
1359         cdr.sectorsRead++;
1360
1361         read_ok = ReadTrack(cdr.SetSectorPlay);
1362         if (read_ok)
1363                 buf = CDR_getBuffer();
1364         if (buf == NULL)
1365                 read_ok = 0;
1366
1367         if (!read_ok) {
1368                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1369                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1370                 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1371                 return;
1372         }
1373         memcpy(cdr.LocL, buf, 8);
1374
1375         if (!cdr.IrqStat && !cdr.Irq1Pending)
1376                 cdrUpdateTransferBuf(buf);
1377
1378         subhdr = (void *)(buf + 4);
1379         do {
1380                 // try to process as adpcm
1381                 if (!(cdr.Mode & MODE_STRSND))
1382                         break;
1383                 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1384                         break;
1385                 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1386                         subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1387                 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1388                         break;
1389                 if (subhdr->chan & 0xe0) { // ?
1390                         if (subhdr->chan != 0xff)
1391                                 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1392                         break;
1393                 }
1394                 if (!cdr.FileChannelSelected) {
1395                         cdr.CurFile = subhdr->file;
1396                         cdr.CurChannel = subhdr->chan;
1397                         cdr.FileChannelSelected = 1;
1398                 }
1399                 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1400                         break;
1401
1402                 // accepted as adpcm
1403                 deliver_data = 0;
1404
1405                 if (Config.Xa)
1406                         break;
1407                 is_start = !cdr.AdpcmActive;
1408                 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1409                 if (cdr.AdpcmActive)
1410                         SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1411         } while (0);
1412
1413         if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1414                 deliver_data = 0;
1415
1416         /*
1417         Croc 2: $40 - only FORM1 (*)
1418         Judge Dredd: $C8 - only FORM1 (*)
1419         Sim Theme Park - no adpcm at all (zero)
1420         */
1421
1422         if (deliver_data)
1423                 cdrReadInterruptSetResult(cdr.StatP);
1424
1425         msfiAdd(cdr.SetSectorPlay, 1);
1426
1427         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1428 }
1429
1430 /*
1431 cdrRead0:
1432         bit 0,1 - reg index
1433         bit 2 - adpcm active
1434         bit 5 - 1 result ready
1435         bit 6 - 1 dma ready
1436         bit 7 - 1 command being processed
1437 */
1438
1439 unsigned char cdrRead0(void) {
1440         cdr.Ctrl &= ~0x24;
1441         cdr.Ctrl |= cdr.AdpcmActive << 2;
1442         cdr.Ctrl |= cdr.ResultReady << 5;
1443
1444         cdr.Ctrl |= 0x40; // data fifo not empty
1445
1446         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1447         cdr.Ctrl |= 0x18;
1448
1449         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1450
1451         return psxHu8(0x1800) = cdr.Ctrl;
1452 }
1453
1454 void cdrWrite0(unsigned char rt) {
1455         CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1456
1457         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1458 }
1459
1460 unsigned char cdrRead1(void) {
1461         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1462                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1463         else
1464                 psxHu8(0x1801) = 0;
1465         cdr.ResultP++;
1466         if (cdr.ResultP == cdr.ResultC)
1467                 cdr.ResultReady = 0;
1468
1469         CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1470
1471         return psxHu8(0x1801);
1472 }
1473
1474 void cdrWrite1(unsigned char rt) {
1475         const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1476         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1477
1478         switch (cdr.Ctrl & 3) {
1479         case 0:
1480                 break;
1481         case 3:
1482                 cdr.AttenuatorRightToRightT = rt;
1483                 return;
1484         default:
1485                 return;
1486         }
1487
1488 #ifdef CDR_LOG_CMD_IRQ
1489         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1490         if (cdr.ParamC) {
1491                 int i;
1492                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1493                 for (i = 0; i < cdr.ParamC; i++)
1494                         SysPrintf(" %x,", cdr.Param[i]);
1495                 SysPrintf("}");
1496         }
1497         SysPrintf(" @%08x\n", psxRegs.pc);
1498 #endif
1499
1500         cdr.ResultReady = 0;
1501         cdr.Ctrl |= 0x80;
1502
1503         if (!cdr.CmdInProgress) {
1504                 cdr.CmdInProgress = rt;
1505                 // should be something like 12k + controller delays
1506                 set_event(PSXINT_CDR, 5000);
1507         }
1508         else {
1509                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1510                         rt, cdr.Cmd, cdr.CmdInProgress);
1511                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1512                         cdr.CmdInProgress = rt;
1513         }
1514
1515         cdr.Cmd = rt;
1516 }
1517
1518 unsigned char cdrRead2(void) {
1519         unsigned char ret = cdr.Transfer[0x920];
1520
1521         if (cdr.FifoOffset < cdr.FifoSize)
1522                 ret = cdr.Transfer[cdr.FifoOffset++];
1523         else
1524                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1525
1526         CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1527         return ret;
1528 }
1529
1530 void cdrWrite2(unsigned char rt) {
1531         const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1532         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1533
1534         switch (cdr.Ctrl & 3) {
1535         case 0:
1536                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1537                         cdr.Param[cdr.ParamC++] = rt;
1538                 return;
1539         case 1:
1540                 cdr.IrqMask = rt;
1541                 setIrq(cdr.IrqStat, 0x1005);
1542                 return;
1543         case 2:
1544                 cdr.AttenuatorLeftToLeftT = rt;
1545                 return;
1546         case 3:
1547                 cdr.AttenuatorRightToLeftT = rt;
1548                 return;
1549         }
1550 }
1551
1552 unsigned char cdrRead3(void) {
1553         if (cdr.Ctrl & 0x1)
1554                 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1555         else
1556                 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1557
1558         CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1559                 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1560         return psxHu8(0x1803);
1561 }
1562
1563 void cdrWrite3(unsigned char rt) {
1564         const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1565         u8 ll, lr, rl, rr;
1566         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1567
1568         switch (cdr.Ctrl & 3) {
1569         case 0:
1570                 break; // transfer
1571         case 1:
1572                 if (cdr.IrqStat & rt) {
1573                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1574                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1575                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1576 #ifdef CDR_LOG_CMD_IRQ
1577                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1578                                 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1579                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1580 #endif
1581                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1582                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1583                         {
1584                                 s32 c = 2048;
1585                                 if (cdr.CmdInProgress) {
1586                                         c = 2048 - (psxRegs.cycle - nextCycle);
1587                                         c = MAX_VALUE(c, 512);
1588                                 }
1589                                 set_event(PSXINT_CDR, c);
1590                         }
1591                 }
1592                 cdr.IrqStat &= ~rt;
1593
1594                 if (rt & 0x40)
1595                         cdr.ParamC = 0;
1596                 return;
1597         case 2:
1598                 cdr.AttenuatorLeftToRightT = rt;
1599                 return;
1600         case 3:
1601                 if (rt & 0x01)
1602                         log_unhandled("Mute ADPCM?\n");
1603                 if (rt & 0x20) {
1604                         ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1605                         rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1606                         if (ll == cdr.AttenuatorLeftToLeft &&
1607                             lr == cdr.AttenuatorLeftToRight &&
1608                             rl == cdr.AttenuatorRightToLeft &&
1609                             rr == cdr.AttenuatorRightToRight)
1610                                 return;
1611                         cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1612                         cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1613                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1614                         SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1615                 }
1616                 return;
1617         }
1618
1619         // test: Viewpoint
1620         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1621                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1622         }
1623         else if (rt & 0x80) {
1624                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1625                         case MODE_SIZE_2328:
1626                         case 0x00:
1627                                 cdr.FifoOffset = 12;
1628                                 cdr.FifoSize = 2048 + 12;
1629                                 break;
1630
1631                         case MODE_SIZE_2340:
1632                         default:
1633                                 cdr.FifoOffset = 0;
1634                                 cdr.FifoSize = 2340;
1635                                 break;
1636                 }
1637         }
1638         else if (!(rt & 0xc0))
1639                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1640 }
1641
1642 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1643         u32 cdsize, max_words;
1644         int size;
1645         u8 *ptr;
1646
1647 #if 0
1648         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1649         if (cdr.FifoOffset == 0) {
1650                 ptr = cdr.Transfer;
1651                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1652         }
1653         SysPrintf("\n");
1654 #endif
1655
1656         switch (chcr & 0x71000000) {
1657                 case 0x11000000:
1658                         madr &= ~3;
1659                         ptr = getDmaRam(madr, &max_words);
1660                         if (ptr == INVALID_PTR) {
1661                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1662                                 break;
1663                         }
1664
1665                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1666
1667                         /*
1668                         GS CDX: Enhancement CD crash
1669                         - Setloc 0:0:0
1670                         - CdlPlay
1671                         - Spams DMA3 and gets buffer overrun
1672                         */
1673                         size = DATA_SIZE - cdr.FifoOffset;
1674                         if (size > cdsize)
1675                                 size = cdsize;
1676                         if (size > max_words * 4)
1677                                 size = max_words * 4;
1678                         if (size > 0)
1679                         {
1680                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1681                                 cdr.FifoOffset += size;
1682                         }
1683                         if (size < cdsize) {
1684                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1685                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1686                         }
1687                         psxCpu->Clear(madr, cdsize / 4);
1688
1689                         set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1690
1691                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1692                         if (chcr & 0x100) {
1693                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1694                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1695                         }
1696                         else {
1697                                 // halted
1698                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1699                         }
1700                         return;
1701
1702                 default:
1703                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1704                         break;
1705         }
1706
1707         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1708         DMA_INTERRUPT(3);
1709 }
1710
1711 void cdrDmaInterrupt(void)
1712 {
1713         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1714         {
1715                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1716                 DMA_INTERRUPT(3);
1717         }
1718 }
1719
1720 static void getCdInfo(void)
1721 {
1722         u8 tmp;
1723
1724         CDR_getTN(cdr.ResultTN);
1725         CDR_getTD(0, cdr.SetSectorEnd);
1726         tmp = cdr.SetSectorEnd[0];
1727         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1728         cdr.SetSectorEnd[2] = tmp;
1729 }
1730
1731 void cdrReset() {
1732         memset(&cdr, 0, sizeof(cdr));
1733         cdr.CurTrack = 1;
1734         cdr.FilterFile = 0;
1735         cdr.FilterChannel = 0;
1736         cdr.IrqMask = 0x1f;
1737         cdr.IrqStat = NoIntr;
1738
1739         // BIOS player - default values
1740         cdr.AttenuatorLeftToLeft = 0x80;
1741         cdr.AttenuatorLeftToRight = 0x00;
1742         cdr.AttenuatorRightToLeft = 0x00;
1743         cdr.AttenuatorRightToRight = 0x80;
1744
1745         softReset();
1746         getCdInfo();
1747 }
1748
1749 int cdrFreeze(void *f, int Mode) {
1750         u32 tmp;
1751         u8 tmpp[3];
1752
1753         if (Mode == 0 && !Config.Cdda)
1754                 CDR_stop();
1755         
1756         cdr.freeze_ver = 0x63647202;
1757         gzfreeze(&cdr, sizeof(cdr));
1758         
1759         if (Mode == 1) {
1760                 cdr.ParamP = cdr.ParamC;
1761                 tmp = cdr.FifoOffset;
1762         }
1763
1764         gzfreeze(&tmp, sizeof(tmp));
1765
1766         if (Mode == 0) {
1767                 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1768                 getCdInfo();
1769
1770                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1771                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1772                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1773                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1774
1775                 // read right sub data
1776                 tmpp[0] = btoi(cdr.Prev[0]);
1777                 tmpp[1] = btoi(cdr.Prev[1]);
1778                 tmpp[2] = btoi(cdr.Prev[2]);
1779                 cdr.Prev[0]++;
1780                 ReadTrack(tmpp);
1781
1782                 if (cdr.Play) {
1783                         if (cdr.freeze_ver < 0x63647202)
1784                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1785
1786                         Find_CurTrack(cdr.SetSectorPlay);
1787                         if (!Config.Cdda)
1788                                 CDR_play(cdr.SetSectorPlay);
1789                 }
1790                 if (!cdr.Muted)
1791                         ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToLeft,
1792                         rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1793                 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1794         }
1795
1796         return 0;
1797 }
1798
1799 void LidInterrupt(void) {
1800         getCdInfo();
1801         cdrLidSeekInterrupt();
1802 }