50255c8028c137c2e6f5b77ef2b5209b8a767139
[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 <stdalign.h>
25 #include <assert.h>
26 #include "cdrom.h"
27 #include "cdrom-async.h"
28 #include "misc.h"
29 #include "ppf.h"
30 #include "psxdma.h"
31 #include "psxevents.h"
32 #include "arm_features.h"
33
34 /* logging */
35 #if 0
36 #define CDR_LOG SysPrintf
37 #else
38 #define CDR_LOG(...)
39 #endif
40 #if 0
41 #define CDR_LOG_I SysPrintf
42 #else
43 #define CDR_LOG_I(fmt, ...) \
44         log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45 #endif
46 #if 0
47 #define CDR_LOG_IO SysPrintf
48 #else
49 #define CDR_LOG_IO(...)
50 #endif
51 //#define CDR_LOG_CMD_IRQ
52
53 static struct {
54         // unused members maintain savesate compatibility
55         unsigned char unused0;
56         unsigned char unused1;
57         unsigned char IrqMask;
58         unsigned char unused2;
59         unsigned char Ctrl;
60         unsigned char IrqStat;
61
62         unsigned char StatP;
63
64         unsigned char Transfer[DATA_SIZE];
65         struct {
66                 unsigned char Track;
67                 unsigned char Index;
68                 unsigned char Relative[3];
69                 unsigned char Absolute[3];
70         } subq;
71         unsigned char TrackChanged;
72         unsigned char ReportDelay;
73         unsigned char PhysCdPropagations;
74         unsigned short sectorsRead;
75         unsigned int  freeze_ver;
76
77         unsigned char Prev[4];
78         unsigned char Param[8];
79         unsigned char Result[16];
80
81         unsigned char ParamC;
82         unsigned char ParamP;
83         unsigned char ResultC;
84         unsigned char ResultP;
85         unsigned char ResultReady;
86         unsigned char Cmd;
87         unsigned char SubqForwardSectors;
88         unsigned char SetlocPending;
89         u32 Reading;
90
91         unsigned char ResultTN[6];
92         unsigned char ResultTD[4];
93         unsigned char SetSectorPlay[4];
94         unsigned char SetSectorEnd[4];
95         unsigned char SetSector[4];
96         unsigned char Track;
97         boolean Play, Muted;
98         int CurTrack;
99         unsigned char Mode;
100         unsigned char FileChannelSelected;
101         unsigned char CurFile, CurChannel;
102         int FilterFile, FilterChannel;
103         unsigned char LocL[8];
104         int unused4;
105
106         xa_decode_t Xa;
107
108         u16 FifoOffset;
109         u16 FifoSize;
110
111         u16 CmdInProgress;
112         u8 Irq1Pending;
113         u8 AdpcmActive;
114         u32 LastReadSeekCycles;
115
116         u8 RetryDetected;
117
118         u8 DriveState; // enum drive_state
119         u8 FastForward;
120         u8 FastBackward;
121         u8 errorRetryhack;
122
123         u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
124         u8 AttenuatorRightToRight, AttenuatorRightToLeft;
125         u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
126         u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
127 } cdr;
128 alignas(64) static s16 read_buf[CD_FRAMESIZE_RAW_ALIGNED / 2];
129
130 struct SubQ {
131         char res0[12];
132         unsigned char ControlAndADR;
133         unsigned char TrackNumber;
134         unsigned char IndexNumber;
135         unsigned char TrackRelativeAddress[3];
136         unsigned char Filler;
137         unsigned char AbsoluteAddress[3];
138         unsigned char CRC[2];
139         char res1[72];
140 };
141
142 /* CD-ROM magic numbers */
143 #define CdlSync        0  /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
144 #define CdlNop         1
145 #define CdlSetloc      2
146 #define CdlPlay        3
147 #define CdlForward     4
148 #define CdlBackward    5
149 #define CdlReadN       6
150 #define CdlStandby     7
151 #define CdlStop        8
152 #define CdlPause       9
153 #define CdlReset       10
154 #define CdlMute        11
155 #define CdlDemute      12
156 #define CdlSetfilter   13
157 #define CdlSetmode     14
158 #define CdlGetparam    15
159 #define CdlGetlocL     16
160 #define CdlGetlocP     17
161 #define CdlReadT       18
162 #define CdlGetTN       19
163 #define CdlGetTD       20
164 #define CdlSeekL       21
165 #define CdlSeekP       22
166 #define CdlSetclock    23
167 #define CdlGetclock    24
168 #define CdlTest        25
169 #define CdlID          26
170 #define CdlReadS       27
171 #define CdlInit        28
172 #define CdlGetQ        29
173 #define CdlReadToc     30
174
175 #ifdef CDR_LOG_CMD_IRQ
176 static const char * const CmdName[0x100] = {
177     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
178     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
179     "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
180     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
181     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
182     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
183     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
184     "CdlInit",     NULL,           "CDlReadToc", NULL
185 };
186 #endif
187
188 unsigned char Test04[] = { 0 };
189 unsigned char Test05[] = { 0 };
190 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
191 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
192 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
193
194 // cdr.IrqStat:
195 #define NoIntr          0
196 #define DataReady       1
197 #define Complete        2
198 #define Acknowledge     3
199 #define DataEnd         4
200 #define DiskError       5
201
202 /* Modes flags */
203 #define MODE_SPEED       (1<<7) // 0x80
204 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
205 #define MODE_SIZE_2340   (1<<5) // 0x20
206 #define MODE_SIZE_2328   (1<<4) // 0x10
207 #define MODE_SIZE_2048   (0<<4) // 0x00
208 #define MODE_SF          (1<<3) // 0x08 channel on/off
209 #define MODE_REPORT      (1<<2) // 0x04
210 #define MODE_AUTOPAUSE   (1<<1) // 0x02
211 #define MODE_CDDA        (1<<0) // 0x01
212
213 /* Status flags */
214 #define STATUS_PLAY      (1<<7) // 0x80
215 #define STATUS_SEEK      (1<<6) // 0x40
216 #define STATUS_READ      (1<<5) // 0x20
217 #define STATUS_SHELLOPEN (1<<4) // 0x10
218 #define STATUS_UNKNOWN3  (1<<3) // 0x08
219 #define STATUS_SEEKERROR (1<<2) // 0x04
220 #define STATUS_ROTATING  (1<<1) // 0x02
221 #define STATUS_ERROR     (1<<0) // 0x01
222
223 /* Errors */
224 #define ERROR_NOTREADY   (1<<7) // 0x80
225 #define ERROR_INVALIDCMD (1<<6) // 0x40
226 #define ERROR_BAD_ARGNUM (1<<5) // 0x20
227 #define ERROR_BAD_ARGVAL (1<<4) // 0x10
228 #define ERROR_SHELLOPEN  (1<<3) // 0x08
229
230 // 1x = 75 sectors per second
231 // PSXCLK = 1 sec in the ps
232 // so (PSXCLK / 75) = cdr read time (linuzappz)
233 #define cdReadTime (PSXCLK / 75)
234
235 #define LOCL_INVALID 0xff
236 #define SUBQ_FORWARD_SECTORS 2u
237
238 enum drive_state {
239         DRIVESTATE_STANDBY = 0, // different from paused
240         DRIVESTATE_LID_OPEN,
241         DRIVESTATE_RESCAN_CD,
242         DRIVESTATE_PREPARE_CD,
243         DRIVESTATE_STOPPED,
244         DRIVESTATE_PAUSED,
245         DRIVESTATE_PLAY_READ,
246         DRIVESTATE_SEEK,
247 };
248
249 static struct CdrStat cdr_stat;
250
251 static unsigned int msf2sec(const u8 *msf) {
252         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
253 }
254
255 // cdrPlayReadInterrupt
256 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
257         u32 e_ = eCycle; \
258         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
259         if (isFirst) \
260                 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
261         else \
262                 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
263         psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
264         set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
265 }
266
267 #define StopReading() { \
268         cdr.Reading = 0; \
269         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
270 }
271
272 #define StopCdda() { \
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 (cdra_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 (cdra_getStatus(&cdr_stat) != 0)
343                         cdr_stat.Status &= ~STATUS_SHELLOPEN;
344
345                 // 02, 12, 10
346                 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
347                         int was_reading = cdr.Reading;
348                         StopReading();
349                         SetPlaySeekRead(cdr.StatP, 0);
350                         cdr.StatP |= STATUS_SHELLOPEN;
351                         memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
352
353                         // IIRC this sometimes doesn't happen on real hw
354                         // (when lots of commands are sent?)
355                         SetResultSize(2);
356                         cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
357                         cdr.Result[1] = ERROR_SHELLOPEN;
358                         if (cdr.CmdInProgress || was_reading) {
359                                 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
360                                 cdr.CmdInProgress = 0;
361                                 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
362                                 cdr.Result[1] = ERROR_NOTREADY;
363                         }
364                         setIrq(DiskError, 0x1006);
365
366                         set_event(PSXINT_CDRLID, cdReadTime * 30);
367                         break;
368                 }
369                 else if (cdr.StatP & STATUS_ROTATING) {
370                         cdr.StatP &= ~STATUS_ROTATING;
371                 }
372                 else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) {
373                         // closed now
374                         CheckCdrom();
375
376                         // cdr.StatP STATUS_SHELLOPEN is "sticky"
377                         // and is only cleared by CdlNop
378
379                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
380                         set_event(PSXINT_CDRLID, cdReadTime * 105);
381                         break;
382                 }
383
384                 // recheck for close
385                 set_event(PSXINT_CDRLID, cdReadTime * 3);
386                 break;
387
388         case DRIVESTATE_RESCAN_CD:
389                 cdr.StatP |= STATUS_ROTATING;
390                 cdr.DriveState = DRIVESTATE_PREPARE_CD;
391
392                 // this is very long on real hardware, over 6 seconds
393                 // make it a bit faster here...
394                 set_event(PSXINT_CDRLID, cdReadTime * 150);
395                 break;
396
397         case DRIVESTATE_PREPARE_CD:
398                 if (cdr.StatP & STATUS_SEEK) {
399                         SetPlaySeekRead(cdr.StatP, 0);
400                         cdr.DriveState = DRIVESTATE_STANDBY;
401                 }
402                 else {
403                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
404                         set_event(PSXINT_CDRLID, cdReadTime * 26);
405                 }
406                 break;
407         }
408 }
409
410 static void Find_CurTrack(const u8 *time)
411 {
412         int current, sect;
413
414         current = msf2sec(time);
415
416         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
417                 cdra_getTD(cdr.CurTrack + 1, cdr.ResultTD);
418                 sect = msf2sec(cdr.ResultTD);
419                 if (sect - current >= 150)
420                         break;
421         }
422 }
423
424 static void generate_subq(const u8 *time)
425 {
426         unsigned char start[3], next[3];
427         unsigned int this_s, start_s, next_s, pregap;
428         int relative_s;
429
430         cdra_getTD(cdr.CurTrack, start);
431         if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
432                 pregap = 150;
433                 cdra_getTD(cdr.CurTrack + 1, next);
434         }
435         else {
436                 // last track - cd size
437                 pregap = 0;
438                 memcpy(next, cdr.SetSectorEnd, 3);
439         }
440
441         this_s = msf2sec(time);
442         start_s = msf2sec(start);
443         next_s = msf2sec(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         lba2msf(relative_s, &cdr.subq.Relative[0],
461                 &cdr.subq.Relative[1], &cdr.subq.Relative[2]);
462
463         cdr.subq.Track = itob(cdr.CurTrack);
464         cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
465         cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
466         cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
467         cdr.subq.Absolute[0] = itob(time[0]);
468         cdr.subq.Absolute[1] = itob(time[1]);
469         cdr.subq.Absolute[2] = itob(time[2]);
470 }
471
472 static int ReadTrack(const u8 *time)
473 {
474         int ret;
475
476         CDR_LOG("ReadTrack *** %02d:%02d:%02d\n", tmp[0], tmp[1], tmp[2]);
477
478         if (memcmp(cdr.Prev, time, 3) == 0)
479                 return 1;
480
481         ret = cdra_readTrack(time);
482         if (ret == 0)
483                 memcpy(cdr.Prev, time, 3);
484         return ret == 0;
485 }
486
487 static void UpdateSubq(const u8 *time)
488 {
489         int ret = -1, s = MSF2SECT(time[0], time[1], time[2]);
490         struct SubQ subq;
491         u16 crc;
492
493         if (CheckSBI(s))
494                 return;
495
496         if (cdr.CurTrack == 1)
497                 ret = cdra_readSub(time, &subq);
498         if (ret == 0) {
499                 crc = calcCrc((u8 *)&subq + 12, 10);
500                 if (crc == (((u16)subq.CRC[0] << 8) | subq.CRC[1])) {
501                         cdr.subq.Track = subq.TrackNumber;
502                         cdr.subq.Index = subq.IndexNumber;
503                         memcpy(cdr.subq.Relative, subq.TrackRelativeAddress, 3);
504                         memcpy(cdr.subq.Absolute, subq.AbsoluteAddress, 3);
505                 }
506                 else {
507                         CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
508                                 time[0], time[1], time[2]);
509                 }
510         }
511         else {
512                 generate_subq(time);
513         }
514
515         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
516                 cdr.subq.Track, cdr.subq.Index,
517                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
518                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
519 }
520
521 static void cdrPlayInterrupt_Autopause()
522 {
523         u32 abs_lev_max = 0;
524         boolean abs_lev_chselect;
525         u32 i;
526
527         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
528                 CDR_LOG_I("autopause\n");
529
530                 SetResultSize(1);
531                 cdr.Result[0] = cdr.StatP;
532                 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
533
534                 StopCdda();
535                 SetPlaySeekRead(cdr.StatP, 0);
536                 cdr.DriveState = DRIVESTATE_PAUSED;
537         }
538         else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
539                  ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
540         {
541                 SetResultSize(8);
542                 cdr.Result[0] = cdr.StatP;
543                 cdr.Result[1] = cdr.subq.Track;
544                 cdr.Result[2] = cdr.subq.Index;
545                 
546                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
547                 
548                 /* 8 is a hack. For accuracy, it should be 588. */
549                 for (i = 0; i < 8; i++)
550                 {
551                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
552                 }
553                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
554                 abs_lev_max |= abs_lev_chselect << 15;
555
556                 if (cdr.subq.Absolute[2] & 0x10) {
557                         cdr.Result[3] = cdr.subq.Relative[0];
558                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
559                         cdr.Result[5] = cdr.subq.Relative[2];
560                 }
561                 else {
562                         cdr.Result[3] = cdr.subq.Absolute[0];
563                         cdr.Result[4] = cdr.subq.Absolute[1];
564                         cdr.Result[5] = cdr.subq.Absolute[2];
565                 }
566                 cdr.Result[6] = abs_lev_max >> 0;
567                 cdr.Result[7] = abs_lev_max >> 8;
568
569                 setIrq(DataReady, 0x1001);
570         }
571
572         if (cdr.ReportDelay)
573                 cdr.ReportDelay--;
574 }
575
576 static boolean canDoTurbo(void)
577 {
578         u32 c = psxRegs.cycle;
579         return Config.TurboCD && !cdr.RetryDetected && !cdr.AdpcmActive
580                 //&& c - psxRegs.intCycle[PSXINT_SPUDMA].sCycle > (u32)cdReadTime * 2
581                 && c - psxRegs.intCycle[PSXINT_MDECOUTDMA].sCycle > (u32)cdReadTime * 16;
582 }
583
584 static int cdrSeekTime(unsigned char *target)
585 {
586         int diff = abs((int)msf2sec(cdr.SetSectorPlay) - (int)msf2sec(target));
587         int seekTime = diff * (cdReadTime / 2000);
588         int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
589         seekTime = MAX_VALUE(seekTime, 20000);
590
591         // sled seek?
592         if (diff >= 7200)
593                 seekTime = PSXCLK / 7 + diff * 64;
594         // add *something* as rotation time until the target sector
595         if (cyclesSinceRS >= cdReadTime)
596                 seekTime += (8 - ((cyclesSinceRS >> 18) & 7)) * (cdReadTime / 2);
597
598         // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
599         // and then wants some slack time
600         if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
601                 seekTime += cdReadTime;
602
603         //seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
604         CDR_LOG("seek: %02d:%02d:%02d %.2f %.2f (%.2f) st %d di %d\n",
605                 target[0], target[1], target[2], (float)seekTime / PSXCLK,
606                 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
607                 cdr.DriveState, diff);
608         return seekTime;
609 }
610
611 static u32 cdrAlignTimingHack(u32 cycles)
612 {
613         /*
614          * timing hack for T'ai Fu - Wrath of the Tiger:
615          * The game has a bug where it issues some cdc commands from a low priority
616          * vint handler, however there is a higher priority default bios handler
617          * that acks the vint irq and returns, so game's handler is not reached
618          * (see bios irq handler chains at e004 and the game's irq handling func
619          * at 80036810). For the game to work, vint has to arrive after the bios
620          * vint handler rejects some other irq (of which only cd and rcnt2 are
621          * active), but before the game's handler loop reads I_STAT. The time
622          * window for this is quite small (~1k cycles of so). Apparently this
623          * somehow happens naturally on the real hardware.
624          *
625          * Note: always enforcing this breaks other games like Crash PAL version
626          * (inputs get dropped because bios handler doesn't see interrupts).
627          */
628         u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
629         while ((s32)(vint_rel - cycles) < 0)
630                 vint_rel += PSXCLK / 60;
631         return vint_rel;
632 }
633
634 static void cdrUpdateTransferBuf(const u8 *buf);
635 static void cdrReadInterrupt(void);
636 static void cdrPrepCdda(s16 *buf, int samples);
637
638 static void msfiAdd(u8 *msfi, u32 count)
639 {
640         assert(count < 75);
641         msfi[2] += count;
642         if (msfi[2] >= 75) {
643                 msfi[2] -= 75;
644                 msfi[1]++;
645                 if (msfi[1] == 60) {
646                         msfi[1] = 0;
647                         msfi[0]++;
648                 }
649         }
650 }
651
652 static void msfiSub(u8 *msfi, u32 count)
653 {
654         assert(count < 75);
655         msfi[2] -= count;
656         if ((s8)msfi[2] < 0) {
657                 msfi[2] += 75;
658                 msfi[1]--;
659                 if ((s8)msfi[1] < 0) {
660                         msfi[1] = 60;
661                         msfi[0]--;
662                 }
663         }
664 }
665
666 static int msfiEq(const u8 *a, const u8 *b)
667 {
668         return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
669 }
670
671 void cdrPlayReadInterrupt(void)
672 {
673         // this works but causes instability for timing sensitive games
674 #if 0
675         int hit = cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
676         if (!hit && cdr.PhysCdPropagations < 75/2) {
677                 // this propagates the real cdrom delays to the emulated game
678                 CDRPLAYREAD_INT(cdReadTime / 2, 0);
679                 cdr.PhysCdPropagations++;
680                 return;
681         }
682 #endif
683         cdr.LastReadSeekCycles = psxRegs.cycle;
684
685         if (cdr.Reading) {
686                 cdrReadInterrupt();
687                 return;
688         }
689
690         if (!cdr.Play) return;
691
692         CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
693                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
694
695         cdr.DriveState = DRIVESTATE_PLAY_READ;
696         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
697         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
698                 CDR_LOG_I("end stop\n");
699                 StopCdda();
700                 SetPlaySeekRead(cdr.StatP, 0);
701                 cdr.TrackChanged = TRUE;
702                 cdr.DriveState = DRIVESTATE_PAUSED;
703         }
704         else {
705                 cdra_readCDDA(cdr.SetSectorPlay, read_buf);
706         }
707
708         if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
709                 cdrPlayInterrupt_Autopause();
710
711         if (cdr.Play && !Config.Cdda) {
712                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
713                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
714         }
715
716         msfiAdd(cdr.SetSectorPlay, 1);
717         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
718
719         // update for CdlGetlocP/autopause
720         generate_subq(cdr.SetSectorPlay);
721
722         CDRPLAYREAD_INT(cdReadTime, 0);
723
724         // stop propagation since it breaks streaming
725         cdr.PhysCdPropagations = 0xff;
726 }
727
728 static void softReset(void)
729 {
730         cdra_getStatus(&cdr_stat);
731         if (cdr_stat.Status & STATUS_SHELLOPEN) {
732                 cdr.DriveState = DRIVESTATE_LID_OPEN;
733                 cdr.StatP = STATUS_SHELLOPEN;
734         }
735         else if (CdromId[0] == '\0') {
736                 cdr.DriveState = DRIVESTATE_STOPPED;
737                 cdr.StatP = 0;
738         }
739         else {
740                 cdr.DriveState = DRIVESTATE_STANDBY;
741                 cdr.StatP = STATUS_ROTATING;
742         }
743
744         cdr.FifoOffset = DATA_SIZE; // fifo empty
745         cdr.LocL[0] = LOCL_INVALID;
746         cdr.Mode = MODE_SIZE_2340;
747         cdr.Muted = FALSE;
748         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
749                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
750 }
751
752 #define CMD_PART2           0x100
753 #define CMD_WHILE_NOT_READY 0x200
754
755 void cdrInterrupt(void) {
756         int start_rotating = 0;
757         int error = 0;
758         u32 cycles, seekTime = 0;
759         u32 second_resp_time = 0;
760         const void *buf;
761         u8 ParamC;
762         int read_ok;
763         u16 not_ready = 0;
764         u8 IrqStat = Acknowledge;
765         u8 DriveStateOld;
766         u16 Cmd;
767         int i;
768
769         if (cdr.IrqStat) {
770                 CDR_LOG_I("cmd %02x with irqstat %x\n",
771                         cdr.CmdInProgress, cdr.IrqStat);
772                 return;
773         }
774         if (cdr.Irq1Pending) {
775                 // hand out the "newest" sector, according to nocash
776                 cdrUpdateTransferBuf(cdra_getBuffer());
777                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
778                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
779                         cdr.CmdInProgress, cdr.Irq1Pending);
780                 SetResultSize(1);
781                 cdr.Result[0] = cdr.Irq1Pending;
782                 cdr.Irq1Pending = 0;
783                 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
784                 return;
785         }
786
787         // default response
788         SetResultSize(1);
789         cdr.Result[0] = cdr.StatP;
790
791         Cmd = cdr.CmdInProgress;
792         cdr.CmdInProgress = 0;
793         ParamC = cdr.ParamC;
794
795         if (Cmd < 0x100) {
796                 cdr.Ctrl &= ~0x80;
797                 cdr.ParamC = 0;
798                 cdr.Cmd = 0;
799         }
800
801         switch (cdr.DriveState) {
802         case DRIVESTATE_PREPARE_CD:
803                 if (Cmd > 2) {
804                         // Syphon filter 2 expects commands to work shortly after it sees
805                         // STATUS_ROTATING, so give up trying to emulate the startup seq
806                         cdr.DriveState = DRIVESTATE_STANDBY;
807                         cdr.StatP &= ~STATUS_SEEK;
808                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
809                         break;
810                 }
811                 // fallthrough
812         case DRIVESTATE_LID_OPEN:
813         case DRIVESTATE_RESCAN_CD:
814                 // no disk or busy with the initial scan, allowed cmds are limited
815                 not_ready = CMD_WHILE_NOT_READY;
816                 break;
817         }
818
819         switch (Cmd | not_ready) {
820                 case CdlNop:
821                 case CdlNop + CMD_WHILE_NOT_READY:
822                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
823                                 cdr.StatP &= ~STATUS_SHELLOPEN;
824                         break;
825
826                 case CdlSetloc:
827                 case CdlSetloc + CMD_WHILE_NOT_READY: // apparently?
828                         if (cdr.StatP & STATUS_SHELLOPEN)
829                                 // wrong? Driver2 vs Amerzone
830                                 goto set_error;
831
832                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
833                         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))
834                         {
835                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
836                                 if (++cdr.errorRetryhack > 100)
837                                         break;
838                                 error = ERROR_BAD_ARGNUM;
839                                 goto set_error;
840                         }
841                         else
842                         {
843                                 u8 set_loc[3];
844                                 for (i = 0; i < 3; i++)
845                                         set_loc[i] = btoi(cdr.Param[i]);
846                                 if ((msfiEq(cdr.SetSector, set_loc)) //|| msfiEq(cdr.Param, cdr.Transfer))
847                                                 && !cdr.SetlocPending)
848                                         cdr.RetryDetected++;
849                                 else
850                                         cdr.RetryDetected = 0;
851                                 memcpy(cdr.SetSector, set_loc, 3);
852                                 cdr.SetSector[3] = 0;
853                                 cdr.SetlocPending = 1;
854                                 cdr.errorRetryhack = 0;
855                         }
856                         break;
857
858                 do_CdlPlay:
859                 case CdlPlay:
860                         StopCdda();
861                         StopReading();
862
863                         cdr.FastBackward = 0;
864                         cdr.FastForward = 0;
865
866                         // BIOS CD Player
867                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
868
869                         if (ParamC != 0 && cdr.Param[0] != 0) {
870                                 int track = btoi( cdr.Param[0] );
871
872                                 if (track <= cdr.ResultTN[1])
873                                         cdr.CurTrack = track;
874
875                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
876
877                                 if (cdra_getTD(cdr.CurTrack, cdr.ResultTD) != -1) {
878                                         seekTime = cdrSeekTime(cdr.ResultTD);
879                                         memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
880                                 }
881                         }
882                         else if (cdr.SetlocPending) {
883                                 seekTime = cdrSeekTime(cdr.SetSector);
884                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
885                         }
886                         else {
887                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
888                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
889                         }
890                         cdr.SetlocPending = 0;
891
892                         /*
893                         Rayman: detect track changes
894                         - fixes logo freeze
895
896                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
897                         - plays tracks without retry play
898
899                         Wild 9: skip PREGAP + starting accurate SubQ
900                         - plays tracks without retry play
901                         */
902                         Find_CurTrack(cdr.SetSectorPlay);
903                         generate_subq(cdr.SetSectorPlay);
904                         cdr.LocL[0] = LOCL_INVALID;
905                         cdr.SubqForwardSectors = 1;
906                         cdr.TrackChanged = FALSE;
907                         cdr.FileChannelSelected = 0;
908                         cdr.AdpcmActive = 0;
909                         cdr.ReportDelay = 60;
910                         cdr.sectorsRead = 0;
911
912                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
913                         
914                         // BIOS player - set flag again
915                         cdr.Play = TRUE;
916                         cdr.DriveState = DRIVESTATE_PLAY_READ;
917                         cdr.PhysCdPropagations = 0;
918
919                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
920                         start_rotating = 1;
921                         break;
922
923                 case CdlForward:
924                         // TODO: error 80 if stopped
925                         IrqStat = Complete;
926
927                         // GameShark CD Player: Calls 2x + Play 2x
928                         cdr.FastForward = 1;
929                         cdr.FastBackward = 0;
930                         break;
931
932                 case CdlBackward:
933                         IrqStat = Complete;
934
935                         // GameShark CD Player: Calls 2x + Play 2x
936                         cdr.FastBackward = 1;
937                         cdr.FastForward = 0;
938                         break;
939
940                 case CdlStandby:
941                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
942                                 error = ERROR_BAD_ARGNUM;
943                                 goto set_error;
944                         }
945                         second_resp_time = cdReadTime * 125 / 2;
946                         start_rotating = 1;
947                         break;
948
949                 case CdlStandby + CMD_PART2:
950                         IrqStat = Complete;
951                         break;
952
953                 case CdlStop:
954                         if (cdr.Play) {
955                                 // grab time for current track
956                                 cdra_getTD(cdr.CurTrack, cdr.ResultTD);
957                                 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
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 (cdra_getTN(cdr.ResultTN) != 0) {
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 (cdra_getTD(cdr.Track, cdr.ResultTD) != 0) {
1111                                 error = ERROR_BAD_ARGVAL;
1112                                 goto set_error;
1113                         }
1114                         SetResultSize_(3);
1115                         cdr.Result[1] = itob(cdr.ResultTD[0]);
1116                         cdr.Result[2] = itob(cdr.ResultTD[1]);
1117                         // no sector number
1118                         //cdr.Result[3] = itob(cdr.ResultTD[2]);
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                         cdra_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 = cdra_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 (cdra_getStatus(&cdr_stat) != 0 || 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.PhysCdPropagations = 0;
1271                         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1272                                         cdr.SetSectorPlay[2]);
1273
1274                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1275                         cycles += seekTime;
1276                         if (Config.hacks.cdr_read_timing)
1277                                 cycles = cdrAlignTimingHack(cycles);
1278                         else if (canDoTurbo())
1279                                 cycles = cdReadTime / 2;
1280                         CDRPLAYREAD_INT(cycles, 1);
1281
1282                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1283                         start_rotating = 1;
1284                         break;
1285
1286                 case CdlSync:
1287                 default:
1288                         error = ERROR_INVALIDCMD;
1289                         // FALLTHROUGH
1290
1291                 set_error:
1292                         SetResultSize_(2);
1293                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1294                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1295                         IrqStat = DiskError;
1296                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1297                         break;
1298         }
1299
1300         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1301                 cdr.DriveState = DRIVESTATE_STANDBY;
1302                 cdr.StatP |= STATUS_ROTATING;
1303         }
1304
1305         if (second_resp_time) {
1306                 cdr.CmdInProgress = Cmd | 0x100;
1307                 set_event(PSXINT_CDR, second_resp_time);
1308         }
1309         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1310                 cdr.CmdInProgress = cdr.Cmd;
1311                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1312         }
1313
1314         setIrq(IrqStat, Cmd);
1315 }
1316
1317 static void cdrPrepCdda(s16 *buf, int samples)
1318 {
1319 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1320         int i;
1321         for (i = 0; i < samples; i++) {
1322                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1323                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1324         }
1325 #endif
1326 }
1327
1328 static void cdrReadInterruptSetResult(unsigned char result)
1329 {
1330         if (cdr.IrqStat) {
1331                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1332                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1333                         cdr.CmdInProgress, cdr.IrqStat);
1334                 cdr.Irq1Pending = result;
1335                 // F1 2000 timing hack :(
1336                 // compensate for some csum func @80014380 taking too long
1337                 if (!cdr.AdpcmActive)
1338                         psxRegs.intCycle[PSXINT_CDREAD].sCycle += cdReadTime / 10;
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 = cdra_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         if (buf[3] != 1 && buf[3] != 2) { // according to duckstation
1436                 deliver_data = 0;
1437                 CDR_LOG_I("%x:%02x:%02x mode %02x ignored\n",
1438                         buf[0], buf[1], buf[2], buf[3]);
1439         }
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         cdra_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 3 - 1 parameter fifo empty
1461         bit 4 - 1 parameter fifo not full
1462         bit 5 - 1 response fifo not empty
1463         bit 6 - 1 data fifo not empty
1464         bit 7 - 1 command being processed
1465 */
1466
1467 unsigned char cdrRead0(void) {
1468         cdr.Ctrl &= ~0x64;
1469         cdr.Ctrl |= cdr.AdpcmActive << 2;
1470         cdr.Ctrl |= cdr.ResultReady << 5;
1471         cdr.Ctrl |= ((signed int)(cdr.FifoOffset - cdr.FifoSize) >> 31) & 0x40;
1472
1473         cdr.Ctrl |= 0x18;
1474
1475         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1476
1477         return psxHu8(0x1800) = cdr.Ctrl;
1478 }
1479
1480 void cdrWrite0(unsigned char rt) {
1481         CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1482
1483         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1484 }
1485
1486 unsigned char cdrRead1(void) {
1487         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1488                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1489         else
1490                 psxHu8(0x1801) = 0;
1491         cdr.ResultP++;
1492         if (cdr.ResultP == cdr.ResultC)
1493                 cdr.ResultReady = 0;
1494
1495         CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1496
1497         return psxHu8(0x1801);
1498 }
1499
1500 void cdrWrite1(unsigned char rt) {
1501         const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1502         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1503
1504         switch (cdr.Ctrl & 3) {
1505         case 0:
1506                 break;
1507         case 3:
1508                 cdr.AttenuatorRightToRightT = rt;
1509                 return;
1510         default:
1511                 return;
1512         }
1513
1514 #ifdef CDR_LOG_CMD_IRQ
1515         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1516         if (cdr.ParamC) {
1517                 int i;
1518                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1519                 for (i = 0; i < cdr.ParamC; i++)
1520                         SysPrintf(" %x,", cdr.Param[i]);
1521                 SysPrintf("}");
1522         }
1523         SysPrintf(" @%08x\n", psxRegs.pc);
1524 #endif
1525
1526         cdr.ResultReady = 0;
1527         cdr.Ctrl |= 0x80;
1528
1529         if (!cdr.CmdInProgress) {
1530                 cdr.CmdInProgress = rt;
1531                 // should be something like 12k + controller delays
1532                 set_event(PSXINT_CDR, 5000);
1533         }
1534         else {
1535                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1536                         rt, cdr.Cmd, cdr.CmdInProgress);
1537                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1538                         cdr.CmdInProgress = rt;
1539         }
1540
1541         cdr.Cmd = rt;
1542 }
1543
1544 unsigned char cdrRead2(void) {
1545         unsigned char ret = cdr.Transfer[0x920];
1546
1547         if (cdr.FifoOffset < cdr.FifoSize)
1548                 ret = cdr.Transfer[cdr.FifoOffset++];
1549         else
1550                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1551
1552         CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1553         return ret;
1554 }
1555
1556 void cdrWrite2(unsigned char rt) {
1557         const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1558         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1559
1560         switch (cdr.Ctrl & 3) {
1561         case 0:
1562                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1563                         cdr.Param[cdr.ParamC++] = rt;
1564                 return;
1565         case 1:
1566                 cdr.IrqMask = rt;
1567                 setIrq(cdr.IrqStat, 0x1005);
1568                 return;
1569         case 2:
1570                 cdr.AttenuatorLeftToLeftT = rt;
1571                 return;
1572         case 3:
1573                 cdr.AttenuatorRightToLeftT = rt;
1574                 return;
1575         }
1576 }
1577
1578 unsigned char cdrRead3(void) {
1579         if (cdr.Ctrl & 0x1)
1580                 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1581         else
1582                 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1583
1584         CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1585                 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1586         return psxHu8(0x1803);
1587 }
1588
1589 void cdrWrite3(unsigned char rt) {
1590         const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1591         u8 ll, lr, rl, rr;
1592         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1593
1594         switch (cdr.Ctrl & 3) {
1595         case 0:
1596                 break; // transfer
1597         case 1:
1598                 if (cdr.IrqStat & rt) {
1599                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1600                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1601                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1602 #ifdef CDR_LOG_CMD_IRQ
1603                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1604                                 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1605                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1606 #endif
1607                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1608                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1609                         {
1610                                 s32 c = 2048;
1611                                 if (cdr.CmdInProgress) {
1612                                         c = 2048 - (psxRegs.cycle - nextCycle);
1613                                         c = MAX_VALUE(c, 512);
1614                                 }
1615                                 set_event(PSXINT_CDR, c);
1616                         }
1617                 }
1618                 cdr.IrqStat &= ~rt;
1619
1620                 if (rt & 0x40)
1621                         cdr.ParamC = 0;
1622                 return;
1623         case 2:
1624                 cdr.AttenuatorLeftToRightT = rt;
1625                 return;
1626         case 3:
1627                 if (rt & 0x01)
1628                         log_unhandled("Mute ADPCM?\n");
1629                 if (rt & 0x20) {
1630                         ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1631                         rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1632                         if (ll == cdr.AttenuatorLeftToLeft &&
1633                             lr == cdr.AttenuatorLeftToRight &&
1634                             rl == cdr.AttenuatorRightToLeft &&
1635                             rr == cdr.AttenuatorRightToRight)
1636                                 return;
1637                         cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1638                         cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1639                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1640                         SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1641                 }
1642                 return;
1643         }
1644
1645         // test: Viewpoint
1646         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1647                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1648         }
1649         else if (rt & 0x80) {
1650                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1651                         case MODE_SIZE_2328:
1652                         case 0x00:
1653                                 cdr.FifoOffset = 12;
1654                                 cdr.FifoSize = 2048 + 12;
1655                                 break;
1656
1657                         case MODE_SIZE_2340:
1658                         default:
1659                                 cdr.FifoOffset = 0;
1660                                 cdr.FifoSize = 2340;
1661                                 break;
1662                 }
1663         }
1664         else if (!(rt & 0xc0))
1665                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1666 }
1667
1668 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1669         u32 cdsize, max_words, cycles;
1670         int size;
1671         u8 *ptr;
1672
1673 #if 0
1674         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1675         if (cdr.FifoOffset == 0) {
1676                 ptr = cdr.Transfer;
1677                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1678         }
1679         SysPrintf("\n");
1680 #endif
1681
1682         switch (chcr & 0x71000000) {
1683                 case 0x11000000:
1684                         madr &= ~3;
1685                         ptr = getDmaRam(madr, &max_words);
1686                         if (ptr == INVALID_PTR) {
1687                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1688                                 break;
1689                         }
1690
1691                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1692
1693                         /*
1694                         GS CDX: Enhancement CD crash
1695                         - Setloc 0:0:0
1696                         - CdlPlay
1697                         - Spams DMA3 and gets buffer overrun
1698                         */
1699                         size = DATA_SIZE - cdr.FifoOffset;
1700                         if (size > cdsize)
1701                                 size = cdsize;
1702                         if (size > max_words * 4)
1703                                 size = max_words * 4;
1704                         if (size > 0)
1705                         {
1706                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1707                                 cdr.FifoOffset += size;
1708                         }
1709                         if (size < cdsize) {
1710                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1711                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1712                         }
1713                         psxCpu->Clear(madr, cdsize / 4);
1714
1715                         cycles = (cdsize / 4) * 24;
1716                         set_event(PSXINT_CDRDMA, cycles);
1717
1718                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1719                         if (chcr & 0x100) {
1720                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1721                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1722                         }
1723                         else {
1724                                 // halted
1725                                 psxRegs.cycle += cycles - 20;
1726                         }
1727                         if (canDoTurbo() && cdr.Reading && cdr.FifoOffset >= 2048)
1728                                 CDRPLAYREAD_INT(cycles + 4096, 1);
1729                         return;
1730
1731                 default:
1732                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1733                         break;
1734         }
1735
1736         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1737         DMA_INTERRUPT(3);
1738 }
1739
1740 void cdrDmaInterrupt(void)
1741 {
1742         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1743         {
1744                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1745                 DMA_INTERRUPT(3);
1746         }
1747 }
1748
1749 static void getCdInfo(void)
1750 {
1751         cdra_getTN(cdr.ResultTN);
1752         cdra_getTD(0, cdr.SetSectorEnd);
1753 }
1754
1755 void cdrReset() {
1756         memset(&cdr, 0, sizeof(cdr));
1757         cdr.CurTrack = 1;
1758         cdr.FilterFile = 0;
1759         cdr.FilterChannel = 0;
1760         cdr.IrqMask = 0x1f;
1761         cdr.IrqStat = NoIntr;
1762
1763         // BIOS player - default values
1764         cdr.AttenuatorLeftToLeft = 0x80;
1765         cdr.AttenuatorLeftToRight = 0x00;
1766         cdr.AttenuatorRightToLeft = 0x00;
1767         cdr.AttenuatorRightToRight = 0x80;
1768
1769         softReset();
1770         getCdInfo();
1771 }
1772
1773 int cdrFreeze(void *f, int Mode) {
1774         u32 tmp;
1775         u8 tmpp[3];
1776
1777         cdr.freeze_ver = 0x63647203;
1778         gzfreeze(&cdr, sizeof(cdr));
1779         
1780         if (Mode == 1) {
1781                 cdr.ParamP = cdr.ParamC;
1782                 tmp = cdr.FifoOffset;
1783         }
1784
1785         gzfreeze(&tmp, sizeof(tmp));
1786
1787         if (Mode == 0) {
1788                 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1789                 getCdInfo();
1790
1791                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1792                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1793                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1794                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1795
1796                 // read right sub data
1797                 memcpy(tmpp, cdr.Prev, sizeof(tmpp));
1798                 if (cdr.freeze_ver < 0x63647203) {
1799                         tmpp[0] = btoi(tmpp[0]);
1800                         tmpp[1] = btoi(tmpp[1]);
1801                         tmpp[2] = btoi(tmpp[2]);
1802                 }
1803                 cdr.Prev[0] = 0xff;
1804                 if (tmpp[0] != 0xff)
1805                         ReadTrack(tmpp);
1806
1807                 if (cdr.Play) {
1808                         if (cdr.freeze_ver < 0x63647202)
1809                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1810
1811                         Find_CurTrack(cdr.SetSectorPlay);
1812                 }
1813                 if (!cdr.Muted)
1814                         ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToRight,
1815                         rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1816                 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1817         }
1818
1819         return 0;
1820 }
1821
1822 void LidInterrupt(void) {
1823         getCdInfo();
1824         cdrLidSeekInterrupt();
1825 }