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