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