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