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