spu: sync on xa playback start
[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 // cdrPlaySeekReadInterrupt
245 #define CDRPLAYSEEKREAD_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 #define StopReading() { \
261         cdr.Reading = 0; \
262         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
263 }
264
265 #define StopCdda() { \
266         if (cdr.Play && !Config.Cdda) CDR_stop(); \
267         cdr.Play = FALSE; \
268         cdr.FastForward = 0; \
269         cdr.FastBackward = 0; \
270 }
271
272 #define SetPlaySeekRead(x, f) { \
273         x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
274         x |= f; \
275 }
276
277 #define SetResultSize(size) { \
278         cdr.ResultP = 0; \
279         cdr.ResultC = size; \
280         cdr.ResultReady = 1; \
281 }
282
283 static void setIrq(int log_cmd)
284 {
285         if (cdr.Stat & cdr.Reg2)
286                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
287
288 #ifdef CDR_LOG_CMD_IRQ
289         {
290                 int i;
291                 SysPrintf("CDR IRQ=%d cmd %02x stat %02x: ",
292                         !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
293                 for (i = 0; i < cdr.ResultC; i++)
294                         SysPrintf("%02x ", cdr.Result[i]);
295                 SysPrintf("\n");
296         }
297 #endif
298 }
299
300 // timing used in this function was taken from tests on real hardware
301 // (yes it's slow, but you probably don't want to modify it)
302 void cdrLidSeekInterrupt(void)
303 {
304         switch (cdr.DriveState) {
305         default:
306         case DRIVESTATE_STANDBY:
307                 StopCdda();
308                 StopReading();
309                 SetPlaySeekRead(cdr.StatP, 0);
310
311                 if (CDR_getStatus(&stat) == -1)
312                         return;
313
314                 if (stat.Status & STATUS_SHELLOPEN)
315                 {
316                         cdr.DriveState = DRIVESTATE_LID_OPEN;
317                         CDRLID_INT(0x800);
318                 }
319                 break;
320
321         case DRIVESTATE_LID_OPEN:
322                 if (CDR_getStatus(&stat) == -1)
323                         stat.Status &= ~STATUS_SHELLOPEN;
324
325                 // 02, 12, 10
326                 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
327                         cdr.StatP |= STATUS_SHELLOPEN;
328
329                         // could generate error irq here, but real hardware
330                         // only sometimes does that
331                         // (not done when lots of commands are sent?)
332
333                         CDRLID_INT(cdReadTime * 30);
334                         break;
335                 }
336                 else if (cdr.StatP & STATUS_ROTATING) {
337                         cdr.StatP &= ~STATUS_ROTATING;
338                 }
339                 else if (!(stat.Status & STATUS_SHELLOPEN)) {
340                         // closed now
341                         CheckCdrom();
342
343                         // cdr.StatP STATUS_SHELLOPEN is "sticky"
344                         // and is only cleared by CdlNop
345
346                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
347                         CDRLID_INT(cdReadTime * 105);
348                         break;
349                 }
350
351                 // recheck for close
352                 CDRLID_INT(cdReadTime * 3);
353                 break;
354
355         case DRIVESTATE_RESCAN_CD:
356                 cdr.StatP |= STATUS_ROTATING;
357                 cdr.DriveState = DRIVESTATE_PREPARE_CD;
358
359                 // this is very long on real hardware, over 6 seconds
360                 // make it a bit faster here...
361                 CDRLID_INT(cdReadTime * 150);
362                 break;
363
364         case DRIVESTATE_PREPARE_CD:
365                 cdr.StatP |= STATUS_SEEK;
366
367                 cdr.DriveState = DRIVESTATE_STANDBY;
368                 CDRLID_INT(cdReadTime * 26);
369                 break;
370         }
371 }
372
373 static void Find_CurTrack(const u8 *time)
374 {
375         int current, sect;
376
377         current = msf2sec(time);
378
379         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
380                 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
381                 sect = fsm2sec(cdr.ResultTD);
382                 if (sect - current >= 150)
383                         break;
384         }
385 }
386
387 static void generate_subq(const u8 *time)
388 {
389         unsigned char start[3], next[3];
390         unsigned int this_s, start_s, next_s, pregap;
391         int relative_s;
392
393         CDR_getTD(cdr.CurTrack, start);
394         if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
395                 pregap = 150;
396                 CDR_getTD(cdr.CurTrack + 1, next);
397         }
398         else {
399                 // last track - cd size
400                 pregap = 0;
401                 next[0] = cdr.SetSectorEnd[2];
402                 next[1] = cdr.SetSectorEnd[1];
403                 next[2] = cdr.SetSectorEnd[0];
404         }
405
406         this_s = msf2sec(time);
407         start_s = fsm2sec(start);
408         next_s = fsm2sec(next);
409
410         cdr.TrackChanged = FALSE;
411
412         if (next_s - this_s < pregap) {
413                 cdr.TrackChanged = TRUE;
414                 cdr.CurTrack++;
415                 start_s = next_s;
416         }
417
418         cdr.subq.Index = 1;
419
420         relative_s = this_s - start_s;
421         if (relative_s < 0) {
422                 cdr.subq.Index = 0;
423                 relative_s = -relative_s;
424         }
425         sec2msf(relative_s, cdr.subq.Relative);
426
427         cdr.subq.Track = itob(cdr.CurTrack);
428         cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
429         cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
430         cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
431         cdr.subq.Absolute[0] = itob(time[0]);
432         cdr.subq.Absolute[1] = itob(time[1]);
433         cdr.subq.Absolute[2] = itob(time[2]);
434 }
435
436 static void ReadTrack(const u8 *time) {
437         unsigned char tmp[3];
438         struct SubQ *subq;
439         u16 crc;
440
441         tmp[0] = itob(time[0]);
442         tmp[1] = itob(time[1]);
443         tmp[2] = itob(time[2]);
444
445         if (memcmp(cdr.Prev, tmp, 3) == 0)
446                 return;
447
448         CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
449
450         cdr.NoErr = CDR_readTrack(tmp);
451         memcpy(cdr.Prev, tmp, 3);
452
453         if (CheckSBI(time))
454                 return;
455
456         subq = (struct SubQ *)CDR_getBufferSub();
457         if (subq != NULL && cdr.CurTrack == 1) {
458                 crc = calcCrc((u8 *)subq + 12, 10);
459                 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
460                         cdr.subq.Track = subq->TrackNumber;
461                         cdr.subq.Index = subq->IndexNumber;
462                         memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
463                         memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
464                 }
465                 else {
466                         CDR_LOG_I("subq bad crc @%02x:%02x:%02x\n",
467                                 tmp[0], tmp[1], tmp[2]);
468                 }
469         }
470         else {
471                 generate_subq(time);
472         }
473
474         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
475                 cdr.subq.Track, cdr.subq.Index,
476                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
477                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
478 }
479
480 static void AddIrqQueue(unsigned short irq, unsigned long ecycle) {
481         if (cdr.Irq != 0) {
482                 if (irq == cdr.Irq || irq + 0x100 == cdr.Irq) {
483                         cdr.IrqRepeated = 1;
484                         CDR_INT(ecycle);
485                         return;
486                 }
487
488                 CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
489         }
490
491         cdr.Irq = irq;
492         cdr.eCycle = ecycle;
493
494         CDR_INT(ecycle);
495 }
496
497 static void cdrPlayInterrupt_Autopause()
498 {
499         u32 abs_lev_max = 0;
500         boolean abs_lev_chselect;
501         u32 i;
502
503         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
504                 CDR_LOG( "CDDA STOP\n" );
505
506                 // Magic the Gathering
507                 // - looping territory cdda
508
509                 // ...?
510                 //cdr.ResultReady = 1;
511                 //cdr.Stat = DataReady;
512                 cdr.Stat = DataEnd;
513                 setIrq(0x200);
514
515                 StopCdda();
516                 SetPlaySeekRead(cdr.StatP, 0);
517         }
518         else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
519                 cdr.Result[0] = cdr.StatP;
520                 cdr.Result[1] = cdr.subq.Track;
521                 cdr.Result[2] = cdr.subq.Index;
522                 
523                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
524                 
525                 /* 8 is a hack. For accuracy, it should be 588. */
526                 for (i = 0; i < 8; i++)
527                 {
528                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
529                 }
530                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
531                 abs_lev_max |= abs_lev_chselect << 15;
532
533                 if (cdr.subq.Absolute[2] & 0x10) {
534                         cdr.Result[3] = cdr.subq.Relative[0];
535                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
536                         cdr.Result[5] = cdr.subq.Relative[2];
537                 }
538                 else {
539                         cdr.Result[3] = cdr.subq.Absolute[0];
540                         cdr.Result[4] = cdr.subq.Absolute[1];
541                         cdr.Result[5] = cdr.subq.Absolute[2];
542                 }
543
544                 cdr.Result[6] = abs_lev_max >> 0;
545                 cdr.Result[7] = abs_lev_max >> 8;
546
547                 // Rayman: Logo freeze (resultready + dataready)
548                 cdr.ResultReady = 1;
549                 cdr.Stat = DataReady;
550
551                 SetResultSize(8);
552                 setIrq(0x201);
553         }
554 }
555
556 static int cdrSeekTime(unsigned char *target)
557 {
558         int seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(target)) * (cdReadTime / 200);
559         /*
560         * Gameblabla :
561         * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
562         * and was unreliable for that game.
563         * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
564         *
565         * Obviously, this isn't perfect but right now, it should be a bit better.
566         * Games to test this against if you change that setting :
567         * - Driver (titlescreen music delay and retry mission)
568         * - Worms Pinball (Will either not boot or crash in the memory card screen)
569         * - Viewpoint (short pauses if the delay in the ingame music is too long)
570         *
571         * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
572         * However, 1000000 is not enough for Worms Pinball to reliably boot.
573         */
574         if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
575         CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
576         return seekTime;
577 }
578
579 static void cdrReadInterrupt(void);
580
581 void cdrPlaySeekReadInterrupt(void)
582 {
583         if (cdr.Reading) {
584                 cdrReadInterrupt();
585                 return;
586         }
587
588         if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
589                 if (cdr.Stat) {
590                         CDR_LOG_I("cdrom: seek stat hack\n");
591                         CDRPLAYSEEKREAD_INT(0x1000);
592                         return;
593                 }
594                 SetResultSize(1);
595                 cdr.StatP |= STATUS_ROTATING;
596                 SetPlaySeekRead(cdr.StatP, 0);
597                 cdr.Result[0] = cdr.StatP;
598                 if (cdr.Irq == 0) {
599                         cdr.Stat = Complete;
600                         setIrq(0x202);
601                 }
602
603                 Find_CurTrack(cdr.SetSectorPlay);
604                 ReadTrack(cdr.SetSectorPlay);
605                 cdr.TrackChanged = FALSE;
606                 return;
607         }
608
609         if (!cdr.Play) return;
610
611         CDR_LOG( "CDDA - %d:%d:%d\n",
612                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
613
614         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
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.Muted && !Config.Cdda) {
628                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
629                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
630                 cdr.FirstSector = 0;
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         CDRPLAYSEEKREAD_INT(cdReadTime);
644
645         // update for CdlGetlocP/autopause
646         generate_subq(cdr.SetSectorPlay);
647 }
648
649 void cdrInterrupt(void) {
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                         cdr.FirstSector = 1;
760
761                         if (!Config.Cdda)
762                                 CDR_play(cdr.SetSectorPlay);
763
764                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
765                         
766                         // BIOS player - set flag again
767                         cdr.Play = TRUE;
768
769                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
770                         start_rotating = 1;
771                         break;
772
773                 case CdlForward:
774                         // TODO: error 80 if stopped
775                         cdr.Stat = Complete;
776
777                         // GameShark CD Player: Calls 2x + Play 2x
778                         cdr.FastForward = 1;
779                         cdr.FastBackward = 0;
780                         break;
781
782                 case CdlBackward:
783                         cdr.Stat = Complete;
784
785                         // GameShark CD Player: Calls 2x + Play 2x
786                         cdr.FastBackward = 1;
787                         cdr.FastForward = 0;
788                         break;
789
790                 case CdlStandby:
791                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
792                                 error = ERROR_INVALIDARG;
793                                 goto set_error;
794                         }
795                         AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
796                         start_rotating = 1;
797                         break;
798
799                 case CdlStandby + 0x100:
800                         cdr.Stat = Complete;
801                         break;
802
803                 case CdlStop:
804                         if (cdr.Play) {
805                                 // grab time for current track
806                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
807
808                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
809                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
810                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
811                         }
812
813                         StopCdda();
814                         StopReading();
815                         SetPlaySeekRead(cdr.StatP, 0);
816                         cdr.StatP &= ~STATUS_ROTATING;
817
818                         delay = 0x800;
819                         if (cdr.DriveState == DRIVESTATE_STANDBY)
820                                 delay = cdReadTime * 30 / 2;
821
822                         cdr.DriveState = DRIVESTATE_STOPPED;
823                         AddIrqQueue(CdlStop + 0x100, delay);
824                         break;
825
826                 case CdlStop + 0x100:
827                         cdr.Stat = Complete;
828                         break;
829
830                 case CdlPause:
831                         StopCdda();
832                         StopReading();
833                         /*
834                         Gundam Battle Assault 2: much slower (*)
835                         - Fixes boot, gameplay
836
837                         Hokuto no Ken 2: slower
838                         - Fixes intro + subtitles
839
840                         InuYasha - Feudal Fairy Tale: slower
841                         - Fixes battles
842                         */
843                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
844                          * The timings from Duckstation are based upon hardware tests.
845                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
846                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
847                          * */
848                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
849                         {
850                                 delay = 7000;
851                         }
852                         else
853                         {
854                                 delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
855                         }
856                         AddIrqQueue(CdlPause + 0x100, delay);
857                         SetPlaySeekRead(cdr.StatP, 0);
858                         cdr.Ctrl |= 0x80;
859                         break;
860
861                 case CdlPause + 0x100:
862                         cdr.Stat = Complete;
863                         break;
864
865                 case CdlReset:
866                         StopCdda();
867                         StopReading();
868                         SetPlaySeekRead(cdr.StatP, 0);
869                         cdr.Muted = FALSE;
870                         cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
871                         AddIrqQueue(CdlReset + 0x100, 4100000);
872                         no_busy_error = 1;
873                         start_rotating = 1;
874                         break;
875
876                 case CdlReset + 0x100:
877                         cdr.Stat = Complete;
878                         break;
879
880                 case CdlMute:
881                         cdr.Muted = TRUE;
882                         break;
883
884                 case CdlDemute:
885                         cdr.Muted = FALSE;
886                         break;
887
888                 case CdlSetfilter:
889                         cdr.File = cdr.Param[0];
890                         cdr.Channel = cdr.Param[1];
891                         break;
892
893                 case CdlSetmode:
894                         no_busy_error = 1;
895                         break;
896
897                 case CdlGetparam:
898                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
899                         SetResultSize(5);
900                         cdr.Result[1] = cdr.Mode;
901                         cdr.Result[2] = 0;
902                         cdr.Result[3] = cdr.File;
903                         cdr.Result[4] = cdr.Channel;
904                         no_busy_error = 1;
905                         break;
906
907                 case CdlGetlocL:
908                         SetResultSize(8);
909                         memcpy(cdr.Result, cdr.Transfer, 8);
910                         break;
911
912                 case CdlGetlocP:
913                         SetResultSize(8);
914                         memcpy(&cdr.Result, &cdr.subq, 8);
915                         break;
916
917                 case CdlReadT: // SetSession?
918                         // really long
919                         AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
920                         start_rotating = 1;
921                         break;
922
923                 case CdlReadT + 0x100:
924                         cdr.Stat = Complete;
925                         break;
926
927                 case CdlGetTN:
928                         SetResultSize(3);
929                         if (CDR_getTN(cdr.ResultTN) == -1) {
930                                 cdr.Stat = DiskError;
931                                 cdr.Result[0] |= STATUS_ERROR;
932                         } else {
933                                 cdr.Stat = Acknowledge;
934                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
935                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
936                         }
937                         break;
938
939                 case CdlGetTD:
940                         cdr.Track = btoi(cdr.Param[0]);
941                         SetResultSize(4);
942                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
943                                 cdr.Stat = DiskError;
944                                 cdr.Result[0] |= STATUS_ERROR;
945                         } else {
946                                 cdr.Stat = Acknowledge;
947                                 cdr.Result[0] = cdr.StatP;
948                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
949                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
950                                 /* According to Nocash's documentation, the function doesn't care about ff.
951                                  * This can be seen also in Mednafen's implementation. */
952                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
953                         }
954                         break;
955
956                 case CdlSeekL:
957                 case CdlSeekP:
958                         StopCdda();
959                         StopReading();
960                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
961
962                         seekTime = cdrSeekTime(cdr.SetSector);
963                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
964                         /*
965                         Crusaders of Might and Magic = 0.5x-4x
966                         - fix cutscene speech start
967
968                         Eggs of Steel = 2x-?
969                         - fix new game
970
971                         Medievil = ?-4x
972                         - fix cutscene speech
973
974                         Rockman X5 = 0.5-4x
975                         - fix capcom logo
976                         */
977                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
978                         start_rotating = 1;
979                         break;
980
981                 case CdlTest:
982                         switch (cdr.Param[0]) {
983                                 case 0x20: // System Controller ROM Version
984                                         SetResultSize(4);
985                                         memcpy(cdr.Result, Test20, 4);
986                                         break;
987                                 case 0x22:
988                                         SetResultSize(8);
989                                         memcpy(cdr.Result, Test22, 4);
990                                         break;
991                                 case 0x23: case 0x24:
992                                         SetResultSize(8);
993                                         memcpy(cdr.Result, Test23, 4);
994                                         break;
995                         }
996                         no_busy_error = 1;
997                         break;
998
999                 case CdlID:
1000                         AddIrqQueue(CdlID + 0x100, 20480);
1001                         break;
1002
1003                 case CdlID + 0x100:
1004                         SetResultSize(8);
1005                         cdr.Result[0] = cdr.StatP;
1006                         cdr.Result[1] = 0;
1007                         cdr.Result[2] = 0;
1008                         cdr.Result[3] = 0;
1009
1010                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1011                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1012                                 cdr.Result[1] = 0xc0;
1013                         }
1014                         else {
1015                                 if (stat.Type == 2)
1016                                         cdr.Result[1] |= 0x10;
1017                                 if (CdromId[0] == '\0')
1018                                         cdr.Result[1] |= 0x80;
1019                         }
1020                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1021
1022                         /* This adds the string "PCSX" in Playstation bios boot screen */
1023                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1024                         cdr.Stat = Complete;
1025                         break;
1026
1027                 case CdlInit:
1028                         StopCdda();
1029                         StopReading();
1030                         SetPlaySeekRead(cdr.StatP, 0);
1031                         // yes, it really sets STATUS_SHELLOPEN
1032                         cdr.StatP |= STATUS_SHELLOPEN;
1033                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1034                         CDRLID_INT(20480);
1035                         no_busy_error = 1;
1036                         start_rotating = 1;
1037                         break;
1038
1039                 case CdlGetQ:
1040                         no_busy_error = 1;
1041                         break;
1042
1043                 case CdlReadToc:
1044                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1045                         no_busy_error = 1;
1046                         start_rotating = 1;
1047                         break;
1048
1049                 case CdlReadToc + 0x100:
1050                         cdr.Stat = Complete;
1051                         no_busy_error = 1;
1052                         break;
1053
1054                 case CdlReadN:
1055                 case CdlReadS:
1056                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1057
1058                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1059                                 // Read* acts as play for cdda tracks in cdda mode
1060                                 goto do_CdlPlay;
1061
1062                         StopCdda();
1063                         if (cdr.SetlocPending) {
1064                                 seekTime = cdrSeekTime(cdr.SetSector);
1065                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1066                                 cdr.SetlocPending = 0;
1067                         }
1068                         cdr.Reading = 1;
1069                         cdr.FirstSector = 1;
1070
1071                         // Fighting Force 2 - update subq time immediately
1072                         // - fixes new game
1073                         ReadTrack(cdr.SetSectorPlay);
1074
1075                         CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1076
1077                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1078                         start_rotating = 1;
1079                         break;
1080                 case CdlSync:
1081                 default:
1082                         CDR_LOG_I("Invalid command: %02x\n", Irq);
1083                         error = ERROR_INVALIDCMD;
1084                         // FALLTHROUGH
1085
1086                 set_error:
1087                         SetResultSize(2);
1088                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1089                         cdr.Result[1] = error;
1090                         cdr.Stat = DiskError;
1091                         break;
1092         }
1093
1094         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1095                 cdr.DriveState = DRIVESTATE_STANDBY;
1096                 cdr.StatP |= STATUS_ROTATING;
1097         }
1098
1099         if (!no_busy_error) {
1100                 switch (cdr.DriveState) {
1101                 case DRIVESTATE_LID_OPEN:
1102                 case DRIVESTATE_RESCAN_CD:
1103                 case DRIVESTATE_PREPARE_CD:
1104                         SetResultSize(2);
1105                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1106                         cdr.Result[1] = ERROR_NOTREADY;
1107                         cdr.Stat = DiskError;
1108                         break;
1109                 }
1110         }
1111
1112 finish:
1113         setIrq(Irq);
1114         cdr.ParamC = 0;
1115 }
1116
1117 #ifdef HAVE_ARMV7
1118  #define ssat32_to_16(v) \
1119   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1120 #else
1121  #define ssat32_to_16(v) do { \
1122   if (v < -32768) v = -32768; \
1123   else if (v > 32767) v = 32767; \
1124  } while (0)
1125 #endif
1126
1127 void cdrAttenuate(s16 *buf, int samples, int stereo)
1128 {
1129         int i, l, r;
1130         int ll = cdr.AttenuatorLeftToLeft;
1131         int lr = cdr.AttenuatorLeftToRight;
1132         int rl = cdr.AttenuatorRightToLeft;
1133         int rr = cdr.AttenuatorRightToRight;
1134
1135         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1136                 return;
1137
1138         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1139                 return;
1140
1141         if (stereo) {
1142                 for (i = 0; i < samples; i++) {
1143                         l = buf[i * 2];
1144                         r = buf[i * 2 + 1];
1145                         l = (l * ll + r * rl) >> 7;
1146                         r = (r * rr + l * lr) >> 7;
1147                         ssat32_to_16(l);
1148                         ssat32_to_16(r);
1149                         buf[i * 2] = l;
1150                         buf[i * 2 + 1] = r;
1151                 }
1152         }
1153         else {
1154                 for (i = 0; i < samples; i++) {
1155                         l = buf[i];
1156                         l = l * (ll + rl) >> 7;
1157                         //r = r * (rr + lr) >> 7;
1158                         ssat32_to_16(l);
1159                         //ssat32_to_16(r);
1160                         buf[i] = l;
1161                 }
1162         }
1163 }
1164
1165 static void cdrReadInterrupt(void)
1166 {
1167         u8 *buf;
1168
1169         if (cdr.Irq || cdr.Stat) {
1170                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1171                 CDRPLAYSEEKREAD_INT(2048);
1172                 return;
1173         }
1174
1175         cdr.OCUP = 1;
1176         SetResultSize(1);
1177         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1178         cdr.Result[0] = cdr.StatP;
1179
1180         ReadTrack(cdr.SetSectorPlay);
1181
1182         buf = CDR_getBuffer();
1183         if (buf == NULL)
1184                 cdr.NoErr = 0;
1185
1186         if (!cdr.NoErr) {
1187                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1188                 memset(cdr.Transfer, 0, DATA_SIZE);
1189                 cdr.Stat = DiskError;
1190                 cdr.Result[0] |= STATUS_ERROR;
1191                 setIrq(0x205);
1192                 return;
1193         }
1194
1195         memcpy(cdr.Transfer, buf, DATA_SIZE);
1196         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1197
1198
1199         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1200
1201         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1202                 // Firemen 2: Multi-XA files - briefings, cutscenes
1203                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1204                         cdr.File = cdr.Transfer[4 + 0];
1205                         cdr.Channel = cdr.Transfer[4 + 1];
1206                 }
1207
1208                 /* Gameblabla 
1209                  * Skips playing on channel 255.
1210                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1211                  * TODO : Check if this is the proper behaviour.
1212                  * */
1213                 if((cdr.Transfer[4 + 2] & 0x4) &&
1214                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1215                          (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1216                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1217                         if (!ret) {
1218                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1219                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1220                                 cdr.FirstSector = 0;
1221                         }
1222                         else cdr.FirstSector = -1;
1223                 }
1224         }
1225
1226         cdr.SetSectorPlay[2]++;
1227         if (cdr.SetSectorPlay[2] == 75) {
1228                 cdr.SetSectorPlay[2] = 0;
1229                 cdr.SetSectorPlay[1]++;
1230                 if (cdr.SetSectorPlay[1] == 60) {
1231                         cdr.SetSectorPlay[1] = 0;
1232                         cdr.SetSectorPlay[0]++;
1233                 }
1234         }
1235
1236         cdr.Readed = 0;
1237
1238         CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1239
1240         /*
1241         Croc 2: $40 - only FORM1 (*)
1242         Judge Dredd: $C8 - only FORM1 (*)
1243         Sim Theme Park - no adpcm at all (zero)
1244         */
1245
1246         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1247                 cdr.Stat = DataReady;
1248                 setIrq(0x203);
1249         }
1250
1251         // update for CdlGetlocP
1252         ReadTrack(cdr.SetSectorPlay);
1253 }
1254
1255 /*
1256 cdrRead0:
1257         bit 0,1 - mode
1258         bit 2 - unknown
1259         bit 3 - unknown
1260         bit 4 - unknown
1261         bit 5 - 1 result ready
1262         bit 6 - 1 dma ready
1263         bit 7 - 1 command being processed
1264 */
1265
1266 unsigned char cdrRead0(void) {
1267         if (cdr.ResultReady)
1268                 cdr.Ctrl |= 0x20;
1269         else
1270                 cdr.Ctrl &= ~0x20;
1271
1272         if (cdr.OCUP)
1273                 cdr.Ctrl |= 0x40;
1274 //  else
1275 //              cdr.Ctrl &= ~0x40;
1276
1277         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1278         cdr.Ctrl |= 0x18;
1279
1280         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1281
1282         return psxHu8(0x1800) = cdr.Ctrl;
1283 }
1284
1285 void cdrWrite0(unsigned char rt) {
1286         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1287
1288         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1289 }
1290
1291 unsigned char cdrRead1(void) {
1292         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1293                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1294         else
1295                 psxHu8(0x1801) = 0;
1296         cdr.ResultP++;
1297         if (cdr.ResultP == cdr.ResultC)
1298                 cdr.ResultReady = 0;
1299
1300         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1301
1302         return psxHu8(0x1801);
1303 }
1304
1305 void cdrWrite1(unsigned char rt) {
1306         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1307         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1308
1309         switch (cdr.Ctrl & 3) {
1310         case 0:
1311                 break;
1312         case 3:
1313                 cdr.AttenuatorRightToRightT = rt;
1314                 return;
1315         default:
1316                 return;
1317         }
1318
1319         cdr.Cmd = rt;
1320         cdr.OCUP = 0;
1321
1322 #ifdef CDR_LOG_CMD_IRQ
1323         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1324         if (cdr.ParamC) {
1325                 int i;
1326                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1327                 for (i = 0; i < cdr.ParamC; i++)
1328                         SysPrintf(" %x,", cdr.Param[i]);
1329                 SysPrintf("}\n");
1330         } else {
1331                 SysPrintf("\n");
1332         }
1333 #endif
1334
1335         cdr.ResultReady = 0;
1336         cdr.Ctrl |= 0x80;
1337         // cdr.Stat = NoIntr; 
1338         AddIrqQueue(cdr.Cmd, 0x800);
1339
1340         switch (cdr.Cmd) {
1341         case CdlSetmode:
1342                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1343
1344                 cdr.Mode = cdr.Param[0];
1345                 break;
1346         }
1347 }
1348
1349 unsigned char cdrRead2(void) {
1350         unsigned char ret;
1351
1352         if (cdr.Readed == 0) {
1353                 ret = 0;
1354         } else {
1355                 ret = *pTransfer++;
1356         }
1357
1358         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1359         return ret;
1360 }
1361
1362 void cdrWrite2(unsigned char rt) {
1363         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1364         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1365
1366         switch (cdr.Ctrl & 3) {
1367         case 0:
1368                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1369                         cdr.Param[cdr.ParamC++] = rt;
1370                 return;
1371         case 1:
1372                 cdr.Reg2 = rt;
1373                 setIrq(0x204);
1374                 return;
1375         case 2:
1376                 cdr.AttenuatorLeftToLeftT = rt;
1377                 return;
1378         case 3:
1379                 cdr.AttenuatorRightToLeftT = rt;
1380                 return;
1381         }
1382 }
1383
1384 unsigned char cdrRead3(void) {
1385         if (cdr.Ctrl & 0x1)
1386                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1387         else
1388                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1389
1390         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1391         return psxHu8(0x1803);
1392 }
1393
1394 void cdrWrite3(unsigned char rt) {
1395         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1396         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1397
1398         switch (cdr.Ctrl & 3) {
1399         case 0:
1400                 break; // transfer
1401         case 1:
1402                 cdr.Stat &= ~rt;
1403
1404                 if (rt & 0x40)
1405                         cdr.ParamC = 0;
1406                 return;
1407         case 2:
1408                 cdr.AttenuatorLeftToRightT = rt;
1409                 return;
1410         case 3:
1411                 if (rt & 0x20) {
1412                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1413                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1414                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1415                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1416                 }
1417                 return;
1418         }
1419
1420         if ((rt & 0x80) && cdr.Readed == 0) {
1421                 cdr.Readed = 1;
1422                 pTransfer = cdr.Transfer;
1423
1424                 switch (cdr.Mode & 0x30) {
1425                         case MODE_SIZE_2328:
1426                         case 0x00:
1427                                 pTransfer += 12;
1428                                 break;
1429
1430                         case MODE_SIZE_2340:
1431                                 pTransfer += 0;
1432                                 break;
1433
1434                         default:
1435                                 break;
1436                 }
1437         }
1438 }
1439
1440 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1441         u32 cdsize;
1442         int size;
1443         u8 *ptr;
1444
1445         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1446
1447         switch (chcr) {
1448                 case 0x11000000:
1449                 case 0x11400100:
1450                         if (cdr.Readed == 0) {
1451                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1452                                 break;
1453                         }
1454
1455                         cdsize = (bcr & 0xffff) * 4;
1456
1457                         // Ape Escape: bcr = 0001 / 0000
1458                         // - fix boot
1459                         if( cdsize == 0 )
1460                         {
1461                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1462                                         case MODE_SIZE_2340: cdsize = 2340; break;
1463                                         case MODE_SIZE_2328: cdsize = 2328; break;
1464                                         default:
1465                                         case MODE_SIZE_2048: cdsize = 2048; break;
1466                                 }
1467                         }
1468
1469
1470                         ptr = (u8 *)PSXM(madr);
1471                         if (ptr == NULL) {
1472                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1473                                 break;
1474                         }
1475
1476                         /*
1477                         GS CDX: Enhancement CD crash
1478                         - Setloc 0:0:0
1479                         - CdlPlay
1480                         - Spams DMA3 and gets buffer overrun
1481                         */
1482                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1483                         if (size > cdsize)
1484                                 size = cdsize;
1485                         if (size > 0)
1486                         {
1487                                 memcpy(ptr, pTransfer, size);
1488                         }
1489
1490                         psxCpu->Clear(madr, cdsize / 4);
1491                         pTransfer += cdsize;
1492
1493                         if( chcr == 0x11400100 ) {
1494                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1495                                 CDRDMA_INT( (cdsize/4) / 4 );
1496                         }
1497                         else if( chcr == 0x11000000 ) {
1498                                 // CDRDMA_INT( (cdsize/4) * 1 );
1499                                 // halted
1500                                 psxRegs.cycle += (cdsize/4) * 24/2;
1501                                 CDRDMA_INT(16);
1502                         }
1503                         return;
1504
1505                 default:
1506                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1507                         break;
1508         }
1509
1510         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1511         DMA_INTERRUPT(3);
1512 }
1513
1514 void cdrDmaInterrupt(void)
1515 {
1516         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1517         {
1518                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1519                 DMA_INTERRUPT(3);
1520         }
1521 }
1522
1523 static void getCdInfo(void)
1524 {
1525         u8 tmp;
1526
1527         CDR_getTN(cdr.ResultTN);
1528         CDR_getTD(0, cdr.SetSectorEnd);
1529         tmp = cdr.SetSectorEnd[0];
1530         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1531         cdr.SetSectorEnd[2] = tmp;
1532 }
1533
1534 void cdrReset() {
1535         memset(&cdr, 0, sizeof(cdr));
1536         cdr.CurTrack = 1;
1537         cdr.File = 1;
1538         cdr.Channel = 1;
1539         cdr.Reg2 = 0x1f;
1540         cdr.Stat = NoIntr;
1541         cdr.DriveState = DRIVESTATE_STANDBY;
1542         cdr.StatP = STATUS_ROTATING;
1543         pTransfer = cdr.Transfer;
1544         
1545         // BIOS player - default values
1546         cdr.AttenuatorLeftToLeft = 0x80;
1547         cdr.AttenuatorLeftToRight = 0x00;
1548         cdr.AttenuatorRightToLeft = 0x00;
1549         cdr.AttenuatorRightToRight = 0x80;
1550
1551         getCdInfo();
1552 }
1553
1554 int cdrFreeze(void *f, int Mode) {
1555         u32 tmp;
1556         u8 tmpp[3];
1557
1558         if (Mode == 0 && !Config.Cdda)
1559                 CDR_stop();
1560         
1561         cdr.freeze_ver = 0x63647202;
1562         gzfreeze(&cdr, sizeof(cdr));
1563         
1564         if (Mode == 1) {
1565                 cdr.ParamP = cdr.ParamC;
1566                 tmp = pTransfer - cdr.Transfer;
1567         }
1568
1569         gzfreeze(&tmp, sizeof(tmp));
1570
1571         if (Mode == 0) {
1572                 getCdInfo();
1573
1574                 pTransfer = cdr.Transfer + tmp;
1575
1576                 // read right sub data
1577                 tmpp[0] = btoi(cdr.Prev[0]);
1578                 tmpp[1] = btoi(cdr.Prev[1]);
1579                 tmpp[2] = btoi(cdr.Prev[2]);
1580                 cdr.Prev[0]++;
1581                 ReadTrack(tmpp);
1582
1583                 if (cdr.Play) {
1584                         if (cdr.freeze_ver < 0x63647202)
1585                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1586
1587                         Find_CurTrack(cdr.SetSectorPlay);
1588                         if (!Config.Cdda)
1589                                 CDR_play(cdr.SetSectorPlay);
1590                         if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1591                                 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1592                 }
1593
1594                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1595                         // old versions did not latch Reg2, have to fixup..
1596                         if (cdr.Reg2 == 0) {
1597                                 SysPrintf("cdrom: fixing up old savestate\n");
1598                                 cdr.Reg2 = 7;
1599                         }
1600                         // also did not save Attenuator..
1601                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1602                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1603                         {
1604                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1605                         }
1606                 }
1607         }
1608
1609         return 0;
1610 }
1611
1612 void LidInterrupt(void) {
1613         getCdInfo();
1614         cdrLidSeekInterrupt();
1615 }