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