cdrom: remove confusing comment
[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_readCDDA && !cdr.Muted && !Config.Cdda) {
628                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
629                 if (SPU_playCDDAchannel)
630                         SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW);
631         }
632
633         cdr.SetSectorPlay[2]++;
634         if (cdr.SetSectorPlay[2] == 75) {
635                 cdr.SetSectorPlay[2] = 0;
636                 cdr.SetSectorPlay[1]++;
637                 if (cdr.SetSectorPlay[1] == 60) {
638                         cdr.SetSectorPlay[1] = 0;
639                         cdr.SetSectorPlay[0]++;
640                 }
641         }
642
643         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
760                         if (!Config.Cdda)
761                                 CDR_play(cdr.SetSectorPlay);
762
763                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
764                         
765                         // BIOS player - set flag again
766                         cdr.Play = TRUE;
767
768                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
769                         start_rotating = 1;
770                         break;
771
772                 case CdlForward:
773                         // TODO: error 80 if stopped
774                         cdr.Stat = Complete;
775
776                         // GameShark CD Player: Calls 2x + Play 2x
777                         cdr.FastForward = 1;
778                         cdr.FastBackward = 0;
779                         break;
780
781                 case CdlBackward:
782                         cdr.Stat = Complete;
783
784                         // GameShark CD Player: Calls 2x + Play 2x
785                         cdr.FastBackward = 1;
786                         cdr.FastForward = 0;
787                         break;
788
789                 case CdlStandby:
790                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
791                                 error = ERROR_INVALIDARG;
792                                 goto set_error;
793                         }
794                         AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
795                         start_rotating = 1;
796                         break;
797
798                 case CdlStandby + 0x100:
799                         cdr.Stat = Complete;
800                         break;
801
802                 case CdlStop:
803                         if (cdr.Play) {
804                                 // grab time for current track
805                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
806
807                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
808                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
809                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
810                         }
811
812                         StopCdda();
813                         StopReading();
814                         SetPlaySeekRead(cdr.StatP, 0);
815                         cdr.StatP &= ~STATUS_ROTATING;
816
817                         delay = 0x800;
818                         if (cdr.DriveState == DRIVESTATE_STANDBY)
819                                 delay = cdReadTime * 30 / 2;
820
821                         cdr.DriveState = DRIVESTATE_STOPPED;
822                         AddIrqQueue(CdlStop + 0x100, delay);
823                         break;
824
825                 case CdlStop + 0x100:
826                         cdr.Stat = Complete;
827                         break;
828
829                 case CdlPause:
830                         StopCdda();
831                         StopReading();
832                         /*
833                         Gundam Battle Assault 2: much slower (*)
834                         - Fixes boot, gameplay
835
836                         Hokuto no Ken 2: slower
837                         - Fixes intro + subtitles
838
839                         InuYasha - Feudal Fairy Tale: slower
840                         - Fixes battles
841                         */
842                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
843                          * The timings from Duckstation are based upon hardware tests.
844                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
845                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
846                          * */
847                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
848                         {
849                                 delay = 7000;
850                         }
851                         else
852                         {
853                                 delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
854                         }
855                         AddIrqQueue(CdlPause + 0x100, delay);
856                         SetPlaySeekRead(cdr.StatP, 0);
857                         cdr.Ctrl |= 0x80;
858                         break;
859
860                 case CdlPause + 0x100:
861                         cdr.Stat = Complete;
862                         break;
863
864                 case CdlReset:
865                         StopCdda();
866                         StopReading();
867                         SetPlaySeekRead(cdr.StatP, 0);
868                         cdr.Muted = FALSE;
869                         cdr.Mode = 0x20; /* This fixes This is Football 2, Pooh's Party lockups */
870                         AddIrqQueue(CdlReset + 0x100, 4100000);
871                         no_busy_error = 1;
872                         start_rotating = 1;
873                         break;
874
875                 case CdlReset + 0x100:
876                         cdr.Stat = Complete;
877                         break;
878
879                 case CdlMute:
880                         cdr.Muted = TRUE;
881                         break;
882
883                 case CdlDemute:
884                         cdr.Muted = FALSE;
885                         break;
886
887                 case CdlSetfilter:
888                         cdr.File = cdr.Param[0];
889                         cdr.Channel = cdr.Param[1];
890                         break;
891
892                 case CdlSetmode:
893                         no_busy_error = 1;
894                         break;
895
896                 case CdlGetparam:
897                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
898                         SetResultSize(5);
899                         cdr.Result[1] = cdr.Mode;
900                         cdr.Result[2] = 0;
901                         cdr.Result[3] = cdr.File;
902                         cdr.Result[4] = cdr.Channel;
903                         no_busy_error = 1;
904                         break;
905
906                 case CdlGetlocL:
907                         SetResultSize(8);
908                         memcpy(cdr.Result, cdr.Transfer, 8);
909                         break;
910
911                 case CdlGetlocP:
912                         SetResultSize(8);
913                         memcpy(&cdr.Result, &cdr.subq, 8);
914                         break;
915
916                 case CdlReadT: // SetSession?
917                         // really long
918                         AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
919                         start_rotating = 1;
920                         break;
921
922                 case CdlReadT + 0x100:
923                         cdr.Stat = Complete;
924                         break;
925
926                 case CdlGetTN:
927                         SetResultSize(3);
928                         if (CDR_getTN(cdr.ResultTN) == -1) {
929                                 cdr.Stat = DiskError;
930                                 cdr.Result[0] |= STATUS_ERROR;
931                         } else {
932                                 cdr.Stat = Acknowledge;
933                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
934                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
935                         }
936                         break;
937
938                 case CdlGetTD:
939                         cdr.Track = btoi(cdr.Param[0]);
940                         SetResultSize(4);
941                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
942                                 cdr.Stat = DiskError;
943                                 cdr.Result[0] |= STATUS_ERROR;
944                         } else {
945                                 cdr.Stat = Acknowledge;
946                                 cdr.Result[0] = cdr.StatP;
947                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
948                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
949                                 /* According to Nocash's documentation, the function doesn't care about ff.
950                                  * This can be seen also in Mednafen's implementation. */
951                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
952                         }
953                         break;
954
955                 case CdlSeekL:
956                 case CdlSeekP:
957                         StopCdda();
958                         StopReading();
959                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
960
961                         seekTime = cdrSeekTime(cdr.SetSector);
962                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
963                         /*
964                         Crusaders of Might and Magic = 0.5x-4x
965                         - fix cutscene speech start
966
967                         Eggs of Steel = 2x-?
968                         - fix new game
969
970                         Medievil = ?-4x
971                         - fix cutscene speech
972
973                         Rockman X5 = 0.5-4x
974                         - fix capcom logo
975                         */
976                         CDRPLAYSEEKREAD_INT(cdReadTime + seekTime);
977                         start_rotating = 1;
978                         break;
979
980                 case CdlTest:
981                         switch (cdr.Param[0]) {
982                                 case 0x20: // System Controller ROM Version
983                                         SetResultSize(4);
984                                         memcpy(cdr.Result, Test20, 4);
985                                         break;
986                                 case 0x22:
987                                         SetResultSize(8);
988                                         memcpy(cdr.Result, Test22, 4);
989                                         break;
990                                 case 0x23: case 0x24:
991                                         SetResultSize(8);
992                                         memcpy(cdr.Result, Test23, 4);
993                                         break;
994                         }
995                         no_busy_error = 1;
996                         break;
997
998                 case CdlID:
999                         AddIrqQueue(CdlID + 0x100, 20480);
1000                         break;
1001
1002                 case CdlID + 0x100:
1003                         SetResultSize(8);
1004                         cdr.Result[0] = cdr.StatP;
1005                         cdr.Result[1] = 0;
1006                         cdr.Result[2] = 0;
1007                         cdr.Result[3] = 0;
1008
1009                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1010                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1011                                 cdr.Result[1] = 0xc0;
1012                         }
1013                         else {
1014                                 if (stat.Type == 2)
1015                                         cdr.Result[1] |= 0x10;
1016                                 if (CdromId[0] == '\0')
1017                                         cdr.Result[1] |= 0x80;
1018                         }
1019                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1020
1021                         /* This adds the string "PCSX" in Playstation bios boot screen */
1022                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1023                         cdr.Stat = Complete;
1024                         break;
1025
1026                 case CdlInit:
1027                         StopCdda();
1028                         StopReading();
1029                         SetPlaySeekRead(cdr.StatP, 0);
1030                         // yes, it really sets STATUS_SHELLOPEN
1031                         cdr.StatP |= STATUS_SHELLOPEN;
1032                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1033                         CDRLID_INT(20480);
1034                         no_busy_error = 1;
1035                         start_rotating = 1;
1036                         break;
1037
1038                 case CdlGetQ:
1039                         no_busy_error = 1;
1040                         break;
1041
1042                 case CdlReadToc:
1043                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1044                         no_busy_error = 1;
1045                         start_rotating = 1;
1046                         break;
1047
1048                 case CdlReadToc + 0x100:
1049                         cdr.Stat = Complete;
1050                         no_busy_error = 1;
1051                         break;
1052
1053                 case CdlReadN:
1054                 case CdlReadS:
1055                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1056
1057                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1058                                 // Read* acts as play for cdda tracks in cdda mode
1059                                 goto do_CdlPlay;
1060
1061                         StopCdda();
1062                         if (cdr.SetlocPending) {
1063                                 seekTime = cdrSeekTime(cdr.SetSector);
1064                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1065                                 cdr.SetlocPending = 0;
1066                         }
1067                         cdr.Reading = 1;
1068                         cdr.FirstSector = 1;
1069
1070                         // Fighting Force 2 - update subq time immediately
1071                         // - fixes new game
1072                         ReadTrack(cdr.SetSectorPlay);
1073
1074                         CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1075
1076                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1077                         start_rotating = 1;
1078                         break;
1079                 case CdlSync:
1080                 default:
1081                         CDR_LOG_I("Invalid command: %02x\n", Irq);
1082                         error = ERROR_INVALIDCMD;
1083                         // FALLTHROUGH
1084
1085                 set_error:
1086                         SetResultSize(2);
1087                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1088                         cdr.Result[1] = error;
1089                         cdr.Stat = DiskError;
1090                         break;
1091         }
1092
1093         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1094                 cdr.DriveState = DRIVESTATE_STANDBY;
1095                 cdr.StatP |= STATUS_ROTATING;
1096         }
1097
1098         if (!no_busy_error) {
1099                 switch (cdr.DriveState) {
1100                 case DRIVESTATE_LID_OPEN:
1101                 case DRIVESTATE_RESCAN_CD:
1102                 case DRIVESTATE_PREPARE_CD:
1103                         SetResultSize(2);
1104                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1105                         cdr.Result[1] = ERROR_NOTREADY;
1106                         cdr.Stat = DiskError;
1107                         break;
1108                 }
1109         }
1110
1111 finish:
1112         setIrq(Irq);
1113         cdr.ParamC = 0;
1114 }
1115
1116 #ifdef HAVE_ARMV7
1117  #define ssat32_to_16(v) \
1118   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1119 #else
1120  #define ssat32_to_16(v) do { \
1121   if (v < -32768) v = -32768; \
1122   else if (v > 32767) v = 32767; \
1123  } while (0)
1124 #endif
1125
1126 void cdrAttenuate(s16 *buf, int samples, int stereo)
1127 {
1128         int i, l, r;
1129         int ll = cdr.AttenuatorLeftToLeft;
1130         int lr = cdr.AttenuatorLeftToRight;
1131         int rl = cdr.AttenuatorRightToLeft;
1132         int rr = cdr.AttenuatorRightToRight;
1133
1134         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1135                 return;
1136
1137         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1138                 return;
1139
1140         if (stereo) {
1141                 for (i = 0; i < samples; i++) {
1142                         l = buf[i * 2];
1143                         r = buf[i * 2 + 1];
1144                         l = (l * ll + r * rl) >> 7;
1145                         r = (r * rr + l * lr) >> 7;
1146                         ssat32_to_16(l);
1147                         ssat32_to_16(r);
1148                         buf[i * 2] = l;
1149                         buf[i * 2 + 1] = r;
1150                 }
1151         }
1152         else {
1153                 for (i = 0; i < samples; i++) {
1154                         l = buf[i];
1155                         l = l * (ll + rl) >> 7;
1156                         //r = r * (rr + lr) >> 7;
1157                         ssat32_to_16(l);
1158                         //ssat32_to_16(r);
1159                         buf[i] = l;
1160                 }
1161         }
1162 }
1163
1164 static void cdrReadInterrupt(void)
1165 {
1166         u8 *buf;
1167
1168         if (cdr.Irq || cdr.Stat) {
1169                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1170                 CDRPLAYSEEKREAD_INT(2048);
1171                 return;
1172         }
1173
1174         cdr.OCUP = 1;
1175         SetResultSize(1);
1176         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1177         cdr.Result[0] = cdr.StatP;
1178
1179         ReadTrack(cdr.SetSectorPlay);
1180
1181         buf = CDR_getBuffer();
1182         if (buf == NULL)
1183                 cdr.NoErr = 0;
1184
1185         if (!cdr.NoErr) {
1186                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1187                 memset(cdr.Transfer, 0, DATA_SIZE);
1188                 cdr.Stat = DiskError;
1189                 cdr.Result[0] |= STATUS_ERROR;
1190                 setIrq(0x205);
1191                 return;
1192         }
1193
1194         memcpy(cdr.Transfer, buf, DATA_SIZE);
1195         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1196
1197
1198         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1199
1200         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1201                 // Firemen 2: Multi-XA files - briefings, cutscenes
1202                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1203                         cdr.File = cdr.Transfer[4 + 0];
1204                         cdr.Channel = cdr.Transfer[4 + 1];
1205                 }
1206
1207                 /* Gameblabla 
1208                  * Skips playing on channel 255.
1209                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1210                  * TODO : Check if this is the proper behaviour.
1211                  * */
1212                 if((cdr.Transfer[4 + 2] & 0x4) &&
1213                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1214                          (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1215                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1216                         if (!ret) {
1217                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1218                                 SPU_playADPCMchannel(&cdr.Xa);
1219                                 cdr.FirstSector = 0;
1220                         }
1221                         else cdr.FirstSector = -1;
1222                 }
1223         }
1224
1225         cdr.SetSectorPlay[2]++;
1226         if (cdr.SetSectorPlay[2] == 75) {
1227                 cdr.SetSectorPlay[2] = 0;
1228                 cdr.SetSectorPlay[1]++;
1229                 if (cdr.SetSectorPlay[1] == 60) {
1230                         cdr.SetSectorPlay[1] = 0;
1231                         cdr.SetSectorPlay[0]++;
1232                 }
1233         }
1234
1235         cdr.Readed = 0;
1236
1237         CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1238
1239         /*
1240         Croc 2: $40 - only FORM1 (*)
1241         Judge Dredd: $C8 - only FORM1 (*)
1242         Sim Theme Park - no adpcm at all (zero)
1243         */
1244
1245         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1246                 cdr.Stat = DataReady;
1247                 setIrq(0x203);
1248         }
1249
1250         // update for CdlGetlocP
1251         ReadTrack(cdr.SetSectorPlay);
1252 }
1253
1254 /*
1255 cdrRead0:
1256         bit 0,1 - mode
1257         bit 2 - unknown
1258         bit 3 - unknown
1259         bit 4 - unknown
1260         bit 5 - 1 result ready
1261         bit 6 - 1 dma ready
1262         bit 7 - 1 command being processed
1263 */
1264
1265 unsigned char cdrRead0(void) {
1266         if (cdr.ResultReady)
1267                 cdr.Ctrl |= 0x20;
1268         else
1269                 cdr.Ctrl &= ~0x20;
1270
1271         if (cdr.OCUP)
1272                 cdr.Ctrl |= 0x40;
1273 //  else
1274 //              cdr.Ctrl &= ~0x40;
1275
1276         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1277         cdr.Ctrl |= 0x18;
1278
1279         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1280
1281         return psxHu8(0x1800) = cdr.Ctrl;
1282 }
1283
1284 void cdrWrite0(unsigned char rt) {
1285         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1286
1287         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1288 }
1289
1290 unsigned char cdrRead1(void) {
1291         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1292                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1293         else
1294                 psxHu8(0x1801) = 0;
1295         cdr.ResultP++;
1296         if (cdr.ResultP == cdr.ResultC)
1297                 cdr.ResultReady = 0;
1298
1299         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1300
1301         return psxHu8(0x1801);
1302 }
1303
1304 void cdrWrite1(unsigned char rt) {
1305         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1306         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1307
1308         switch (cdr.Ctrl & 3) {
1309         case 0:
1310                 break;
1311         case 3:
1312                 cdr.AttenuatorRightToRightT = rt;
1313                 return;
1314         default:
1315                 return;
1316         }
1317
1318         cdr.Cmd = rt;
1319         cdr.OCUP = 0;
1320
1321 #ifdef CDR_LOG_CMD_IRQ
1322         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1323         if (cdr.ParamC) {
1324                 int i;
1325                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1326                 for (i = 0; i < cdr.ParamC; i++)
1327                         SysPrintf(" %x,", cdr.Param[i]);
1328                 SysPrintf("}\n");
1329         } else {
1330                 SysPrintf("\n");
1331         }
1332 #endif
1333
1334         cdr.ResultReady = 0;
1335         cdr.Ctrl |= 0x80;
1336         // cdr.Stat = NoIntr; 
1337         AddIrqQueue(cdr.Cmd, 0x800);
1338
1339         switch (cdr.Cmd) {
1340         case CdlSetmode:
1341                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1342
1343                 cdr.Mode = cdr.Param[0];
1344                 break;
1345         }
1346 }
1347
1348 unsigned char cdrRead2(void) {
1349         unsigned char ret;
1350
1351         if (cdr.Readed == 0) {
1352                 ret = 0;
1353         } else {
1354                 ret = *pTransfer++;
1355         }
1356
1357         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1358         return ret;
1359 }
1360
1361 void cdrWrite2(unsigned char rt) {
1362         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1363         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1364
1365         switch (cdr.Ctrl & 3) {
1366         case 0:
1367                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1368                         cdr.Param[cdr.ParamC++] = rt;
1369                 return;
1370         case 1:
1371                 cdr.Reg2 = rt;
1372                 setIrq(0x204);
1373                 return;
1374         case 2:
1375                 cdr.AttenuatorLeftToLeftT = rt;
1376                 return;
1377         case 3:
1378                 cdr.AttenuatorRightToLeftT = rt;
1379                 return;
1380         }
1381 }
1382
1383 unsigned char cdrRead3(void) {
1384         if (cdr.Ctrl & 0x1)
1385                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1386         else
1387                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1388
1389         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1390         return psxHu8(0x1803);
1391 }
1392
1393 void cdrWrite3(unsigned char rt) {
1394         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1395         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1396
1397         switch (cdr.Ctrl & 3) {
1398         case 0:
1399                 break; // transfer
1400         case 1:
1401                 cdr.Stat &= ~rt;
1402
1403                 if (rt & 0x40)
1404                         cdr.ParamC = 0;
1405                 return;
1406         case 2:
1407                 cdr.AttenuatorLeftToRightT = rt;
1408                 return;
1409         case 3:
1410                 if (rt & 0x20) {
1411                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1412                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1413                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1414                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1415                 }
1416                 return;
1417         }
1418
1419         if ((rt & 0x80) && cdr.Readed == 0) {
1420                 cdr.Readed = 1;
1421                 pTransfer = cdr.Transfer;
1422
1423                 switch (cdr.Mode & 0x30) {
1424                         case MODE_SIZE_2328:
1425                         case 0x00:
1426                                 pTransfer += 12;
1427                                 break;
1428
1429                         case MODE_SIZE_2340:
1430                                 pTransfer += 0;
1431                                 break;
1432
1433                         default:
1434                                 break;
1435                 }
1436         }
1437 }
1438
1439 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1440         u32 cdsize;
1441         int size;
1442         u8 *ptr;
1443
1444         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1445
1446         switch (chcr) {
1447                 case 0x11000000:
1448                 case 0x11400100:
1449                         if (cdr.Readed == 0) {
1450                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1451                                 break;
1452                         }
1453
1454                         cdsize = (bcr & 0xffff) * 4;
1455
1456                         // Ape Escape: bcr = 0001 / 0000
1457                         // - fix boot
1458                         if( cdsize == 0 )
1459                         {
1460                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1461                                         case MODE_SIZE_2340: cdsize = 2340; break;
1462                                         case MODE_SIZE_2328: cdsize = 2328; break;
1463                                         default:
1464                                         case MODE_SIZE_2048: cdsize = 2048; break;
1465                                 }
1466                         }
1467
1468
1469                         ptr = (u8 *)PSXM(madr);
1470                         if (ptr == NULL) {
1471                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1472                                 break;
1473                         }
1474
1475                         /*
1476                         GS CDX: Enhancement CD crash
1477                         - Setloc 0:0:0
1478                         - CdlPlay
1479                         - Spams DMA3 and gets buffer overrun
1480                         */
1481                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1482                         if (size > cdsize)
1483                                 size = cdsize;
1484                         if (size > 0)
1485                         {
1486                                 memcpy(ptr, pTransfer, size);
1487                         }
1488
1489                         psxCpu->Clear(madr, cdsize / 4);
1490                         pTransfer += cdsize;
1491
1492                         if( chcr == 0x11400100 ) {
1493                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1494                                 CDRDMA_INT( (cdsize/4) / 4 );
1495                         }
1496                         else if( chcr == 0x11000000 ) {
1497                                 // CDRDMA_INT( (cdsize/4) * 1 );
1498                                 // halted
1499                                 psxRegs.cycle += (cdsize/4) * 24/2;
1500                                 CDRDMA_INT(16);
1501                         }
1502                         return;
1503
1504                 default:
1505                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1506                         break;
1507         }
1508
1509         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1510         DMA_INTERRUPT(3);
1511 }
1512
1513 void cdrDmaInterrupt(void)
1514 {
1515         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1516         {
1517                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1518                 DMA_INTERRUPT(3);
1519         }
1520 }
1521
1522 static void getCdInfo(void)
1523 {
1524         u8 tmp;
1525
1526         CDR_getTN(cdr.ResultTN);
1527         CDR_getTD(0, cdr.SetSectorEnd);
1528         tmp = cdr.SetSectorEnd[0];
1529         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1530         cdr.SetSectorEnd[2] = tmp;
1531 }
1532
1533 void cdrReset() {
1534         memset(&cdr, 0, sizeof(cdr));
1535         cdr.CurTrack = 1;
1536         cdr.File = 1;
1537         cdr.Channel = 1;
1538         cdr.Reg2 = 0x1f;
1539         cdr.Stat = NoIntr;
1540         cdr.DriveState = DRIVESTATE_STANDBY;
1541         cdr.StatP = STATUS_ROTATING;
1542         pTransfer = cdr.Transfer;
1543         
1544         // BIOS player - default values
1545         cdr.AttenuatorLeftToLeft = 0x80;
1546         cdr.AttenuatorLeftToRight = 0x00;
1547         cdr.AttenuatorRightToLeft = 0x00;
1548         cdr.AttenuatorRightToRight = 0x80;
1549
1550         getCdInfo();
1551 }
1552
1553 int cdrFreeze(void *f, int Mode) {
1554         u32 tmp;
1555         u8 tmpp[3];
1556
1557         if (Mode == 0 && !Config.Cdda)
1558                 CDR_stop();
1559         
1560         cdr.freeze_ver = 0x63647202;
1561         gzfreeze(&cdr, sizeof(cdr));
1562         
1563         if (Mode == 1) {
1564                 cdr.ParamP = cdr.ParamC;
1565                 tmp = pTransfer - cdr.Transfer;
1566         }
1567
1568         gzfreeze(&tmp, sizeof(tmp));
1569
1570         if (Mode == 0) {
1571                 getCdInfo();
1572
1573                 pTransfer = cdr.Transfer + tmp;
1574
1575                 // read right sub data
1576                 tmpp[0] = btoi(cdr.Prev[0]);
1577                 tmpp[1] = btoi(cdr.Prev[1]);
1578                 tmpp[2] = btoi(cdr.Prev[2]);
1579                 cdr.Prev[0]++;
1580                 ReadTrack(tmpp);
1581
1582                 if (cdr.Play) {
1583                         if (cdr.freeze_ver < 0x63647202)
1584                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1585
1586                         Find_CurTrack(cdr.SetSectorPlay);
1587                         if (!Config.Cdda)
1588                                 CDR_play(cdr.SetSectorPlay);
1589                         if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1590                                 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1591                 }
1592
1593                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1594                         // old versions did not latch Reg2, have to fixup..
1595                         if (cdr.Reg2 == 0) {
1596                                 SysPrintf("cdrom: fixing up old savestate\n");
1597                                 cdr.Reg2 = 7;
1598                         }
1599                         // also did not save Attenuator..
1600                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1601                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1602                         {
1603                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1604                         }
1605                 }
1606         }
1607
1608         return 0;
1609 }
1610
1611 void LidInterrupt(void) {
1612         getCdInfo();
1613         cdrLidSeekInterrupt();
1614 }