6e1ff0479bd3e44248f6239b52a7a027b2863cb4
[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
52 //#define CDR_LOG_CMD_ACK
53
54 static struct {
55         // unused members maintain savesate compatibility
56         unsigned char unused0;
57         unsigned char unused1;
58         unsigned char IrqMask;
59         unsigned char unused2;
60         unsigned char Ctrl;
61         unsigned char IrqStat;
62
63         unsigned char StatP;
64
65         unsigned char Transfer[DATA_SIZE];
66         struct {
67                 unsigned char Track;
68                 unsigned char Index;
69                 unsigned char Relative[3];
70                 unsigned char Absolute[3];
71         } subq;
72         unsigned char TrackChanged;
73         unsigned char ReportDelay;
74         unsigned char PhysCdPropagations;
75         unsigned short sectorsRead;
76         unsigned int  freeze_ver;
77
78         unsigned char Prev[4];
79         unsigned char Param[8];
80         unsigned char Result[16];
81
82         unsigned char ParamC;
83         unsigned char ParamP;
84         unsigned char ResultC;
85         unsigned char ResultP;
86         unsigned char ResultReady;
87         unsigned char Cmd;
88         unsigned char SubqForwardSectors;
89         unsigned char SetlocPending;
90         u32 Reading;
91
92         unsigned char ResultTN[6];
93         unsigned char ResultTD[4];
94         unsigned char SetSectorPlay[4];
95         unsigned char SetSectorEnd[4];
96         unsigned char SetSector[4];
97         unsigned char Track;
98         boolean Play, Muted;
99         int CurTrack;
100         unsigned char Mode;
101         unsigned char FileChannelSelected;
102         unsigned char CurFile, CurChannel;
103         int FilterFile, FilterChannel;
104         unsigned char LocL[8];
105         int unused4;
106
107         xa_decode_t Xa;
108
109         u16 FifoOffset;
110         u16 FifoSize;
111
112         u16 CmdInProgress;
113         u8 Irq1Pending;
114         u8 AdpcmActive;
115         u32 LastReadSeekCycles;
116
117         u8 RetryDetected;
118
119         u8 DriveState; // enum drive_state
120         u8 FastForward;
121         u8 FastBackward;
122         u8 errorRetryhack;
123
124         u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
125         u8 AttenuatorRightToRight, AttenuatorRightToLeft;
126         u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
127         u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
128 } cdr;
129 alignas(64) static s16 read_buf[CD_FRAMESIZE_RAW_ALIGNED / 2];
130
131 struct SubQ {
132         char res0[12];
133         unsigned char ControlAndADR;
134         unsigned char TrackNumber;
135         unsigned char IndexNumber;
136         unsigned char TrackRelativeAddress[3];
137         unsigned char Filler;
138         unsigned char AbsoluteAddress[3];
139         unsigned char CRC[2];
140         char res1[72];
141 };
142
143 /* CD-ROM magic numbers */
144 #define CdlSync        0  /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
145 #define CdlNop         1
146 #define CdlSetloc      2
147 #define CdlPlay        3
148 #define CdlForward     4
149 #define CdlBackward    5
150 #define CdlReadN       6
151 #define CdlStandby     7
152 #define CdlStop        8
153 #define CdlPause       9
154 #define CdlReset       10
155 #define CdlMute        11
156 #define CdlDemute      12
157 #define CdlSetfilter   13
158 #define CdlSetmode     14
159 #define CdlGetparam    15
160 #define CdlGetlocL     16
161 #define CdlGetlocP     17
162 #define CdlReadT       18
163 #define CdlGetTN       19
164 #define CdlGetTD       20
165 #define CdlSeekL       21
166 #define CdlSeekP       22
167 #define CdlSetclock    23
168 #define CdlGetclock    24
169 #define CdlTest        25
170 #define CdlID          26
171 #define CdlReadS       27
172 #define CdlInit        28
173 #define CdlGetQ        29
174 #define CdlReadToc     30
175
176 #ifdef CDR_LOG_CMD
177 static const char * const CmdName[0x100] = {
178     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
179     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
180     "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
181     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
182     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
183     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
184     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
185     "CdlInit",     NULL,           "CDlReadToc", NULL
186 };
187 #endif
188
189 unsigned char Test04[] = { 0 };
190 unsigned char Test05[] = { 0 };
191 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
192 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
193 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
194
195 // cdr.IrqStat:
196 #define NoIntr          0
197 #define DataReady       1
198 #define Complete        2
199 #define Acknowledge     3
200 #define DataEnd         4
201 #define DiskError       5
202
203 /* Modes flags */
204 #define MODE_SPEED       (1<<7) // 0x80
205 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
206 #define MODE_SIZE_2340   (1<<5) // 0x20
207 #define MODE_SIZE_2328   (1<<4) // 0x10 (likely wrong)
208 #define MODE_SIZE_2048   (0<<4) // 0x00
209 #define MODE_BIT4        (1<<4) // 0x10
210 #define MODE_SF          (1<<3) // 0x08 channel on/off
211 #define MODE_REPORT      (1<<2) // 0x04
212 #define MODE_AUTOPAUSE   (1<<1) // 0x02
213 #define MODE_CDDA        (1<<0) // 0x01
214
215 /* Status flags */
216 #define STATUS_PLAY      (1<<7) // 0x80
217 #define STATUS_SEEK      (1<<6) // 0x40
218 #define STATUS_READ      (1<<5) // 0x20
219 #define STATUS_SHELLOPEN (1<<4) // 0x10
220 #define STATUS_UNKNOWN3  (1<<3) // 0x08
221 #define STATUS_SEEKERROR (1<<2) // 0x04
222 #define STATUS_ROTATING  (1<<1) // 0x02
223 #define STATUS_ERROR     (1<<0) // 0x01
224
225 /* Errors */
226 #define ERROR_NOTREADY   (1<<7) // 0x80
227 #define ERROR_INVALIDCMD (1<<6) // 0x40
228 #define ERROR_BAD_ARGNUM (1<<5) // 0x20
229 #define ERROR_BAD_ARGVAL (1<<4) // 0x10
230 #define ERROR_SHELLOPEN  (1<<3) // 0x08
231
232 // 1x = 75 sectors per second
233 // PSXCLK = 1 sec in the ps
234 // so (PSXCLK / 75) = cdr read time (linuzappz)
235 #define cdReadTime (PSXCLK / 75)
236
237 #define LOCL_INVALID 0xff
238 #define SUBQ_FORWARD_SECTORS 2u
239
240 enum drive_state {
241         DRIVESTATE_STANDBY = 0, // different from paused
242         DRIVESTATE_LID_OPEN,
243         DRIVESTATE_RESCAN_CD,
244         DRIVESTATE_PREPARE_CD,
245         DRIVESTATE_STOPPED,
246         DRIVESTATE_PAUSED,
247         DRIVESTATE_PLAY_READ,
248         DRIVESTATE_SEEK,
249 };
250
251 static struct CdrStat cdr_stat;
252
253 static unsigned int msf2sec(const u8 *msf) {
254         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
255 }
256
257 // cdrPlayReadInterrupt
258 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
259         u32 e_ = eCycle; \
260         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
261         if (isFirst) \
262                 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
263         else \
264                 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
265         psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
266         set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
267 }
268
269 #define StopReading() { \
270         cdr.Reading = 0; \
271         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
272 }
273
274 #define StopCdda() { \
275         cdr.Play = FALSE; \
276         cdr.FastForward = 0; \
277         cdr.FastBackward = 0; \
278 }
279
280 #define SetPlaySeekRead(x, f) { \
281         x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
282         x |= f; \
283 }
284
285 #define SetResultSize_(size) { \
286         cdr.ResultP = 0; \
287         cdr.ResultC = size; \
288         cdr.ResultReady = 1; \
289 }
290
291 #define SetResultSize(size) { \
292         if (cdr.ResultP < cdr.ResultC) \
293                 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
294         SetResultSize_(size); \
295 }
296
297 static void setIrq(u8 irq, int log_cmd)
298 {
299         u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
300         u8 new_ = irq & cdr.IrqMask ? 1 : 0;
301
302         cdr.IrqStat = irq;
303         if ((old ^ new_) & new_)
304                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
305
306 #ifdef CDR_LOG_CMD
307         if (cdr.IrqStat)
308         {
309                 int i;
310                 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
311                         !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
312                 for (i = 0; i < cdr.ResultC; i++)
313                         SysPrintf("%02x ", cdr.Result[i]);
314                 SysPrintf("\n");
315         }
316 #endif
317 }
318
319 // timing used in this function was taken from tests on real hardware
320 // (yes it's slow, but you probably don't want to modify it)
321 void cdrLidSeekInterrupt(void)
322 {
323         CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
324
325         switch (cdr.DriveState) {
326         default:
327         case DRIVESTATE_STANDBY:
328                 StopCdda();
329                 //StopReading();
330                 SetPlaySeekRead(cdr.StatP, 0);
331
332                 if (cdra_getStatus(&cdr_stat) == -1)
333                         return;
334
335                 if (cdr_stat.Status & STATUS_SHELLOPEN)
336                 {
337                         memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
338                         cdr.DriveState = DRIVESTATE_LID_OPEN;
339                         set_event(PSXINT_CDRLID, 0x800);
340                 }
341                 break;
342
343         case DRIVESTATE_LID_OPEN:
344                 if (cdra_getStatus(&cdr_stat) != 0)
345                         cdr_stat.Status &= ~STATUS_SHELLOPEN;
346
347                 // 02, 12, 10
348                 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
349                         int was_reading = cdr.Reading;
350                         StopReading();
351                         SetPlaySeekRead(cdr.StatP, 0);
352                         cdr.StatP |= STATUS_SHELLOPEN;
353                         memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
354
355                         // IIRC this sometimes doesn't happen on real hw
356                         // (when lots of commands are sent?)
357                         SetResultSize(2);
358                         cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
359                         cdr.Result[1] = ERROR_SHELLOPEN;
360                         if (cdr.CmdInProgress || was_reading) {
361                                 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
362                                 cdr.CmdInProgress = 0;
363                                 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
364                                 cdr.Result[1] = ERROR_NOTREADY;
365                         }
366                         setIrq(DiskError, 0x1006);
367
368                         set_event(PSXINT_CDRLID, cdReadTime * 30);
369                         break;
370                 }
371                 else if (cdr.StatP & STATUS_ROTATING) {
372                         cdr.StatP &= ~STATUS_ROTATING;
373                 }
374                 else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) {
375                         // closed now
376                         CheckCdrom();
377
378                         // cdr.StatP STATUS_SHELLOPEN is "sticky"
379                         // and is only cleared by CdlNop
380
381                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
382                         set_event(PSXINT_CDRLID, cdReadTime * 105);
383                         break;
384                 }
385
386                 // recheck for close
387                 set_event(PSXINT_CDRLID, cdReadTime * 3);
388                 break;
389
390         case DRIVESTATE_RESCAN_CD:
391                 cdr.StatP |= STATUS_ROTATING;
392                 cdr.DriveState = DRIVESTATE_PREPARE_CD;
393
394                 // this is very long on real hardware, over 6 seconds
395                 // make it a bit faster here...
396                 set_event(PSXINT_CDRLID, cdReadTime * 150);
397                 break;
398
399         case DRIVESTATE_PREPARE_CD:
400                 if (cdr.StatP & STATUS_SEEK) {
401                         SetPlaySeekRead(cdr.StatP, 0);
402                         cdr.DriveState = DRIVESTATE_STANDBY;
403                 }
404                 else {
405                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
406                         set_event(PSXINT_CDRLID, cdReadTime * 26);
407                 }
408                 break;
409         }
410 }
411
412 static void Find_CurTrack(const u8 *time)
413 {
414         int current, sect;
415
416         current = msf2sec(time);
417
418         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
419                 cdra_getTD(cdr.CurTrack + 1, cdr.ResultTD);
420                 sect = msf2sec(cdr.ResultTD);
421                 if (sect - current >= 150)
422                         break;
423         }
424 }
425
426 static void generate_subq(const u8 *time)
427 {
428         unsigned char start[3], next[3];
429         int this_s, start_s, next_s, pregap;
430         int relative_s;
431
432         if (cdr.CurTrack <= cdr.ResultTN[1])
433                 cdra_getTD(cdr.CurTrack, start);
434         else
435                 memcpy(start, cdr.SetSectorEnd, 3);
436         if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
437                 pregap = 150;
438                 cdra_getTD(cdr.CurTrack + 1, next);
439         }
440         else {
441                 // last track - cd size
442                 pregap = 0;
443                 memcpy(next, cdr.SetSectorEnd, 3);
444         }
445
446         this_s = msf2sec(time);
447         start_s = msf2sec(start);
448         next_s = msf2sec(next);
449
450         cdr.TrackChanged = FALSE;
451
452         if (next_s - this_s < pregap && cdr.CurTrack <= cdr.ResultTN[1]) {
453                 cdr.TrackChanged = TRUE;
454                 cdr.CurTrack++;
455                 start_s = next_s;
456         }
457
458         cdr.subq.Index = 1;
459
460         relative_s = this_s - start_s;
461         if (relative_s < 0) {
462                 cdr.subq.Index = 0;
463                 relative_s = -relative_s;
464         }
465         lba2msf(relative_s, &cdr.subq.Relative[0],
466                 &cdr.subq.Relative[1], &cdr.subq.Relative[2]);
467
468         cdr.subq.Track = itob(cdr.CurTrack);
469         if (cdr.CurTrack > cdr.ResultTN[1]) // lead-out
470                 cdr.subq.Track = 0xaa;
471         cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
472         cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
473         cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
474         cdr.subq.Absolute[0] = itob(time[0]);
475         cdr.subq.Absolute[1] = itob(time[1]);
476         cdr.subq.Absolute[2] = itob(time[2]);
477 }
478
479 static int ReadTrack(const u8 *time)
480 {
481         int ret;
482
483         CDR_LOG("ReadTrack *** %02d:%02d:%02d\n", time[0], time[1], time[2]);
484
485         if (memcmp(cdr.Prev, time, 3) == 0)
486                 return 1;
487
488         ret = cdra_readTrack(time);
489         if (ret == 0)
490                 memcpy(cdr.Prev, time, 3);
491         else
492                 log_unhandled("ReadTrack %02d:%02d:%02d ret %d\n",
493                         time[0], time[1], time[2], ret);
494         return ret == 0;
495 }
496
497 static void UpdateSubq(const u8 *time)
498 {
499         int ret = -1, s = MSF2SECT(time[0], time[1], time[2]);
500         struct SubQ subq;
501         u16 crc;
502
503         if (CheckSBI(s))
504                 return;
505
506         if (cdr.CurTrack == 1)
507                 ret = cdra_readSub(time, &subq);
508         if (ret == 0) {
509                 crc = calcCrc((u8 *)&subq + 12, 10);
510                 if (crc == (((u16)subq.CRC[0] << 8) | subq.CRC[1])) {
511                         cdr.subq.Track = subq.TrackNumber;
512                         cdr.subq.Index = subq.IndexNumber;
513                         memcpy(cdr.subq.Relative, subq.TrackRelativeAddress, 3);
514                         memcpy(cdr.subq.Absolute, subq.AbsoluteAddress, 3);
515                 }
516                 else {
517                         CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
518                                 time[0], time[1], time[2]);
519                 }
520         }
521         else {
522                 generate_subq(time);
523         }
524
525         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
526                 cdr.subq.Track, cdr.subq.Index,
527                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
528                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
529 }
530
531 static void cdrPlayInterrupt_Autopause()
532 {
533         u32 abs_lev_max = 0;
534         boolean abs_lev_chselect;
535         u32 i;
536
537         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
538                 CDR_LOG_I("autopause\n");
539
540                 SetResultSize(1);
541                 cdr.Result[0] = cdr.StatP;
542                 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
543
544                 StopCdda();
545                 SetPlaySeekRead(cdr.StatP, 0);
546                 cdr.DriveState = DRIVESTATE_PAUSED;
547         }
548         else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
549                  ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
550         {
551                 SetResultSize(8);
552                 cdr.Result[0] = cdr.StatP;
553                 cdr.Result[1] = cdr.subq.Track;
554                 cdr.Result[2] = cdr.subq.Index;
555                 
556                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
557                 
558                 /* 8 is a hack. For accuracy, it should be 588. */
559                 for (i = 0; i < 8; i++)
560                 {
561                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
562                 }
563                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
564                 abs_lev_max |= abs_lev_chselect << 15;
565
566                 if (cdr.subq.Absolute[2] & 0x10) {
567                         cdr.Result[3] = cdr.subq.Relative[0];
568                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
569                         cdr.Result[5] = cdr.subq.Relative[2];
570                 }
571                 else {
572                         cdr.Result[3] = cdr.subq.Absolute[0];
573                         cdr.Result[4] = cdr.subq.Absolute[1];
574                         cdr.Result[5] = cdr.subq.Absolute[2];
575                 }
576                 cdr.Result[6] = abs_lev_max >> 0;
577                 cdr.Result[7] = abs_lev_max >> 8;
578
579                 setIrq(DataReady, 0x1001);
580         }
581
582         if (cdr.ReportDelay)
583                 cdr.ReportDelay--;
584 }
585
586 static boolean canDoTurbo(void)
587 {
588         u32 c = psxRegs.cycle;
589         return Config.TurboCD && !cdr.RetryDetected && !cdr.AdpcmActive
590                 //&& c - psxRegs.intCycle[PSXINT_SPUDMA].sCycle > (u32)cdReadTime * 2
591                 && c - psxRegs.intCycle[PSXINT_MDECOUTDMA].sCycle > (u32)cdReadTime * 16;
592 }
593
594 static int cdrSeekTime(unsigned char *target)
595 {
596         int diff = abs((int)msf2sec(cdr.SetSectorPlay) - (int)msf2sec(target));
597         int seekTime = diff * (cdReadTime / 2000);
598         int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
599         seekTime = MAX_VALUE(seekTime, 20000);
600
601         // sled seek?
602         if (diff >= 7200)
603                 seekTime = PSXCLK / 7 + diff * 64;
604         // add *something* as rotation time until the target sector
605         if (cyclesSinceRS >= cdReadTime)
606                 seekTime += (8 - ((cyclesSinceRS >> 18) & 7)) * (cdReadTime / 2);
607
608         // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
609         // and then wants some slack time
610         if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
611                 seekTime += cdReadTime;
612
613         //seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
614         CDR_LOG("seek: %02d:%02d:%02d %.2f %.2f (%.2f) st %d di %d\n",
615                 target[0], target[1], target[2], (float)seekTime / PSXCLK,
616                 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
617                 cdr.DriveState, diff);
618         return seekTime;
619 }
620
621 static u32 cdrAlignTimingHack(u32 cycles)
622 {
623         /*
624          * timing hack for T'ai Fu - Wrath of the Tiger:
625          * The game has a bug where it issues some cdc commands from a low priority
626          * vint handler, however there is a higher priority default bios handler
627          * that acks the vint irq and returns, so game's handler is not reached
628          * (see bios irq handler chains at e004 and the game's irq handling func
629          * at 80036810). For the game to work, vint has to arrive after the bios
630          * vint handler rejects some other irq (of which only cd and rcnt2 are
631          * active), but before the game's handler loop reads I_STAT. The time
632          * window for this is quite small (~1k cycles of so). Apparently this
633          * somehow happens naturally on the real hardware.
634          *
635          * Note: always enforcing this breaks other games like Crash PAL version
636          * (inputs get dropped because bios handler doesn't see interrupts).
637          */
638         u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
639         while ((s32)(vint_rel - cycles) < 0)
640                 vint_rel += PSXCLK / 60;
641         return vint_rel;
642 }
643
644 static void cdrUpdateTransferBuf(const u8 *buf);
645 static void cdrReadInterrupt(void);
646 static void cdrPrepCdda(s16 *buf, int samples);
647
648 static void msfiAdd(u8 *msfi, u32 count)
649 {
650         assert(count < 75);
651         msfi[2] += count;
652         if (msfi[2] >= 75) {
653                 msfi[2] -= 75;
654                 msfi[1]++;
655                 if (msfi[1] == 60) {
656                         msfi[1] = 0;
657                         msfi[0]++;
658                 }
659         }
660 }
661
662 static void msfiSub(u8 *msfi, u32 count)
663 {
664         assert(count < 75);
665         msfi[2] -= count;
666         if ((s8)msfi[2] < 0) {
667                 msfi[2] += 75;
668                 msfi[1]--;
669                 if ((s8)msfi[1] < 0) {
670                         msfi[1] = 60;
671                         msfi[0]--;
672                 }
673         }
674 }
675
676 static int msfiEq(const u8 *a, const u8 *b)
677 {
678         return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
679 }
680
681 void cdrPlayReadInterrupt(void)
682 {
683         // this works but causes instability for timing sensitive games
684 #if 0
685         int hit = cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
686         if (!hit && cdr.PhysCdPropagations < 75/2) {
687                 // this propagates the real cdrom delays to the emulated game
688                 CDRPLAYREAD_INT(cdReadTime / 2, 0);
689                 cdr.PhysCdPropagations++;
690                 return;
691         }
692 #endif
693         cdr.LastReadSeekCycles = psxRegs.cycle;
694
695         if (cdr.Reading) {
696                 cdrReadInterrupt();
697                 return;
698         }
699
700         if (!cdr.Play) return;
701
702         CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
703                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
704
705         cdr.DriveState = DRIVESTATE_PLAY_READ;
706         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
707         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
708                 CDR_LOG_I("end stop\n");
709                 StopCdda();
710                 SetPlaySeekRead(cdr.StatP, 0);
711                 cdr.TrackChanged = TRUE;
712                 cdr.DriveState = DRIVESTATE_PAUSED;
713         }
714         else {
715                 cdra_readCDDA(cdr.SetSectorPlay, read_buf);
716         }
717
718         if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
719                 cdrPlayInterrupt_Autopause();
720
721         if (cdr.Play && !Config.Cdda) {
722                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
723                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
724         }
725
726         msfiAdd(cdr.SetSectorPlay, 1);
727         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 1);
728
729         // update for CdlGetlocP/autopause
730         generate_subq(cdr.SetSectorPlay);
731
732         CDRPLAYREAD_INT(cdReadTime, 0);
733
734         // stop propagation since it breaks streaming
735         cdr.PhysCdPropagations = 0xff;
736 }
737
738 static void softReset(void)
739 {
740         cdra_getStatus(&cdr_stat);
741         if (cdr_stat.Status & STATUS_SHELLOPEN) {
742                 cdr.DriveState = DRIVESTATE_LID_OPEN;
743                 cdr.StatP = STATUS_SHELLOPEN;
744         }
745         else if (CdromId[0] == '\0') {
746                 cdr.DriveState = DRIVESTATE_STOPPED;
747                 cdr.StatP = 0;
748         }
749         else {
750                 cdr.DriveState = DRIVESTATE_STANDBY;
751                 cdr.StatP = STATUS_ROTATING;
752         }
753
754         cdr.FifoOffset = DATA_SIZE; // fifo empty
755         cdr.LocL[0] = LOCL_INVALID;
756         cdr.Mode = MODE_SIZE_2340;
757         cdr.Muted = FALSE;
758         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
759                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
760 }
761
762 #define CMD_PART2           0x100
763 #define CMD_WHILE_NOT_READY 0x200
764
765 void cdrInterrupt(void) {
766         int start_rotating = 0;
767         int error = 0;
768         u32 cycles, seekTime = 0;
769         u32 second_resp_time = 0;
770         const void *buf;
771         u8 ParamC;
772         int read_ok;
773         u16 not_ready = 0;
774         u8 IrqStat = Acknowledge;
775         u8 DriveStateOld;
776         u16 Cmd;
777         int i;
778
779         if (cdr.IrqStat) {
780                 CDR_LOG_I("cmd %02x with irqstat %x\n",
781                         cdr.CmdInProgress, cdr.IrqStat);
782                 return;
783         }
784         if (cdr.Irq1Pending) {
785                 // hand out the "newest" sector, according to nocash
786                 cdrUpdateTransferBuf(cdra_getBuffer());
787                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
788                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
789                         cdr.CmdInProgress, cdr.Irq1Pending);
790                 SetResultSize(1);
791                 cdr.Result[0] = cdr.Irq1Pending;
792                 cdr.Irq1Pending = 0;
793                 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
794                 return;
795         }
796
797         // default response
798         SetResultSize(1);
799         cdr.Result[0] = cdr.StatP;
800
801         Cmd = cdr.CmdInProgress;
802         cdr.CmdInProgress = 0;
803         ParamC = cdr.ParamC;
804
805         if (Cmd < 0x100) {
806                 cdr.Ctrl &= ~0x80;
807                 cdr.ParamC = 0;
808                 cdr.Cmd = 0;
809         }
810
811         switch (cdr.DriveState) {
812         case DRIVESTATE_PREPARE_CD:
813                 if (Cmd > 2) {
814                         // Syphon filter 2 expects commands to work shortly after it sees
815                         // STATUS_ROTATING, so give up trying to emulate the startup seq
816                         cdr.DriveState = DRIVESTATE_STANDBY;
817                         cdr.StatP &= ~STATUS_SEEK;
818                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
819                         break;
820                 }
821                 // fallthrough
822         case DRIVESTATE_LID_OPEN:
823         case DRIVESTATE_RESCAN_CD:
824                 // no disk or busy with the initial scan, allowed cmds are limited
825                 not_ready = CMD_WHILE_NOT_READY;
826                 break;
827         }
828
829         switch (Cmd | not_ready) {
830                 case CdlNop:
831                 case CdlNop + CMD_WHILE_NOT_READY:
832                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
833                                 cdr.StatP &= ~STATUS_SHELLOPEN;
834                         break;
835
836                 case CdlSetloc:
837                 case CdlSetloc + CMD_WHILE_NOT_READY: // apparently?
838                         if (cdr.StatP & STATUS_SHELLOPEN)
839                                 // wrong? Driver2 vs Amerzone
840                                 goto set_error;
841
842                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
843                         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))
844                         {
845                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
846                                 if (++cdr.errorRetryhack > 100)
847                                         break;
848                                 error = ERROR_BAD_ARGNUM;
849                                 goto set_error;
850                         }
851                         else
852                         {
853                                 u8 set_loc[3];
854                                 for (i = 0; i < 3; i++)
855                                         set_loc[i] = btoi(cdr.Param[i]);
856                                 if ((msfiEq(cdr.SetSector, set_loc)) //|| msfiEq(cdr.Param, cdr.Transfer))
857                                                 && !cdr.SetlocPending)
858                                         cdr.RetryDetected++;
859                                 else
860                                         cdr.RetryDetected = 0;
861                                 memcpy(cdr.SetSector, set_loc, 3);
862                                 cdr.SetSector[3] = 0;
863                                 cdr.SetlocPending = 1;
864                                 cdr.errorRetryhack = 0;
865                         }
866                         break;
867
868                 case CdlPlay:
869                         StopCdda();
870                         StopReading();
871
872                         cdr.FastBackward = 0;
873                         cdr.FastForward = 0;
874
875                         // BIOS CD Player
876                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
877
878                         if (ParamC != 0 && cdr.Param[0] != 0) {
879                                 int track = btoi( cdr.Param[0] );
880
881                                 if (track <= cdr.ResultTN[1])
882                                         cdr.CurTrack = track;
883
884                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
885
886                                 if (cdra_getTD(cdr.CurTrack, cdr.ResultTD) != -1) {
887                                         seekTime = cdrSeekTime(cdr.ResultTD);
888                                         memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
889                                 }
890                         }
891                         else if (cdr.SetlocPending) {
892                                 seekTime = cdrSeekTime(cdr.SetSector);
893                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
894                         }
895                         else {
896                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
897                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
898                         }
899                         cdr.SetlocPending = 0;
900
901                         /*
902                         Rayman: detect track changes
903                         - fixes logo freeze
904
905                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
906                         - plays tracks without retry play
907
908                         Wild 9: skip PREGAP + starting accurate SubQ
909                         - plays tracks without retry play
910                         */
911                         Find_CurTrack(cdr.SetSectorPlay);
912                         generate_subq(cdr.SetSectorPlay);
913                         cdr.LocL[0] = LOCL_INVALID;
914                         cdr.SubqForwardSectors = 1;
915                         cdr.TrackChanged = FALSE;
916                         cdr.FileChannelSelected = 0;
917                         cdr.AdpcmActive = 0;
918                         cdr.ReportDelay = 60;
919                         cdr.sectorsRead = 0;
920
921                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
922                         
923                         // BIOS player - set flag again
924                         cdr.Play = TRUE;
925                         cdr.DriveState = DRIVESTATE_PLAY_READ;
926                         cdr.PhysCdPropagations = 0;
927
928                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
929                         start_rotating = 1;
930                         break;
931
932                 case CdlForward:
933                         // TODO: error 80 if stopped
934                         IrqStat = Complete;
935
936                         // GameShark CD Player: Calls 2x + Play 2x
937                         cdr.FastForward = 1;
938                         cdr.FastBackward = 0;
939                         break;
940
941                 case CdlBackward:
942                         IrqStat = Complete;
943
944                         // GameShark CD Player: Calls 2x + Play 2x
945                         cdr.FastBackward = 1;
946                         cdr.FastForward = 0;
947                         break;
948
949                 case CdlStandby:
950                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
951                                 error = ERROR_BAD_ARGNUM;
952                                 goto set_error;
953                         }
954                         second_resp_time = cdReadTime * 125 / 2;
955                         start_rotating = 1;
956                         break;
957
958                 case CdlStandby + CMD_PART2:
959                         IrqStat = Complete;
960                         break;
961
962                 case CdlStop:
963                         if (cdr.Play) {
964                                 // grab time for current track
965                                 cdra_getTD(cdr.CurTrack, cdr.ResultTD);
966                                 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
967                         }
968
969                         StopCdda();
970                         StopReading();
971                         SetPlaySeekRead(cdr.StatP, 0);
972                         cdr.StatP &= ~STATUS_ROTATING;
973                         cdr.LocL[0] = LOCL_INVALID;
974
975                         second_resp_time = 0x800;
976                         if (cdr.DriveState != DRIVESTATE_STOPPED)
977                                 second_resp_time = cdReadTime * 30 / 2;
978
979                         cdr.DriveState = DRIVESTATE_STOPPED;
980                         break;
981
982                 case CdlStop + CMD_PART2:
983                         IrqStat = Complete;
984                         break;
985
986                 case CdlPause:
987                         if (cdr.AdpcmActive) {
988                                 cdr.AdpcmActive = 0;
989                                 cdr.Xa.nsamples = 0;
990                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
991                         }
992                         StopCdda();
993                         StopReading();
994
995                         // how the drive maintains the position while paused is quite
996                         // complicated, this is the minimum to make "Bedlam" happy
997                         msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
998                         cdr.sectorsRead = 0;
999
1000                         /*
1001                         Gundam Battle Assault 2
1002                         Hokuto no Ken 2
1003                         InuYasha - Feudal Fairy Tale
1004                         Dance Dance Revolution Konamix
1005                         Digimon Rumble Arena
1006                         ...
1007                         */
1008                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
1009                         {
1010                                 second_resp_time = 7000;
1011                         }
1012                         else
1013                         {
1014                                 second_resp_time = 2100011;
1015                                 // a hack to try to avoid weird cmd vs irq1 races causing games to retry
1016                                 second_resp_time += (cdr.RetryDetected & 15) * 100001;
1017                         }
1018                         SetPlaySeekRead(cdr.StatP, 0);
1019                         DriveStateOld = cdr.DriveState;
1020                         cdr.DriveState = DRIVESTATE_PAUSED;
1021                         if (DriveStateOld == DRIVESTATE_SEEK) {
1022                                 // According to Duckstation this fails, but the
1023                                 // exact conditions and effects are not clear.
1024                                 // Moto Racer World Tour seems to rely on this.
1025                                 // For now assume pause works anyway, just errors out.
1026                                 error = ERROR_NOTREADY;
1027                                 goto set_error;
1028                         }
1029                         break;
1030
1031                 case CdlPause + CMD_PART2:
1032                         IrqStat = Complete;
1033                         break;
1034
1035                 case CdlReset:
1036                 case CdlReset + CMD_WHILE_NOT_READY:
1037                         // note: nocash and Duckstation calls this 'Init', but
1038                         // the official SDK calls it 'Reset', and so do we
1039                         StopCdda();
1040                         StopReading();
1041                         softReset();
1042                         second_resp_time = not_ready ? 70000 : 4100000;
1043                         start_rotating = 1;
1044                         break;
1045
1046                 case CdlReset + CMD_PART2:
1047                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1048                         IrqStat = Complete;
1049                         break;
1050
1051                 case CdlMute:
1052                         cdr.Muted = TRUE;
1053                         SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1054                         break;
1055
1056                 case CdlDemute:
1057                         cdr.Muted = FALSE;
1058                         SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1059                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1060                         break;
1061
1062                 case CdlSetfilter:
1063                         cdr.FilterFile = cdr.Param[0];
1064                         cdr.FilterChannel = cdr.Param[1];
1065                         cdr.FileChannelSelected = 0;
1066                         break;
1067
1068                 case CdlSetmode:
1069                 case CdlSetmode + CMD_WHILE_NOT_READY:
1070                         if ((cdr.Mode ^ cdr.Param[0]) & MODE_BIT4)
1071                                 log_unhandled("cdrom: mode4 changed: %02x\n", cdr.Param[0]);
1072                         cdr.Mode = cdr.Param[0];
1073                         break;
1074
1075                 case CdlGetparam:
1076                 case CdlGetparam + CMD_WHILE_NOT_READY:
1077                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1078                         SetResultSize_(5);
1079                         cdr.Result[1] = cdr.Mode;
1080                         cdr.Result[2] = 0;
1081                         cdr.Result[3] = cdr.FilterFile;
1082                         cdr.Result[4] = cdr.FilterChannel;
1083                         break;
1084
1085                 case CdlGetlocL:
1086                         if (cdr.LocL[0] == LOCL_INVALID) {
1087                                 error = 0x80;
1088                                 goto set_error;
1089                         }
1090                         SetResultSize_(8);
1091                         memcpy(cdr.Result, cdr.LocL, 8);
1092                         break;
1093
1094                 case CdlGetlocP:
1095                         SetResultSize_(8);
1096                         memcpy(&cdr.Result, &cdr.subq, 8);
1097                         break;
1098
1099                 case CdlReadT: // SetSession?
1100                         // really long
1101                         second_resp_time = cdReadTime * 290 / 4;
1102                         start_rotating = 1;
1103                         break;
1104
1105                 case CdlReadT + CMD_PART2:
1106                         IrqStat = Complete;
1107                         break;
1108
1109                 case CdlGetTN:
1110                         if (cdra_getTN(cdr.ResultTN) != 0) {
1111                                 assert(0);
1112                         }
1113                         SetResultSize_(3);
1114                         cdr.Result[1] = itob(cdr.ResultTN[0]);
1115                         cdr.Result[2] = itob(cdr.ResultTN[1]);
1116                         break;
1117
1118                 case CdlGetTD:
1119                         cdr.Track = btoi(cdr.Param[0]);
1120                         if (cdra_getTD(cdr.Track, cdr.ResultTD) != 0) {
1121                                 error = ERROR_BAD_ARGVAL;
1122                                 goto set_error;
1123                         }
1124                         SetResultSize_(3);
1125                         cdr.Result[1] = itob(cdr.ResultTD[0]);
1126                         cdr.Result[2] = itob(cdr.ResultTD[1]);
1127                         // no sector number
1128                         //cdr.Result[3] = itob(cdr.ResultTD[2]);
1129                         break;
1130
1131                 case CdlSeekL:
1132                 case CdlSeekP:
1133                         StopCdda();
1134                         StopReading();
1135                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1136
1137                         if (!canDoTurbo())
1138                                 seekTime = cdrSeekTime(cdr.SetSector);
1139                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1140                         cdr.DriveState = DRIVESTATE_SEEK;
1141                         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1142                                         cdr.SetSectorPlay[2], 0);
1143                         /*
1144                         Crusaders of Might and Magic = 0.5x-4x
1145                         - fix cutscene speech start
1146
1147                         Eggs of Steel = 2x-?
1148                         - fix new game
1149
1150                         Medievil = ?-4x
1151                         - fix cutscene speech
1152
1153                         Rockman X5 = 0.5-4x
1154                         - fix capcom logo
1155                         */
1156                         second_resp_time = cdReadTime + seekTime;
1157                         start_rotating = 1;
1158                         break;
1159
1160                 case CdlSeekL + CMD_PART2:
1161                 case CdlSeekP + CMD_PART2:
1162                         SetPlaySeekRead(cdr.StatP, 0);
1163                         cdr.Result[0] = cdr.StatP;
1164                         IrqStat = Complete;
1165
1166                         Find_CurTrack(cdr.SetSectorPlay);
1167                         read_ok = ReadTrack(cdr.SetSectorPlay);
1168                         if (read_ok && (buf = cdra_getBuffer()))
1169                                 memcpy(cdr.LocL, buf, 8);
1170                         UpdateSubq(cdr.SetSectorPlay);
1171                         cdr.DriveState = DRIVESTATE_STANDBY;
1172                         cdr.TrackChanged = FALSE;
1173                         cdr.LastReadSeekCycles = psxRegs.cycle;
1174                         break;
1175
1176                 case CdlTest:
1177                 case CdlTest + CMD_WHILE_NOT_READY:
1178                         switch (cdr.Param[0]) {
1179                                 case 0x20: // System Controller ROM Version
1180                                         SetResultSize_(4);
1181                                         memcpy(cdr.Result, Test20, 4);
1182                                         break;
1183                                 case 0x22:
1184                                         SetResultSize_(8);
1185                                         memcpy(cdr.Result, Test22, 4);
1186                                         break;
1187                                 case 0x23: case 0x24:
1188                                         SetResultSize_(8);
1189                                         memcpy(cdr.Result, Test23, 4);
1190                                         break;
1191                         }
1192                         break;
1193
1194                 case CdlID:
1195                         second_resp_time = 20480;
1196                         break;
1197
1198                 case CdlID + CMD_PART2:
1199                         SetResultSize_(8);
1200                         cdr.Result[0] = cdr.StatP;
1201                         cdr.Result[1] = 0;
1202                         cdr.Result[2] = 0;
1203                         cdr.Result[3] = 0;
1204
1205                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1206                         if (cdra_getStatus(&cdr_stat) != 0 ||
1207                             cdr_stat.Type == CDRT_UNKNOWN || cdr_stat.Type == 0xff) {
1208                                 cdr.Result[1] = 0xc0;
1209                         }
1210                         else {
1211                                 if (cdr_stat.Type == CDRT_CDDA)
1212                                         cdr.Result[1] |= 0x10;
1213                                 if (CdromId[0] == '\0')
1214                                         cdr.Result[1] |= 0x80;
1215                         }
1216                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1217                         CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1218                                 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1219
1220                         /* 4-char string in Playstation bios boot screen */
1221                         if (Config.SlowBoot == 1)
1222                                 memcpy(&cdr.Result[4], "PCSX", 4);
1223                         else {
1224                                 cdr.Result[4] = 'S';
1225                                 cdr.Result[5] = 'C';
1226                                 cdr.Result[6] = 'E';
1227                                 if (Config.PsxType == PSX_TYPE_PAL)
1228                                         cdr.Result[7] = 'E';
1229                                 else if (CdromId[2] == 'P' || CdromId[2] == 'p')
1230                                         cdr.Result[7] = 'I';
1231                                 else
1232                                         cdr.Result[7] = 'A';
1233                         }
1234                         IrqStat = Complete;
1235                         break;
1236
1237                 case CdlInit:
1238                 case CdlInit + CMD_WHILE_NOT_READY:
1239                         StopCdda();
1240                         StopReading();
1241                         SetPlaySeekRead(cdr.StatP, 0);
1242                         // yes, it really sets STATUS_SHELLOPEN
1243                         cdr.StatP |= STATUS_SHELLOPEN;
1244                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1245                         set_event(PSXINT_CDRLID, 20480);
1246                         start_rotating = 1;
1247                         break;
1248
1249                 case CdlGetQ:
1250                 case CdlGetQ + CMD_WHILE_NOT_READY:
1251                         break;
1252
1253                 case CdlReadToc:
1254                 case CdlReadToc + CMD_WHILE_NOT_READY:
1255                         cdr.LocL[0] = LOCL_INVALID;
1256                         second_resp_time = cdReadTime * 180 / 4;
1257                         if (!Config.HLE && Config.SlowBoot) {
1258                                 // hack: compensate cdrom being emulated too fast
1259                                 // and bios finishing before the reverb decays
1260                                 second_resp_time += cdReadTime * 75*2;
1261                                 if ((psxRegs.pc >> 28) == 0x0b)
1262                                         second_resp_time += cdReadTime * 75*3;
1263                         }
1264                         start_rotating = 1;
1265                         break;
1266
1267                 case CdlReadToc + CMD_PART2:
1268                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1269                         IrqStat = Complete;
1270                         break;
1271
1272                 case CdlReadN:
1273                 case CdlReadS:
1274                         if (cdr.Reading && !cdr.SetlocPending)
1275                                 break;
1276
1277                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1278
1279                         if (cdr_stat.Type != CDRT_DATA && !(cdr.Mode & MODE_CDDA)) {
1280                                 error = ERROR_INVALIDCMD;
1281                                 goto set_error;
1282                         }
1283
1284                         StopCdda();
1285                         if (cdr.SetlocPending) {
1286                                 seekTime = cdrSeekTime(cdr.SetSector);
1287                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1288                                 cdr.SetlocPending = 0;
1289                         }
1290                         cdr.Reading = 1;
1291                         cdr.FileChannelSelected = 0;
1292                         cdr.AdpcmActive = 0;
1293
1294                         // Fighting Force 2 - update subq time immediately
1295                         // - fixes new game
1296                         UpdateSubq(cdr.SetSectorPlay);
1297                         cdr.LocL[0] = LOCL_INVALID;
1298                         cdr.SubqForwardSectors = 1;
1299                         cdr.sectorsRead = 0;
1300                         cdr.DriveState = DRIVESTATE_SEEK;
1301                         cdr.PhysCdPropagations = 0;
1302                         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1303                                         cdr.SetSectorPlay[2], 0);
1304
1305                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1306                         cycles += seekTime;
1307                         if (Config.hacks.cdr_read_timing)
1308                                 cycles = cdrAlignTimingHack(cycles);
1309                         else if (canDoTurbo())
1310                                 cycles = cdReadTime / 2;
1311                         CDRPLAYREAD_INT(cycles, 1);
1312
1313                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1314                         start_rotating = 1;
1315                         break;
1316
1317                 case CdlSync:
1318                 default:
1319                         error = ERROR_INVALIDCMD;
1320                         // FALLTHROUGH
1321
1322                 set_error:
1323                         SetResultSize_(2);
1324                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1325                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1326                         IrqStat = DiskError;
1327                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1328                         break;
1329         }
1330
1331         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1332                 cdr.DriveState = DRIVESTATE_STANDBY;
1333                 cdr.StatP |= STATUS_ROTATING;
1334         }
1335
1336         if (second_resp_time) {
1337                 cdr.CmdInProgress = Cmd | 0x100;
1338                 set_event(PSXINT_CDR, second_resp_time);
1339         }
1340         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1341                 cdr.CmdInProgress = cdr.Cmd;
1342                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1343         }
1344
1345         setIrq(IrqStat, Cmd);
1346 }
1347
1348 static void cdrPrepCdda(s16 *buf, int samples)
1349 {
1350 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1351         int i;
1352         for (i = 0; i < samples; i++) {
1353                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1354                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1355         }
1356 #endif
1357 }
1358
1359 static void cdrReadInterruptSetResult(unsigned char result)
1360 {
1361         if (cdr.IrqStat) {
1362                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1363                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1364                         cdr.CmdInProgress, cdr.IrqStat);
1365                 cdr.Irq1Pending = result;
1366                 // F1 2000 timing hack :(
1367                 // compensate for some csum func @80014380 taking too long
1368                 if (!cdr.AdpcmActive)
1369                         psxRegs.intCycle[PSXINT_CDREAD].sCycle += cdReadTime / 10;
1370                 return;
1371         }
1372         SetResultSize(1);
1373         cdr.Result[0] = result;
1374         setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1375 }
1376
1377 static void cdrUpdateTransferBuf(const u8 *buf)
1378 {
1379         if (!buf)
1380                 return;
1381         memcpy(cdr.Transfer, buf, DATA_SIZE);
1382         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1383         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1384                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1385         if (cdr.FifoOffset < 2048 + 12)
1386                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1387 }
1388
1389 static void cdrReadInterrupt(void)
1390 {
1391         const struct { u8 file, chan, mode, coding; } *subhdr;
1392         const u8 *buf = NULL;
1393         int deliver_data = 1;
1394         u8 subqPos[3];
1395         int read_ok;
1396         int is_start;
1397
1398         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1399         msfiAdd(subqPos, cdr.SubqForwardSectors);
1400         UpdateSubq(subqPos);
1401         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1402                 cdr.SubqForwardSectors++;
1403                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1404                 return;
1405         }
1406
1407         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1408         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1409         cdr.DriveState = DRIVESTATE_PLAY_READ;
1410         cdr.sectorsRead++;
1411
1412         read_ok = ReadTrack(cdr.SetSectorPlay);
1413         if (read_ok)
1414                 buf = cdra_getBuffer();
1415         if (buf == NULL)
1416                 read_ok = 0;
1417
1418         if (!read_ok) {
1419                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1420                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1421                 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1422                 return;
1423         }
1424         memcpy(cdr.LocL, buf, 8);
1425
1426         if (!cdr.IrqStat && !cdr.Irq1Pending)
1427                 cdrUpdateTransferBuf(buf);
1428
1429         subhdr = (void *)(buf + 4);
1430         do {
1431                 // try to process as adpcm
1432                 if (!(cdr.Mode & MODE_STRSND))
1433                         break;
1434                 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1435                         break;
1436                 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1437                         subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1438                 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1439                         break;
1440                 if (subhdr->chan & 0x80) { // ?
1441                         if (subhdr->chan != 0xff)
1442                                 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1443                         break;
1444                 }
1445                 if (!cdr.FileChannelSelected) {
1446                         cdr.CurFile = subhdr->file;
1447                         cdr.CurChannel = subhdr->chan;
1448                         cdr.FileChannelSelected = 1;
1449                 }
1450                 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1451                         break;
1452
1453                 // accepted as adpcm
1454                 deliver_data = 0;
1455
1456                 if (Config.Xa)
1457                         break;
1458                 is_start = !cdr.AdpcmActive;
1459                 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1460                 if (cdr.AdpcmActive)
1461                         SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1462         } while (0);
1463
1464         if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1465                 deliver_data = 0;
1466         if (!(cdr.Mode & MODE_SIZE_2340) && buf[3] != 1 && buf[3] != 2) {
1467                 deliver_data = 0; // according to duckstation
1468                 CDR_LOG_I("%d:%02d:%02d msf %x:%02x:%02x mode %02x ignored\n",
1469                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1470                         buf[0], buf[1], buf[2], buf[3]);
1471         }
1472
1473         /*
1474         Croc 2: $40 - only FORM1 (*)
1475         Judge Dredd: $C8 - only FORM1 (*)
1476         Sim Theme Park - no adpcm at all (zero)
1477         */
1478
1479         if (deliver_data)
1480                 cdrReadInterruptSetResult(cdr.StatP);
1481
1482         msfiAdd(cdr.SetSectorPlay, 1);
1483         cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 0);
1484
1485         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1486 }
1487
1488 /*
1489 cdrRead0:
1490         bit 0,1 - reg index
1491         bit 2 - adpcm active
1492         bit 3 - 1 parameter fifo empty
1493         bit 4 - 1 parameter fifo not full
1494         bit 5 - 1 response fifo not empty
1495         bit 6 - 1 data fifo not empty
1496         bit 7 - 1 command being processed
1497 */
1498
1499 unsigned char cdrRead0(void) {
1500         cdr.Ctrl &= ~0x64;
1501         cdr.Ctrl |= cdr.AdpcmActive << 2;
1502         cdr.Ctrl |= cdr.ResultReady << 5;
1503         cdr.Ctrl |= ((signed int)(cdr.FifoOffset - cdr.FifoSize) >> 31) & 0x40;
1504
1505         cdr.Ctrl |= 0x18;
1506
1507         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1508
1509         return psxHu8(0x1800) = cdr.Ctrl;
1510 }
1511
1512 void cdrWrite0(unsigned char rt) {
1513         CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1514
1515         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1516 }
1517
1518 unsigned char cdrRead1(void) {
1519         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1520                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1521         else
1522                 psxHu8(0x1801) = 0;
1523         cdr.ResultP++;
1524         if (cdr.ResultP == cdr.ResultC)
1525                 cdr.ResultReady = 0;
1526
1527         CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1528
1529         return psxHu8(0x1801);
1530 }
1531
1532 void cdrWrite1(unsigned char rt) {
1533         const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1534         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1535
1536         switch (cdr.Ctrl & 3) {
1537         case 0:
1538                 break;
1539         case 3:
1540                 cdr.AttenuatorRightToRightT = rt;
1541                 return;
1542         default:
1543                 return;
1544         }
1545
1546 #ifdef CDR_LOG_CMD
1547         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1548         if (cdr.ParamC) {
1549                 int i;
1550                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1551                 for (i = 0; i < cdr.ParamC; i++)
1552                         SysPrintf(" %x,", cdr.Param[i]);
1553                 SysPrintf("}");
1554         }
1555         SysPrintf(" @%08x\n", psxRegs.pc);
1556 #endif
1557
1558         cdr.ResultReady = 0;
1559         cdr.Ctrl |= 0x80;
1560
1561         if (!cdr.CmdInProgress) {
1562                 cdr.CmdInProgress = rt;
1563                 // should be something like 12k + controller delays
1564                 set_event(PSXINT_CDR, 5000);
1565         }
1566         else {
1567                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1568                         rt, cdr.Cmd, cdr.CmdInProgress);
1569                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1570                         cdr.CmdInProgress = rt;
1571         }
1572
1573         cdr.Cmd = rt;
1574 }
1575
1576 unsigned char cdrRead2(void) {
1577         unsigned char ret = cdr.Transfer[0x920];
1578
1579         if (cdr.FifoOffset < cdr.FifoSize)
1580                 ret = cdr.Transfer[cdr.FifoOffset++];
1581         else
1582                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1583
1584         CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1585         return ret;
1586 }
1587
1588 void cdrWrite2(unsigned char rt) {
1589         const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1590         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1591
1592         switch (cdr.Ctrl & 3) {
1593         case 0:
1594                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1595                         cdr.Param[cdr.ParamC++] = rt;
1596                 return;
1597         case 1:
1598                 cdr.IrqMask = rt;
1599                 setIrq(cdr.IrqStat, 0x1005);
1600                 return;
1601         case 2:
1602                 cdr.AttenuatorLeftToLeftT = rt;
1603                 return;
1604         case 3:
1605                 cdr.AttenuatorRightToLeftT = rt;
1606                 return;
1607         }
1608 }
1609
1610 unsigned char cdrRead3(void) {
1611         if (cdr.Ctrl & 0x1)
1612                 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1613         else
1614                 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1615
1616         CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1617                 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1618         return psxHu8(0x1803);
1619 }
1620
1621 void cdrWrite3(unsigned char rt) {
1622         const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1623         u8 ll, lr, rl, rr;
1624         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1625
1626         switch (cdr.Ctrl & 3) {
1627         case 0:
1628                 break; // transfer
1629         case 1:
1630                 if (cdr.IrqStat & rt) {
1631                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1632                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1633                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1634 #ifdef CDR_LOG_CMD_ACK
1635                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1636                                 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1637                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1638 #endif
1639                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1640                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1641                         {
1642                                 s32 c = 2048;
1643                                 if (cdr.CmdInProgress) {
1644                                         c = 2048 - (psxRegs.cycle - nextCycle);
1645                                         c = MAX_VALUE(c, 512);
1646                                 }
1647                                 set_event(PSXINT_CDR, c);
1648                         }
1649                 }
1650                 cdr.IrqStat &= ~rt;
1651
1652                 if (rt & 0x40)
1653                         cdr.ParamC = 0;
1654                 return;
1655         case 2:
1656                 cdr.AttenuatorLeftToRightT = rt;
1657                 return;
1658         case 3:
1659                 if (rt & 0x01)
1660                         log_unhandled("Mute ADPCM?\n");
1661                 if (rt & 0x20) {
1662                         ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1663                         rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1664                         if (ll == cdr.AttenuatorLeftToLeft &&
1665                             lr == cdr.AttenuatorLeftToRight &&
1666                             rl == cdr.AttenuatorRightToLeft &&
1667                             rr == cdr.AttenuatorRightToRight)
1668                                 return;
1669                         cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1670                         cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1671                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1672                         SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1673                 }
1674                 return;
1675         }
1676
1677         // test: Viewpoint
1678         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1679                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1680         }
1681         else if (rt & 0x80) {
1682                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1683                         case MODE_SIZE_2328:
1684                         case 0x00:
1685                                 cdr.FifoOffset = 12;
1686                                 cdr.FifoSize = 2048 + 12;
1687                                 break;
1688
1689                         case MODE_SIZE_2340:
1690                         default:
1691                                 cdr.FifoOffset = 0;
1692                                 cdr.FifoSize = 2340;
1693                                 break;
1694                 }
1695         }
1696         else if (!(rt & 0xc0))
1697                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1698 }
1699
1700 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1701         u32 cdsize, max_words, cycles;
1702         int size;
1703         u8 *ptr;
1704
1705 #if 0
1706         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1707         if (cdr.FifoOffset == 0) {
1708                 ptr = cdr.Transfer;
1709                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1710         }
1711         SysPrintf("\n");
1712 #endif
1713
1714         switch (chcr & 0x71000000) {
1715                 case 0x11000000:
1716                         madr &= ~3;
1717                         ptr = getDmaRam(madr, &max_words);
1718                         if (ptr == INVALID_PTR) {
1719                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1720                                 break;
1721                         }
1722
1723                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1724
1725                         /*
1726                         GS CDX: Enhancement CD crash
1727                         - Setloc 0:0:0
1728                         - CdlPlay
1729                         - Spams DMA3 and gets buffer overrun
1730                         */
1731                         size = DATA_SIZE - cdr.FifoOffset;
1732                         if (size > cdsize)
1733                                 size = cdsize;
1734                         if (size > max_words * 4)
1735                                 size = max_words * 4;
1736                         if (size > 0)
1737                         {
1738                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1739                                 cdr.FifoOffset += size;
1740                         }
1741                         if (size < cdsize) {
1742                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1743                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1744                         }
1745                         psxCpu->Clear(madr, cdsize / 4);
1746
1747                         cycles = (cdsize / 4) * 24;
1748                         set_event(PSXINT_CDRDMA, cycles);
1749
1750                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1751                         if (chcr & 0x100) {
1752                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1753                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1754                         }
1755                         else {
1756                                 // halted
1757                                 psxRegs.cycle += cycles - 20;
1758                         }
1759                         if (canDoTurbo() && cdr.Reading && cdr.FifoOffset >= 2048)
1760                                 CDRPLAYREAD_INT(cycles + 4096, 1);
1761                         return;
1762
1763                 default:
1764                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1765                         break;
1766         }
1767
1768         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1769         DMA_INTERRUPT(3);
1770 }
1771
1772 void cdrDmaInterrupt(void)
1773 {
1774         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1775         {
1776                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1777                 DMA_INTERRUPT(3);
1778         }
1779 }
1780
1781 static void getCdInfo(void)
1782 {
1783         cdra_getTN(cdr.ResultTN);
1784         cdra_getTD(0, cdr.SetSectorEnd);
1785         cdra_getStatus(&cdr_stat);
1786 }
1787
1788 void cdrReset() {
1789         memset(&cdr, 0, sizeof(cdr));
1790         cdr.CurTrack = 1;
1791         cdr.FilterFile = 0;
1792         cdr.FilterChannel = 0;
1793         cdr.IrqMask = 0x1f;
1794         cdr.IrqStat = NoIntr;
1795
1796         // BIOS player - default values
1797         cdr.AttenuatorLeftToLeft = 0x80;
1798         cdr.AttenuatorLeftToRight = 0x00;
1799         cdr.AttenuatorRightToLeft = 0x00;
1800         cdr.AttenuatorRightToRight = 0x80;
1801
1802         softReset();
1803         getCdInfo();
1804 }
1805
1806 int cdrFreeze(void *f, int Mode) {
1807         u32 tmp;
1808         u8 tmpp[3];
1809
1810         cdr.freeze_ver = 0x63647203;
1811         gzfreeze(&cdr, sizeof(cdr));
1812         
1813         if (Mode == 1) {
1814                 cdr.ParamP = cdr.ParamC;
1815                 tmp = cdr.FifoOffset;
1816         }
1817
1818         gzfreeze(&tmp, sizeof(tmp));
1819
1820         if (Mode == 0) {
1821                 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1822                 getCdInfo();
1823
1824                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1825                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1826                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1827                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1828
1829                 // read right sub data
1830                 memcpy(tmpp, cdr.Prev, sizeof(tmpp));
1831                 if (cdr.freeze_ver < 0x63647203) {
1832                         tmpp[0] = btoi(tmpp[0]);
1833                         tmpp[1] = btoi(tmpp[1]);
1834                         tmpp[2] = btoi(tmpp[2]);
1835                 }
1836                 cdr.Prev[0] = 0xff;
1837                 if (tmpp[0] != 0xff)
1838                         ReadTrack(tmpp);
1839
1840                 if (cdr.Play) {
1841                         if (cdr.freeze_ver < 0x63647202)
1842                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1843
1844                         Find_CurTrack(cdr.SetSectorPlay);
1845                 }
1846                 if (!cdr.Muted)
1847                         ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToRight,
1848                         rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1849                 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1850         }
1851
1852         return 0;
1853 }
1854
1855 void LidInterrupt(void) {
1856         getCdInfo();
1857         cdrLidSeekInterrupt();
1858 }