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