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