5dfa658dc7d96b70899ec37f493ed601878737c3
[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 "cdrom.h"
25 #include "ppf.h"
26 #include "psxdma.h"
27 #include "arm_features.h"
28
29 /* logging */
30 #if 0
31 #define CDR_LOG SysPrintf
32 #else
33 #define CDR_LOG(...)
34 #endif
35 #if 0
36 #define CDR_LOG_I SysPrintf
37 #else
38 #define CDR_LOG_I(...)
39 #endif
40 #if 0
41 #define CDR_LOG_IO SysPrintf
42 #else
43 #define CDR_LOG_IO(...)
44 #endif
45 //#define CDR_LOG_CMD_IRQ
46
47 static struct {
48         unsigned char OCUP;
49         unsigned char Reg1Mode;
50         unsigned char Reg2;
51         unsigned char CmdProcess;
52         unsigned char Ctrl;
53         unsigned char Stat;
54
55         unsigned char StatP;
56
57         unsigned char Transfer[DATA_SIZE];
58         struct {
59                 unsigned char Track;
60                 unsigned char Index;
61                 unsigned char Relative[3];
62                 unsigned char Absolute[3];
63         } subq;
64         unsigned char TrackChanged;
65         unsigned char pad1[3];
66         unsigned int  freeze_ver;
67
68         unsigned char Prev[4];
69         unsigned char Param[8];
70         unsigned char Result[16];
71
72         unsigned char ParamC;
73         unsigned char ParamP;
74         unsigned char ResultC;
75         unsigned char ResultP;
76         unsigned char ResultReady;
77         unsigned char Cmd;
78         unsigned char Readed;
79         unsigned char SetlocPending;
80         u32 Reading;
81
82         unsigned char ResultTN[6];
83         unsigned char ResultTD[4];
84         unsigned char SetSectorPlay[4];
85         unsigned char SetSectorEnd[4];
86         unsigned char SetSector[4];
87         unsigned char Track;
88         boolean Play, Muted;
89         int CurTrack;
90         int Mode, File, Channel;
91         int Reset;
92         int NoErr;
93         int FirstSector;
94
95         xa_decode_t Xa;
96
97         int Init;
98
99         u16 Irq;
100         u8 IrqRepeated;
101         u32 eCycle;
102
103         u8 pad2;
104
105         u8 DriveState;
106         u8 FastForward;
107         u8 FastBackward;
108         u8 pad;
109
110         u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
111         u8 AttenuatorRightToRight, AttenuatorRightToLeft;
112         u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
113         u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
114 } cdr;
115 static unsigned char *pTransfer;
116 static s16 read_buf[CD_FRAMESIZE_RAW/2];
117
118 /* CD-ROM magic numbers */
119 #define CdlSync        0  /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
120 #define CdlNop         1
121 #define CdlSetloc      2
122 #define CdlPlay        3
123 #define CdlForward     4
124 #define CdlBackward    5
125 #define CdlReadN       6
126 #define CdlStandby     7
127 #define CdlStop        8
128 #define CdlPause       9
129 #define CdlReset       10
130 #define CdlMute        11
131 #define CdlDemute      12
132 #define CdlSetfilter   13
133 #define CdlSetmode     14
134 #define CdlGetparam    15
135 #define CdlGetlocL     16
136 #define CdlGetlocP     17
137 #define CdlReadT       18
138 #define CdlGetTN       19
139 #define CdlGetTD       20
140 #define CdlSeekL       21
141 #define CdlSeekP       22
142 #define CdlSetclock    23
143 #define CdlGetclock    24
144 #define CdlTest        25
145 #define CdlID          26
146 #define CdlReadS       27
147 #define CdlInit        28
148 #define CdlGetQ        29
149 #define CdlReadToc     30
150
151 #ifdef CDR_LOG_CMD_IRQ
152 static const char * const CmdName[0x100] = {
153     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
154     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
155     "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
156     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
157     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
158     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
159     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
160     "CdlInit",     NULL,           "CDlReadToc", NULL
161 };
162 #endif
163
164 unsigned char Test04[] = { 0 };
165 unsigned char Test05[] = { 0 };
166 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
167 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
168 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
169
170 // cdr.Stat:
171 #define NoIntr          0
172 #define DataReady       1
173 #define Complete        2
174 #define Acknowledge     3
175 #define DataEnd         4
176 #define DiskError       5
177
178 /* Modes flags */
179 #define MODE_SPEED       (1<<7) // 0x80
180 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
181 #define MODE_SIZE_2340   (1<<5) // 0x20
182 #define MODE_SIZE_2328   (1<<4) // 0x10
183 #define MODE_SIZE_2048   (0<<4) // 0x00
184 #define MODE_SF          (1<<3) // 0x08 channel on/off
185 #define MODE_REPORT      (1<<2) // 0x04
186 #define MODE_AUTOPAUSE   (1<<1) // 0x02
187 #define MODE_CDDA        (1<<0) // 0x01
188
189 /* Status flags */
190 #define STATUS_PLAY      (1<<7) // 0x80
191 #define STATUS_SEEK      (1<<6) // 0x40
192 #define STATUS_READ      (1<<5) // 0x20
193 #define STATUS_SHELLOPEN (1<<4) // 0x10
194 #define STATUS_UNKNOWN3  (1<<3) // 0x08
195 #define STATUS_UNKNOWN2  (1<<2) // 0x04
196 #define STATUS_ROTATING  (1<<1) // 0x02
197 #define STATUS_ERROR     (1<<0) // 0x01
198
199 /* Errors */
200 #define ERROR_NOTREADY   (1<<7) // 0x80
201 #define ERROR_INVALIDCMD (1<<6) // 0x40
202 #define ERROR_INVALIDARG (1<<5) // 0x20
203
204 // 1x = 75 sectors per second
205 // PSXCLK = 1 sec in the ps
206 // so (PSXCLK / 75) = cdr read time (linuzappz)
207 #define cdReadTime (PSXCLK / 75)
208
209 enum drive_state {
210         DRIVESTATE_STANDBY = 0, // pause, play, read
211         DRIVESTATE_LID_OPEN,
212         DRIVESTATE_RESCAN_CD,
213         DRIVESTATE_PREPARE_CD,
214         DRIVESTATE_STOPPED,
215 };
216
217 static struct CdrStat stat;
218
219 static unsigned int msf2sec(const u8 *msf) {
220         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
221 }
222
223 // for that weird psemu API..
224 static unsigned int fsm2sec(const u8 *msf) {
225         return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
226 }
227
228 static void sec2msf(unsigned int s, u8 *msf) {
229         msf[0] = s / 75 / 60;
230         s = s - msf[0] * 75 * 60;
231         msf[1] = s / 75;
232         s = s - msf[1] * 75;
233         msf[2] = s;
234 }
235
236 // cdrInterrupt
237 #define CDR_INT(eCycle) { \
238         psxRegs.interrupt |= (1 << PSXINT_CDR); \
239         psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
240         psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
241         new_dyna_set_event(PSXINT_CDR, eCycle); \
242 }
243
244 // cdrReadInterrupt
245 #define CDREAD_INT(eCycle) { \
246         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
247         psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
248         psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
249         new_dyna_set_event(PSXINT_CDREAD, eCycle); \
250 }
251
252 // cdrLidSeekInterrupt
253 #define CDRLID_INT(eCycle) { \
254         psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
255         psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
256         psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
257         new_dyna_set_event(PSXINT_CDRLID, eCycle); \
258 }
259
260 // cdrPlayInterrupt
261 #define CDRSEEKPLAY_INT(eCycle) { \
262         psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
263         psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
264         psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
265         new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \
266 }
267
268 #define StopReading() { \
269         cdr.Reading = 0; \
270         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
271 }
272
273 #define StopCdda() { \
274         if (cdr.Play && !Config.Cdda) CDR_stop(); \
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 static void setIrq(int log_cmd)
292 {
293         if (cdr.Stat & cdr.Reg2)
294                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
295
296 #ifdef CDR_LOG_CMD_IRQ
297         {
298                 int i;
299                 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
300                         !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
301                 for (i = 0; i < cdr.ResultC; i++)
302                         SysPrintf("%02x ", cdr.Result[i]);
303                 SysPrintf("\n");
304         }
305 #endif
306 }
307
308 // timing used in this function was taken from tests on real hardware
309 // (yes it's slow, but you probably don't want to modify it)
310 void cdrLidSeekInterrupt()
311 {
312         switch (cdr.DriveState) {
313         default:
314         case DRIVESTATE_STANDBY:
315                 StopCdda();
316                 StopReading();
317                 SetPlaySeekRead(cdr.StatP, 0);
318
319                 if (CDR_getStatus(&stat) == -1)
320                         return;
321
322                 if (stat.Status & STATUS_SHELLOPEN)
323                 {
324                         cdr.DriveState = DRIVESTATE_LID_OPEN;
325                         CDRLID_INT(0x800);
326                 }
327                 break;
328
329         case DRIVESTATE_LID_OPEN:
330                 if (CDR_getStatus(&stat) == -1)
331                         stat.Status &= ~STATUS_SHELLOPEN;
332
333                 // 02, 12, 10
334                 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
335                         cdr.StatP |= STATUS_SHELLOPEN;
336
337                         // could generate error irq here, but real hardware
338                         // only sometimes does that
339                         // (not done when lots of commands are sent?)
340
341                         CDRLID_INT(cdReadTime * 30);
342                         break;
343                 }
344                 else if (cdr.StatP & STATUS_ROTATING) {
345                         cdr.StatP &= ~STATUS_ROTATING;
346                 }
347                 else if (!(stat.Status & STATUS_SHELLOPEN)) {
348                         // closed now
349                         CheckCdrom();
350
351                         // cdr.StatP STATUS_SHELLOPEN is "sticky"
352                         // and is only cleared by CdlNop
353
354                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
355                         CDRLID_INT(cdReadTime * 105);
356                         break;
357                 }
358
359                 // recheck for close
360                 CDRLID_INT(cdReadTime * 3);
361                 break;
362
363         case DRIVESTATE_RESCAN_CD:
364                 cdr.StatP |= STATUS_ROTATING;
365                 cdr.DriveState = DRIVESTATE_PREPARE_CD;
366
367                 // this is very long on real hardware, over 6 seconds
368                 // make it a bit faster here...
369                 CDRLID_INT(cdReadTime * 150);
370                 break;
371
372         case DRIVESTATE_PREPARE_CD:
373                 cdr.StatP |= STATUS_SEEK;
374
375                 cdr.DriveState = DRIVESTATE_STANDBY;
376                 CDRLID_INT(cdReadTime * 26);
377                 break;
378         }
379 }
380
381 static void Find_CurTrack(const u8 *time)
382 {
383         int current, sect;
384
385         current = msf2sec(time);
386
387         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
388                 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
389                 sect = fsm2sec(cdr.ResultTD);
390                 if (sect - current >= 150)
391                         break;
392         }
393 }
394
395 static void generate_subq(const u8 *time)
396 {
397         unsigned char start[3], next[3];
398         unsigned int this_s, start_s, next_s, pregap;
399         int relative_s;
400
401         CDR_getTD(cdr.CurTrack, start);
402         if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
403                 pregap = 150;
404                 CDR_getTD(cdr.CurTrack + 1, next);
405         }
406         else {
407                 // last track - cd size
408                 pregap = 0;
409                 next[0] = cdr.SetSectorEnd[2];
410                 next[1] = cdr.SetSectorEnd[1];
411                 next[2] = cdr.SetSectorEnd[0];
412         }
413
414         this_s = msf2sec(time);
415         start_s = fsm2sec(start);
416         next_s = fsm2sec(next);
417
418         cdr.TrackChanged = FALSE;
419
420         if (next_s - this_s < pregap) {
421                 cdr.TrackChanged = TRUE;
422                 cdr.CurTrack++;
423                 start_s = next_s;
424         }
425
426         cdr.subq.Index = 1;
427
428         relative_s = this_s - start_s;
429         if (relative_s < 0) {
430                 cdr.subq.Index = 0;
431                 relative_s = -relative_s;
432         }
433         sec2msf(relative_s, cdr.subq.Relative);
434
435         cdr.subq.Track = itob(cdr.CurTrack);
436         cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
437         cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
438         cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
439         cdr.subq.Absolute[0] = itob(time[0]);
440         cdr.subq.Absolute[1] = itob(time[1]);
441         cdr.subq.Absolute[2] = itob(time[2]);
442 }
443
444 static void ReadTrack(const u8 *time) {
445         unsigned char tmp[3];
446         struct SubQ *subq;
447         u16 crc;
448
449         tmp[0] = itob(time[0]);
450         tmp[1] = itob(time[1]);
451         tmp[2] = itob(time[2]);
452
453         if (memcmp(cdr.Prev, tmp, 3) == 0)
454                 return;
455
456         CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
457
458         cdr.NoErr = CDR_readTrack(tmp);
459         memcpy(cdr.Prev, tmp, 3);
460
461         if (CheckSBI(time))
462                 return;
463
464         subq = (struct SubQ *)CDR_getBufferSub();
465         if (subq != NULL && cdr.CurTrack == 1) {
466                 crc = calcCrc((u8 *)subq + 12, 10);
467                 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
468                         cdr.subq.Track = subq->TrackNumber;
469                         cdr.subq.Index = subq->IndexNumber;
470                         memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
471                         memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
472                 }
473                 else {
474                         CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
475                                 tmp[0], tmp[1], tmp[2]);
476                 }
477         }
478         else {
479                 generate_subq(time);
480         }
481
482         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
483                 cdr.subq.Track, cdr.subq.Index,
484                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
485                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
486 }
487
488 static void AddIrqQueue(unsigned short irq, unsigned long ecycle) {
489         if (cdr.Irq != 0) {
490                 if (irq == cdr.Irq || irq + 0x100 == cdr.Irq) {
491                         cdr.IrqRepeated = 1;
492                         CDR_INT(ecycle);
493                         return;
494                 }
495
496                 CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
497         }
498
499         cdr.Irq = irq;
500         cdr.eCycle = ecycle;
501
502         CDR_INT(ecycle);
503 }
504
505 static void cdrPlayInterrupt_Autopause()
506 {
507         u32 abs_lev_max = 0;
508         boolean abs_lev_chselect;
509         u32 i;
510
511         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
512                 CDR_LOG( "CDDA STOP\n" );
513
514                 // Magic the Gathering
515                 // - looping territory cdda
516
517                 // ...?
518                 //cdr.ResultReady = 1;
519                 //cdr.Stat = DataReady;
520                 cdr.Stat = DataEnd;
521                 setIrq(0x200);
522
523                 StopCdda();
524                 SetPlaySeekRead(cdr.StatP, 0);
525         }
526         else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
527                 cdr.Result[0] = cdr.StatP;
528                 cdr.Result[1] = cdr.subq.Track;
529                 cdr.Result[2] = cdr.subq.Index;
530                 
531                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
532                 
533                 /* 8 is a hack. For accuracy, it should be 588. */
534                 for (i = 0; i < 8; i++)
535                 {
536                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
537                 }
538                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
539                 abs_lev_max |= abs_lev_chselect << 15;
540
541                 if (cdr.subq.Absolute[2] & 0x10) {
542                         cdr.Result[3] = cdr.subq.Relative[0];
543                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
544                         cdr.Result[5] = cdr.subq.Relative[2];
545                 }
546                 else {
547                         cdr.Result[3] = cdr.subq.Absolute[0];
548                         cdr.Result[4] = cdr.subq.Absolute[1];
549                         cdr.Result[5] = cdr.subq.Absolute[2];
550                 }
551
552                 cdr.Result[6] = abs_lev_max >> 0;
553                 cdr.Result[7] = abs_lev_max >> 8;
554
555                 // Rayman: Logo freeze (resultready + dataready)
556                 cdr.ResultReady = 1;
557                 cdr.Stat = DataReady;
558
559                 SetResultSize(8);
560                 setIrq(0x201);
561         }
562 }
563
564 static int cdrSeekTime(unsigned char *target)
565 {
566         int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
567         /*
568         * Gameblabla :
569         * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
570         * and was unreliable for that game.
571         * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
572         *
573         * Obviously, this isn't perfect but right now, it should be a bit better.
574         * Games to test this against if you change that setting :
575         * - Driver (titlescreen music delay and retry mission)
576         * - Worms Pinball (Will either not boot or crash in the memory card screen)
577         * - Viewpoint (short pauses if the delay in the ingame music is too long)
578         *
579         * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
580         * However, 1000000 is not enough for Worms Pinball to reliably boot.
581         */
582         if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
583         CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
584         return seekTime;
585 }
586
587 // also handles seek
588 void cdrPlayInterrupt()
589 {
590         if (cdr.StatP & STATUS_SEEK) {
591                 if (cdr.Stat) {
592                         CDR_LOG_I("cdrom: seek stat hack\n");
593                         CDRSEEKPLAY_INT(0x1000);
594                         return;
595                 }
596                 SetResultSize(1);
597                 cdr.StatP |= STATUS_ROTATING;
598                 SetPlaySeekRead(cdr.StatP, cdr.Play ? STATUS_PLAY : 0);
599                 cdr.Result[0] = cdr.StatP;
600                 if (cdr.Irq == 0) {
601                         cdr.Stat = Complete;
602                         setIrq(0x202);
603                 }
604
605                 Find_CurTrack(cdr.SetSectorPlay);
606                 ReadTrack(cdr.SetSectorPlay);
607                 cdr.TrackChanged = FALSE;
608         }
609
610         if (!cdr.Play) return;
611
612         CDR_LOG( "CDDA - %d:%d:%d\n",
613                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
614
615         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
616                 StopCdda();
617                 SetPlaySeekRead(cdr.StatP, 0);
618                 cdr.TrackChanged = TRUE;
619         }
620         else {
621                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
622         }
623
624         if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
625                 cdrPlayInterrupt_Autopause();
626
627         if (CDR_readCDDA && !cdr.Muted && !Config.Cdda) {
628                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
629                 if (SPU_playCDDAchannel)
630                         SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW);
631         }
632
633         cdr.SetSectorPlay[2]++;
634         if (cdr.SetSectorPlay[2] == 75) {
635                 cdr.SetSectorPlay[2] = 0;
636                 cdr.SetSectorPlay[1]++;
637                 if (cdr.SetSectorPlay[1] == 60) {
638                         cdr.SetSectorPlay[1] = 0;
639                         cdr.SetSectorPlay[0]++;
640                 }
641         }
642
643         CDRSEEKPLAY_INT(cdReadTime);
644
645         // update for CdlGetlocP/autopause
646         generate_subq(cdr.SetSectorPlay);
647 }
648
649 void cdrInterrupt() {
650         u16 Irq = cdr.Irq;
651         int no_busy_error = 0;
652         int start_rotating = 0;
653         int error = 0;
654         int delay;
655         unsigned int seekTime = 0;
656         u8 set_loc[3];
657         int i;
658
659         // Reschedule IRQ
660         if (cdr.Stat) {
661                 CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat);
662                 CDR_INT(0x1000);
663                 return;
664         }
665
666         cdr.Ctrl &= ~0x80;
667
668         // default response
669         SetResultSize(1);
670         cdr.Result[0] = cdr.StatP;
671         cdr.Stat = Acknowledge;
672
673         if (cdr.IrqRepeated) {
674                 cdr.IrqRepeated = 0;
675                 if (cdr.eCycle > psxRegs.cycle) {
676                         CDR_INT(cdr.eCycle);
677                         goto finish;
678                 }
679         }
680
681         cdr.Irq = 0;
682
683         switch (Irq) {
684                 case CdlNop:
685                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
686                                 cdr.StatP &= ~STATUS_SHELLOPEN;
687                         no_busy_error = 1;
688                         break;
689
690                 case CdlSetloc:
691                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
692
693                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
694                         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))
695                         {
696                                 CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
697                                 error = ERROR_INVALIDARG;
698                                 goto set_error;
699                         }
700                         else
701                         {
702                                 for (i = 0; i < 3; i++)
703                                         set_loc[i] = btoi(cdr.Param[i]);
704                                 memcpy(cdr.SetSector, set_loc, 3);
705                                 cdr.SetSector[3] = 0;
706                                 cdr.SetlocPending = 1;
707                         }
708                         break;
709
710                 do_CdlPlay:
711                 case CdlPlay:
712                         StopCdda();
713                         StopReading();
714
715                         cdr.FastBackward = 0;
716                         cdr.FastForward = 0;
717
718                         // BIOS CD Player
719                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
720
721                         if (cdr.ParamC != 0 && cdr.Param[0] != 0) {
722                                 int track = btoi( cdr.Param[0] );
723
724                                 if (track <= cdr.ResultTN[1])
725                                         cdr.CurTrack = track;
726
727                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
728
729                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
730                                         for (i = 0; i < 3; i++)
731                                                 set_loc[i] = cdr.ResultTD[2 - i];
732                                         seekTime = cdrSeekTime(set_loc);
733                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
734                                 }
735                         }
736                         else if (cdr.SetlocPending) {
737                                 seekTime = cdrSeekTime(cdr.SetSector);
738                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
739                         }
740                         else {
741                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
742                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
743                         }
744                         cdr.SetlocPending = 0;
745
746                         /*
747                         Rayman: detect track changes
748                         - fixes logo freeze
749
750                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
751                         - plays tracks without retry play
752
753                         Wild 9: skip PREGAP + starting accurate SubQ
754                         - plays tracks without retry play
755                         */
756                         Find_CurTrack(cdr.SetSectorPlay);
757                         ReadTrack(cdr.SetSectorPlay);
758                         cdr.TrackChanged = FALSE;
759
760                         if (!Config.Cdda)
761                                 CDR_play(cdr.SetSectorPlay);
762
763                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
764                         
765                         // BIOS player - set flag again
766                         cdr.Play = TRUE;
767
768                         CDRSEEKPLAY_INT(cdReadTime + seekTime);
769                         start_rotating = 1;
770                         break;
771
772                 case CdlForward:
773                         // TODO: error 80 if stopped
774                         cdr.Stat = Complete;
775
776                         // GameShark CD Player: Calls 2x + Play 2x
777                         cdr.FastForward = 1;
778                         cdr.FastBackward = 0;
779                         break;
780
781                 case CdlBackward:
782                         cdr.Stat = Complete;
783
784                         // GameShark CD Player: Calls 2x + Play 2x
785                         cdr.FastBackward = 1;
786                         cdr.FastForward = 0;
787                         break;
788
789                 case CdlStandby:
790                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
791                                 error = ERROR_INVALIDARG;
792                                 goto set_error;
793                         }
794                         AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
795                         start_rotating = 1;
796                         break;
797
798                 case CdlStandby + 0x100:
799                         cdr.Stat = Complete;
800                         break;
801
802                 case CdlStop:
803                         if (cdr.Play) {
804                                 // grab time for current track
805                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
806
807                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
808                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
809                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
810                         }
811
812                         StopCdda();
813                         StopReading();
814                         SetPlaySeekRead(cdr.StatP, 0);
815                         cdr.StatP &= ~STATUS_ROTATING;
816
817                         delay = 0x800;
818                         if (cdr.DriveState == DRIVESTATE_STANDBY)
819                                 delay = cdReadTime * 30 / 2;
820
821                         cdr.DriveState = DRIVESTATE_STOPPED;
822                         AddIrqQueue(CdlStop + 0x100, delay);
823                         break;
824
825                 case CdlStop + 0x100:
826                         cdr.Stat = Complete;
827                         break;
828
829                 case CdlPause:
830                         StopCdda();
831                         StopReading();
832                         /*
833                         Gundam Battle Assault 2: much slower (*)
834                         - Fixes boot, gameplay
835
836                         Hokuto no Ken 2: slower
837                         - Fixes intro + subtitles
838
839                         InuYasha - Feudal Fairy Tale: slower
840                         - Fixes battles
841                         */
842                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
843                          * The timings from Duckstation are based upon hardware tests.
844                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
845                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
846                          * */
847                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
848                         {
849                                 delay = 7000;
850                         }
851                         else
852                         {
853                                 delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
854                         }
855                         AddIrqQueue(CdlPause + 0x100, delay);
856                         SetPlaySeekRead(cdr.StatP, 0);
857                         cdr.Ctrl |= 0x80;
858                         break;
859
860                 case CdlPause + 0x100:
861                         cdr.Stat = Complete;
862                         break;
863
864                 case CdlReset:
865                         StopCdda();
866                         StopReading();
867                         SetPlaySeekRead(cdr.StatP, 0);
868                         cdr.Muted = FALSE;
869                         cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
870                         AddIrqQueue(CdlReset + 0x100, 4100000);
871                         no_busy_error = 1;
872                         start_rotating = 1;
873                         break;
874
875                 case CdlReset + 0x100:
876                         cdr.Stat = Complete;
877                         break;
878
879                 case CdlMute:
880                         cdr.Muted = TRUE;
881                         break;
882
883                 case CdlDemute:
884                         cdr.Muted = FALSE;
885                         break;
886
887                 case CdlSetfilter:
888                         cdr.File = cdr.Param[0];
889                         cdr.Channel = cdr.Param[1];
890                         break;
891
892                 case CdlSetmode:
893                         no_busy_error = 1;
894                         break;
895
896                 case CdlGetparam:
897                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
898                         SetResultSize(5);
899                         cdr.Result[1] = cdr.Mode;
900                         cdr.Result[2] = 0;
901                         cdr.Result[3] = cdr.File;
902                         cdr.Result[4] = cdr.Channel;
903                         no_busy_error = 1;
904                         break;
905
906                 case CdlGetlocL:
907                         SetResultSize(8);
908                         memcpy(cdr.Result, cdr.Transfer, 8);
909                         break;
910
911                 case CdlGetlocP:
912                         SetResultSize(8);
913                         memcpy(&cdr.Result, &cdr.subq, 8);
914                         break;
915
916                 case CdlReadT: // SetSession?
917                         // really long
918                         AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
919                         start_rotating = 1;
920                         break;
921
922                 case CdlReadT + 0x100:
923                         cdr.Stat = Complete;
924                         break;
925
926                 case CdlGetTN:
927                         SetResultSize(3);
928                         if (CDR_getTN(cdr.ResultTN) == -1) {
929                                 cdr.Stat = DiskError;
930                                 cdr.Result[0] |= STATUS_ERROR;
931                         } else {
932                                 cdr.Stat = Acknowledge;
933                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
934                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
935                         }
936                         break;
937
938                 case CdlGetTD:
939                         cdr.Track = btoi(cdr.Param[0]);
940                         SetResultSize(4);
941                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
942                                 cdr.Stat = DiskError;
943                                 cdr.Result[0] |= STATUS_ERROR;
944                         } else {
945                                 cdr.Stat = Acknowledge;
946                                 cdr.Result[0] = cdr.StatP;
947                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
948                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
949                                 /* According to Nocash's documentation, the function doesn't care about ff.
950                                  * This can be seen also in Mednafen's implementation. */
951                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
952                         }
953                         break;
954
955                 case CdlSeekL:
956                 case CdlSeekP:
957                         StopCdda();
958                         StopReading();
959                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
960
961                         seekTime = cdrSeekTime(cdr.SetSector);
962                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
963                         /*
964                         Crusaders of Might and Magic = 0.5x-4x
965                         - fix cutscene speech start
966
967                         Eggs of Steel = 2x-?
968                         - fix new game
969
970                         Medievil = ?-4x
971                         - fix cutscene speech
972
973                         Rockman X5 = 0.5-4x
974                         - fix capcom logo
975                         */
976                         CDRSEEKPLAY_INT(cdReadTime + seekTime);
977                         start_rotating = 1;
978                         break;
979
980                 case CdlTest:
981                         switch (cdr.Param[0]) {
982                                 case 0x20: // System Controller ROM Version
983                                         SetResultSize(4);
984                                         memcpy(cdr.Result, Test20, 4);
985                                         break;
986                                 case 0x22:
987                                         SetResultSize(8);
988                                         memcpy(cdr.Result, Test22, 4);
989                                         break;
990                                 case 0x23: case 0x24:
991                                         SetResultSize(8);
992                                         memcpy(cdr.Result, Test23, 4);
993                                         break;
994                         }
995                         no_busy_error = 1;
996                         break;
997
998                 case CdlID:
999                         AddIrqQueue(CdlID + 0x100, 20480);
1000                         break;
1001
1002                 case CdlID + 0x100:
1003                         SetResultSize(8);
1004                         cdr.Result[0] = cdr.StatP;
1005                         cdr.Result[1] = 0;
1006                         cdr.Result[2] = 0;
1007                         cdr.Result[3] = 0;
1008
1009                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1010                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1011                                 cdr.Result[1] = 0xc0;
1012                         }
1013                         else {
1014                                 if (stat.Type == 2)
1015                                         cdr.Result[1] |= 0x10;
1016                                 if (CdromId[0] == '\0')
1017                                         cdr.Result[1] |= 0x80;
1018                         }
1019                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1020
1021                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
1022                         cdr.Stat = Complete;
1023                         break;
1024
1025                 case CdlInit:
1026                         StopCdda();
1027                         StopReading();
1028                         SetPlaySeekRead(cdr.StatP, 0);
1029                         // yes, it really sets STATUS_SHELLOPEN
1030                         cdr.StatP |= STATUS_SHELLOPEN;
1031                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1032                         CDRLID_INT(20480);
1033                         no_busy_error = 1;
1034                         start_rotating = 1;
1035                         break;
1036
1037                 case CdlGetQ:
1038                         no_busy_error = 1;
1039                         break;
1040
1041                 case CdlReadToc:
1042                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1043                         no_busy_error = 1;
1044                         start_rotating = 1;
1045                         break;
1046
1047                 case CdlReadToc + 0x100:
1048                         cdr.Stat = Complete;
1049                         no_busy_error = 1;
1050                         break;
1051
1052                 case CdlReadN:
1053                 case CdlReadS:
1054                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1055
1056                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1057                                 // Read* acts as play for cdda tracks in cdda mode
1058                                 goto do_CdlPlay;
1059
1060                         StopCdda();
1061                         if (cdr.SetlocPending) {
1062                                 seekTime = cdrSeekTime(cdr.SetSector);
1063                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1064                                 cdr.SetlocPending = 0;
1065                         }
1066                         cdr.Reading = 1;
1067                         cdr.FirstSector = 1;
1068
1069                         // Fighting Force 2 - update subq time immediately
1070                         // - fixes new game
1071                         ReadTrack(cdr.SetSectorPlay);
1072
1073
1074                         // Crusaders of Might and Magic - update getlocl now
1075                         // - fixes cutscene speech
1076                         {
1077                                 u8 *buf = CDR_getBuffer();
1078                                 if (buf != NULL)
1079                                         memcpy(cdr.Transfer, buf, 8);
1080                         }
1081
1082                         /*
1083                         Duke Nukem: Land of the Babes - seek then delay read for one frame
1084                         - fixes cutscenes
1085                         C-12 - Final Resistance - doesn't like seek
1086                         */
1087                         
1088                         /*      
1089                                 By nicolasnoble from PCSX Redux :
1090                                 "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now.
1091                                 For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect
1092                                 the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK
1093                                 flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game
1094                                 tries to protect itself against errors by preventing from issuing a GetLocP while it knows the
1095                                 last status was "seek". But this makes the logic just softlock as it'll never get a notification
1096                                 about the fact the drive is done seeking and the read actually started.
1097
1098                                 In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is
1099                                 done right away. It's rather when it's done seeking, and the read has actually started. This probably
1100                                 requires a bit more work to make sure seek delays are processed properly.
1101                                 Checked with a few games, this seems to work fine."
1102                                 
1103                                 Gameblabla additional notes :
1104                                 This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error.
1105                         */
1106                         CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1107
1108                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1109                         start_rotating = 1;
1110                         break;
1111                 case CdlSync:
1112                 default:
1113                         CDR_LOG_I("Invalid command: %02x\n", Irq);
1114                         error = ERROR_INVALIDCMD;
1115                         // FALLTHROUGH
1116
1117                 set_error:
1118                         SetResultSize(2);
1119                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1120                         cdr.Result[1] = error;
1121                         cdr.Stat = DiskError;
1122                         break;
1123         }
1124
1125         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1126                 cdr.DriveState = DRIVESTATE_STANDBY;
1127                 cdr.StatP |= STATUS_ROTATING;
1128         }
1129
1130         if (!no_busy_error) {
1131                 switch (cdr.DriveState) {
1132                 case DRIVESTATE_LID_OPEN:
1133                 case DRIVESTATE_RESCAN_CD:
1134                 case DRIVESTATE_PREPARE_CD:
1135                         SetResultSize(2);
1136                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1137                         cdr.Result[1] = ERROR_NOTREADY;
1138                         cdr.Stat = DiskError;
1139                         break;
1140                 }
1141         }
1142
1143 finish:
1144         setIrq(Irq);
1145         cdr.ParamC = 0;
1146 }
1147
1148 #ifdef HAVE_ARMV7
1149  #define ssat32_to_16(v) \
1150   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1151 #else
1152  #define ssat32_to_16(v) do { \
1153   if (v < -32768) v = -32768; \
1154   else if (v > 32767) v = 32767; \
1155  } while (0)
1156 #endif
1157
1158 void cdrAttenuate(s16 *buf, int samples, int stereo)
1159 {
1160         int i, l, r;
1161         int ll = cdr.AttenuatorLeftToLeft;
1162         int lr = cdr.AttenuatorLeftToRight;
1163         int rl = cdr.AttenuatorRightToLeft;
1164         int rr = cdr.AttenuatorRightToRight;
1165
1166         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1167                 return;
1168
1169         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1170                 return;
1171
1172         if (stereo) {
1173                 for (i = 0; i < samples; i++) {
1174                         l = buf[i * 2];
1175                         r = buf[i * 2 + 1];
1176                         l = (l * ll + r * rl) >> 7;
1177                         r = (r * rr + l * lr) >> 7;
1178                         ssat32_to_16(l);
1179                         ssat32_to_16(r);
1180                         buf[i * 2] = l;
1181                         buf[i * 2 + 1] = r;
1182                 }
1183         }
1184         else {
1185                 for (i = 0; i < samples; i++) {
1186                         l = buf[i];
1187                         l = l * (ll + rl) >> 7;
1188                         //r = r * (rr + lr) >> 7;
1189                         ssat32_to_16(l);
1190                         //ssat32_to_16(r);
1191                         buf[i] = l;
1192                 }
1193         }
1194 }
1195
1196 void cdrReadInterrupt() {
1197         u8 *buf;
1198
1199         if (!cdr.Reading)
1200                 return;
1201
1202         if (cdr.Irq || cdr.Stat) {
1203                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1204                 CDREAD_INT(2048);
1205                 return;
1206         }
1207
1208         cdr.OCUP = 1;
1209         SetResultSize(1);
1210         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1211         cdr.Result[0] = cdr.StatP;
1212
1213         ReadTrack(cdr.SetSectorPlay);
1214
1215         buf = CDR_getBuffer();
1216         if (buf == NULL)
1217                 cdr.NoErr = 0;
1218
1219         if (!cdr.NoErr) {
1220                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1221                 memset(cdr.Transfer, 0, DATA_SIZE);
1222                 cdr.Stat = DiskError;
1223                 cdr.Result[0] |= STATUS_ERROR;
1224                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1225                 return;
1226         }
1227
1228         memcpy(cdr.Transfer, buf, DATA_SIZE);
1229         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1230
1231
1232         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1233
1234         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1235                 // Firemen 2: Multi-XA files - briefings, cutscenes
1236                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1237                         cdr.File = cdr.Transfer[4 + 0];
1238                         cdr.Channel = cdr.Transfer[4 + 1];
1239                 }
1240
1241                 /* Gameblabla 
1242                  * Skips playing on channel 255.
1243                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1244                  * TODO : Check if this is the proper behaviour.
1245                  * */
1246                 if((cdr.Transfer[4 + 2] & 0x4) &&
1247                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1248                          (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1249                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1250                         if (!ret) {
1251                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1252                                 SPU_playADPCMchannel(&cdr.Xa);
1253                                 cdr.FirstSector = 0;
1254                         }
1255                         else cdr.FirstSector = -1;
1256                 }
1257         }
1258
1259         cdr.SetSectorPlay[2]++;
1260         if (cdr.SetSectorPlay[2] == 75) {
1261                 cdr.SetSectorPlay[2] = 0;
1262                 cdr.SetSectorPlay[1]++;
1263                 if (cdr.SetSectorPlay[1] == 60) {
1264                         cdr.SetSectorPlay[1] = 0;
1265                         cdr.SetSectorPlay[0]++;
1266                 }
1267         }
1268
1269         cdr.Readed = 0;
1270
1271         CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1272
1273         /*
1274         Croc 2: $40 - only FORM1 (*)
1275         Judge Dredd: $C8 - only FORM1 (*)
1276         Sim Theme Park - no adpcm at all (zero)
1277         */
1278
1279         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1280                 cdr.Stat = DataReady;
1281                 setIrq(0x203);
1282         }
1283
1284         // update for CdlGetlocP
1285         ReadTrack(cdr.SetSectorPlay);
1286 }
1287
1288 /*
1289 cdrRead0:
1290         bit 0,1 - mode
1291         bit 2 - unknown
1292         bit 3 - unknown
1293         bit 4 - unknown
1294         bit 5 - 1 result ready
1295         bit 6 - 1 dma ready
1296         bit 7 - 1 command being processed
1297 */
1298
1299 unsigned char cdrRead0(void) {
1300         if (cdr.ResultReady)
1301                 cdr.Ctrl |= 0x20;
1302         else
1303                 cdr.Ctrl &= ~0x20;
1304
1305         if (cdr.OCUP)
1306                 cdr.Ctrl |= 0x40;
1307 //  else
1308 //              cdr.Ctrl &= ~0x40;
1309
1310         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1311         cdr.Ctrl |= 0x18;
1312
1313         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1314
1315         return psxHu8(0x1800) = cdr.Ctrl;
1316 }
1317
1318 void cdrWrite0(unsigned char rt) {
1319         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1320
1321         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1322 }
1323
1324 unsigned char cdrRead1(void) {
1325         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1326                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1327         else
1328                 psxHu8(0x1801) = 0;
1329         cdr.ResultP++;
1330         if (cdr.ResultP == cdr.ResultC)
1331                 cdr.ResultReady = 0;
1332
1333         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1334
1335         return psxHu8(0x1801);
1336 }
1337
1338 void cdrWrite1(unsigned char rt) {
1339         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1340         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1341
1342         switch (cdr.Ctrl & 3) {
1343         case 0:
1344                 break;
1345         case 3:
1346                 cdr.AttenuatorRightToRightT = rt;
1347                 return;
1348         default:
1349                 return;
1350         }
1351
1352         cdr.Cmd = rt;
1353         cdr.OCUP = 0;
1354
1355 #ifdef CDR_LOG_CMD_IRQ
1356         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1357         if (cdr.ParamC) {
1358                 int i;
1359                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1360                 for (i = 0; i < cdr.ParamC; i++)
1361                         SysPrintf(" %x,", cdr.Param[i]);
1362                 SysPrintf("}\n");
1363         } else {
1364                 SysPrintf("\n");
1365         }
1366 #endif
1367
1368         cdr.ResultReady = 0;
1369         cdr.Ctrl |= 0x80;
1370         // cdr.Stat = NoIntr; 
1371         AddIrqQueue(cdr.Cmd, 0x800);
1372
1373         switch (cdr.Cmd) {
1374         case CdlSetmode:
1375                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1376
1377                 cdr.Mode = cdr.Param[0];
1378                 break;
1379         }
1380 }
1381
1382 unsigned char cdrRead2(void) {
1383         unsigned char ret;
1384
1385         if (cdr.Readed == 0) {
1386                 ret = 0;
1387         } else {
1388                 ret = *pTransfer++;
1389         }
1390
1391         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1392         return ret;
1393 }
1394
1395 void cdrWrite2(unsigned char rt) {
1396         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1397         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1398
1399         switch (cdr.Ctrl & 3) {
1400         case 0:
1401                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1402                         cdr.Param[cdr.ParamC++] = rt;
1403                 return;
1404         case 1:
1405                 cdr.Reg2 = rt;
1406                 setIrq(0x204);
1407                 return;
1408         case 2:
1409                 cdr.AttenuatorLeftToLeftT = rt;
1410                 return;
1411         case 3:
1412                 cdr.AttenuatorRightToLeftT = rt;
1413                 return;
1414         }
1415 }
1416
1417 unsigned char cdrRead3(void) {
1418         if (cdr.Ctrl & 0x1)
1419                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1420         else
1421                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1422
1423         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1424         return psxHu8(0x1803);
1425 }
1426
1427 void cdrWrite3(unsigned char rt) {
1428         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1429         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1430
1431         switch (cdr.Ctrl & 3) {
1432         case 0:
1433                 break; // transfer
1434         case 1:
1435                 cdr.Stat &= ~rt;
1436
1437                 if (rt & 0x40)
1438                         cdr.ParamC = 0;
1439                 return;
1440         case 2:
1441                 cdr.AttenuatorLeftToRightT = rt;
1442                 return;
1443         case 3:
1444                 if (rt & 0x20) {
1445                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1446                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1447                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1448                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1449                 }
1450                 return;
1451         }
1452
1453         if ((rt & 0x80) && cdr.Readed == 0) {
1454                 cdr.Readed = 1;
1455                 pTransfer = cdr.Transfer;
1456
1457                 switch (cdr.Mode & 0x30) {
1458                         case MODE_SIZE_2328:
1459                         case 0x00:
1460                                 pTransfer += 12;
1461                                 break;
1462
1463                         case MODE_SIZE_2340:
1464                                 pTransfer += 0;
1465                                 break;
1466
1467                         default:
1468                                 break;
1469                 }
1470         }
1471 }
1472
1473 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1474         u32 cdsize;
1475         int size;
1476         u8 *ptr;
1477
1478         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1479
1480         switch (chcr) {
1481                 case 0x11000000:
1482                 case 0x11400100:
1483                         if (cdr.Readed == 0) {
1484                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1485                                 break;
1486                         }
1487
1488                         cdsize = (bcr & 0xffff) * 4;
1489
1490                         // Ape Escape: bcr = 0001 / 0000
1491                         // - fix boot
1492                         if( cdsize == 0 )
1493                         {
1494                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1495                                         case MODE_SIZE_2340: cdsize = 2340; break;
1496                                         case MODE_SIZE_2328: cdsize = 2328; break;
1497                                         default:
1498                                         case MODE_SIZE_2048: cdsize = 2048; break;
1499                                 }
1500                         }
1501
1502
1503                         ptr = (u8 *)PSXM(madr);
1504                         if (ptr == NULL) {
1505                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1506                                 break;
1507                         }
1508
1509                         /*
1510                         GS CDX: Enhancement CD crash
1511                         - Setloc 0:0:0
1512                         - CdlPlay
1513                         - Spams DMA3 and gets buffer overrun
1514                         */
1515                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1516                         if (size > cdsize)
1517                                 size = cdsize;
1518                         if (size > 0)
1519                         {
1520                                 memcpy(ptr, pTransfer, size);
1521                         }
1522
1523                         psxCpu->Clear(madr, cdsize / 4);
1524                         pTransfer += cdsize;
1525
1526                         if( chcr == 0x11400100 ) {
1527                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1528                                 CDRDMA_INT( (cdsize/4) / 4 );
1529                         }
1530                         else if( chcr == 0x11000000 ) {
1531                                 // CDRDMA_INT( (cdsize/4) * 1 );
1532                                 // halted
1533                                 psxRegs.cycle += (cdsize/4) * 24/2;
1534                                 CDRDMA_INT(16);
1535                         }
1536                         return;
1537
1538                 default:
1539                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1540                         break;
1541         }
1542
1543         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1544         DMA_INTERRUPT(3);
1545 }
1546
1547 void cdrDmaInterrupt()
1548 {
1549         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1550         {
1551                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1552                 DMA_INTERRUPT(3);
1553         }
1554 }
1555
1556 static void getCdInfo(void)
1557 {
1558         u8 tmp;
1559
1560         CDR_getTN(cdr.ResultTN);
1561         CDR_getTD(0, cdr.SetSectorEnd);
1562         tmp = cdr.SetSectorEnd[0];
1563         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1564         cdr.SetSectorEnd[2] = tmp;
1565 }
1566
1567 void cdrReset() {
1568         memset(&cdr, 0, sizeof(cdr));
1569         cdr.CurTrack = 1;
1570         cdr.File = 1;
1571         cdr.Channel = 1;
1572         cdr.Reg2 = 0x1f;
1573         cdr.Stat = NoIntr;
1574         cdr.DriveState = DRIVESTATE_STANDBY;
1575         cdr.StatP = STATUS_ROTATING;
1576         pTransfer = cdr.Transfer;
1577         
1578         // BIOS player - default values
1579         cdr.AttenuatorLeftToLeft = 0x80;
1580         cdr.AttenuatorLeftToRight = 0x00;
1581         cdr.AttenuatorRightToLeft = 0x00;
1582         cdr.AttenuatorRightToRight = 0x80;
1583
1584         getCdInfo();
1585 }
1586
1587 int cdrFreeze(void *f, int Mode) {
1588         u32 tmp;
1589         u8 tmpp[3];
1590
1591         if (Mode == 0 && !Config.Cdda)
1592                 CDR_stop();
1593         
1594         cdr.freeze_ver = 0x63647202;
1595         gzfreeze(&cdr, sizeof(cdr));
1596         
1597         if (Mode == 1) {
1598                 cdr.ParamP = cdr.ParamC;
1599                 tmp = pTransfer - cdr.Transfer;
1600         }
1601
1602         gzfreeze(&tmp, sizeof(tmp));
1603
1604         if (Mode == 0) {
1605                 getCdInfo();
1606
1607                 pTransfer = cdr.Transfer + tmp;
1608
1609                 // read right sub data
1610                 tmpp[0] = btoi(cdr.Prev[0]);
1611                 tmpp[1] = btoi(cdr.Prev[1]);
1612                 tmpp[2] = btoi(cdr.Prev[2]);
1613                 cdr.Prev[0]++;
1614                 ReadTrack(tmpp);
1615
1616                 if (cdr.Play) {
1617                         if (cdr.freeze_ver < 0x63647202)
1618                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1619
1620                         Find_CurTrack(cdr.SetSectorPlay);
1621                         if (!Config.Cdda)
1622                                 CDR_play(cdr.SetSectorPlay);
1623                 }
1624
1625                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1626                         // old versions did not latch Reg2, have to fixup..
1627                         if (cdr.Reg2 == 0) {
1628                                 SysPrintf("cdrom: fixing up old savestate\n");
1629                                 cdr.Reg2 = 7;
1630                         }
1631                         // also did not save Attenuator..
1632                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1633                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1634                         {
1635                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1636                         }
1637                 }
1638         }
1639
1640         return 0;
1641 }
1642
1643 void LidInterrupt() {
1644         getCdInfo();
1645         cdrLidSeekInterrupt();
1646 }