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