cdriso: prefer .toc over .cue
[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
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|STATUS_SEEK);\
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                 if (cdr.SetlocPending) {
496                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
497                         cdr.SetlocPending = 0;
498                 }
499                 Find_CurTrack(cdr.SetSectorPlay);
500                 ReadTrack(cdr.SetSectorPlay);
501                 cdr.TrackChanged = FALSE;
502         }
503
504         if (!cdr.Play) return;
505
506         CDR_LOG( "CDDA - %d:%d:%d\n",
507                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
508
509         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
510                 StopCdda();
511                 cdr.TrackChanged = TRUE;
512         }
513
514         if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
515                 cdrPlayInterrupt_Autopause();
516
517         if (!cdr.Play) return;
518
519         cdr.SetSectorPlay[2]++;
520         if (cdr.SetSectorPlay[2] == 75) {
521                 cdr.SetSectorPlay[2] = 0;
522                 cdr.SetSectorPlay[1]++;
523                 if (cdr.SetSectorPlay[1] == 60) {
524                         cdr.SetSectorPlay[1] = 0;
525                         cdr.SetSectorPlay[0]++;
526                 }
527         }
528
529         CDRMISC_INT(cdReadTime);
530
531         // update for CdlGetlocP/autopause
532         generate_subq(cdr.SetSectorPlay);
533 }
534
535 void cdrInterrupt() {
536         u16 Irq = cdr.Irq;
537         int no_busy_error = 0;
538         int start_rotating = 0;
539         int error = 0;
540         int delay;
541
542         // Reschedule IRQ
543         if (cdr.Stat) {
544                 CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat);
545                 CDR_INT(0x1000);
546                 return;
547         }
548
549         cdr.Ctrl &= ~0x80;
550
551         // default response
552         SetResultSize(1);
553         cdr.Result[0] = cdr.StatP;
554         cdr.Stat = Acknowledge;
555
556         if (cdr.IrqRepeated) {
557                 cdr.IrqRepeated = 0;
558                 if (cdr.eCycle > psxRegs.cycle) {
559                         CDR_INT(cdr.eCycle);
560                         goto finish;
561                 }
562         }
563
564         cdr.Irq = 0;
565
566         switch (Irq) {
567                 case CdlSync:
568                         // TOOD: sometimes/always return error?
569                         break;
570
571                 case CdlNop:
572                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
573                                 cdr.StatP &= ~STATUS_SHELLOPEN;
574                         no_busy_error = 1;
575                         break;
576
577                 case CdlSetloc:
578                         break;
579
580                 do_CdlPlay:
581                 case CdlPlay:
582                         StopCdda();
583                         if (cdr.Seeked == SEEK_PENDING) {
584                                 // XXX: wrong, should seek instead..
585                                 cdr.Seeked = SEEK_DONE;
586                         }
587                         if (cdr.SetlocPending) {
588                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
589                                 cdr.SetlocPending = 0;
590                         }
591
592                         // BIOS CD Player
593                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
594
595                         if (cdr.ParamC == 0 || cdr.Param[0] == 0) {
596                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
597                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
598                         }
599                         else
600                         {
601                                 int track = btoi( cdr.Param[0] );
602
603                                 if (track <= cdr.ResultTN[1])
604                                         cdr.CurTrack = track;
605
606                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
607
608                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
609                                         cdr.SetSectorPlay[0] = cdr.ResultTD[2];
610                                         cdr.SetSectorPlay[1] = cdr.ResultTD[1];
611                                         cdr.SetSectorPlay[2] = cdr.ResultTD[0];
612                                 }
613                         }
614
615                         /*
616                         Rayman: detect track changes
617                         - fixes logo freeze
618
619                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
620                         - plays tracks without retry play
621
622                         Wild 9: skip PREGAP + starting accurate SubQ
623                         - plays tracks without retry play
624                         */
625                         Find_CurTrack(cdr.SetSectorPlay);
626                         ReadTrack(cdr.SetSectorPlay);
627                         cdr.TrackChanged = FALSE;
628
629                         if (!Config.Cdda)
630                                 CDR_play(cdr.SetSectorPlay);
631
632                         // Vib Ribbon: gameplay checks flag
633                         cdr.StatP &= ~STATUS_SEEK;
634                         cdr.Result[0] = cdr.StatP;
635
636                         cdr.StatP |= STATUS_PLAY;
637                         
638                         // BIOS player - set flag again
639                         cdr.Play = TRUE;
640
641                         CDRMISC_INT( cdReadTime );
642                         start_rotating = 1;
643                         break;
644
645                 case CdlForward:
646                         // TODO: error 80 if stopped
647                         cdr.Stat = Complete;
648
649                         // GameShark CD Player: Calls 2x + Play 2x
650                         if( cdr.FastForward == 0 ) cdr.FastForward = 2;
651                         else cdr.FastForward++;
652
653                         cdr.FastBackward = 0;
654                         break;
655
656                 case CdlBackward:
657                         cdr.Stat = Complete;
658
659                         // GameShark CD Player: Calls 2x + Play 2x
660                         if( cdr.FastBackward == 0 ) cdr.FastBackward = 2;
661                         else cdr.FastBackward++;
662
663                         cdr.FastForward = 0;
664                         break;
665
666                 case CdlStandby:
667                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
668                                 error = ERROR_INVALIDARG;
669                                 goto set_error;
670                         }
671                         AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
672                         start_rotating = 1;
673                         break;
674
675                 case CdlStandby + 0x100:
676                         cdr.Stat = Complete;
677                         break;
678
679                 case CdlStop:
680                         if (cdr.Play) {
681                                 // grab time for current track
682                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
683
684                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
685                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
686                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
687                         }
688
689                         StopCdda();
690                         StopReading();
691
692                         delay = 0x800;
693                         if (cdr.DriveState == DRIVESTATE_STANDBY)
694                                 delay = cdReadTime * 30 / 2;
695
696                         cdr.DriveState = DRIVESTATE_STOPPED;
697                         AddIrqQueue(CdlStop + 0x100, delay);
698                         break;
699
700                 case CdlStop + 0x100:
701                         cdr.StatP &= ~STATUS_ROTATING;
702                         cdr.Result[0] = cdr.StatP;
703                         cdr.Stat = Complete;
704                         break;
705
706                 case CdlPause:
707                         /*
708                         Gundam Battle Assault 2: much slower (*)
709                         - Fixes boot, gameplay
710
711                         Hokuto no Ken 2: slower
712                         - Fixes intro + subtitles
713
714                         InuYasha - Feudal Fairy Tale: slower
715                         - Fixes battles
716                         */
717                         AddIrqQueue(CdlPause + 0x100, cdReadTime * 3);
718                         cdr.Ctrl |= 0x80;
719                         break;
720
721                 case CdlPause + 0x100:
722                         cdr.StatP &= ~STATUS_READ;
723                         cdr.Result[0] = cdr.StatP;
724                         cdr.Stat = Complete;
725                         break;
726
727                 case CdlInit:
728                         AddIrqQueue(CdlInit + 0x100, cdReadTime * 6);
729                         no_busy_error = 1;
730                         start_rotating = 1;
731                         break;
732
733                 case CdlInit + 0x100:
734                         cdr.Stat = Complete;
735                         break;
736
737                 case CdlMute:
738                         cdr.Muted = TRUE;
739                         break;
740
741                 case CdlDemute:
742                         cdr.Muted = FALSE;
743                         break;
744
745                 case CdlSetfilter:
746                         cdr.File = cdr.Param[0];
747                         cdr.Channel = cdr.Param[1];
748                         break;
749
750                 case CdlSetmode:
751                         no_busy_error = 1;
752                         break;
753
754                 case CdlGetmode:
755                         SetResultSize(6);
756                         cdr.Result[1] = cdr.Mode;
757                         cdr.Result[2] = cdr.File;
758                         cdr.Result[3] = cdr.Channel;
759                         cdr.Result[4] = 0;
760                         cdr.Result[5] = 0;
761                         no_busy_error = 1;
762                         break;
763
764                 case CdlGetlocL:
765                         SetResultSize(8);
766                         memcpy(cdr.Result, cdr.Transfer, 8);
767                         break;
768
769                 case CdlGetlocP:
770                         SetResultSize(8);
771                         memcpy(&cdr.Result, &cdr.subq, 8);
772
773                         if (!cdr.Play && !cdr.Reading)
774                                 cdr.Result[1] = 0; // HACK?
775                         break;
776
777                 case CdlReadT: // SetSession?
778                         // really long
779                         AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
780                         start_rotating = 1;
781                         break;
782
783                 case CdlReadT + 0x100:
784                         cdr.Stat = Complete;
785                         break;
786
787                 case CdlGetTN:
788                         SetResultSize(3);
789                         if (CDR_getTN(cdr.ResultTN) == -1) {
790                                 cdr.Stat = DiskError;
791                                 cdr.Result[0] |= STATUS_ERROR;
792                         } else {
793                                 cdr.Stat = Acknowledge;
794                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
795                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
796                         }
797                         break;
798
799                 case CdlGetTD:
800                         cdr.Track = btoi(cdr.Param[0]);
801                         SetResultSize(4);
802                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
803                                 cdr.Stat = DiskError;
804                                 cdr.Result[0] |= STATUS_ERROR;
805                         } else {
806                                 cdr.Stat = Acknowledge;
807                                 cdr.Result[0] = cdr.StatP;
808                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
809                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
810                                 cdr.Result[3] = itob(cdr.ResultTD[0]);
811                         }
812                         break;
813
814                 case CdlSeekL:
815                 case CdlSeekP:
816                         StopCdda();
817                         StopReading();
818                         cdr.StatP |= STATUS_SEEK;
819
820                         /*
821                         Crusaders of Might and Magic = 0.5x-4x
822                         - fix cutscene speech start
823
824                         Eggs of Steel = 2x-?
825                         - fix new game
826
827                         Medievil = ?-4x
828                         - fix cutscene speech
829
830                         Rockman X5 = 0.5-4x
831                         - fix capcom logo
832                         */
833                         CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
834                         cdr.Seeked = SEEK_PENDING;
835                         start_rotating = 1;
836                         break;
837
838                 case CdlTest:
839                         switch (cdr.Param[0]) {
840                                 case 0x20: // System Controller ROM Version
841                                         SetResultSize(4);
842                                         memcpy(cdr.Result, Test20, 4);
843                                         break;
844                                 case 0x22:
845                                         SetResultSize(8);
846                                         memcpy(cdr.Result, Test22, 4);
847                                         break;
848                                 case 0x23: case 0x24:
849                                         SetResultSize(8);
850                                         memcpy(cdr.Result, Test23, 4);
851                                         break;
852                         }
853                         no_busy_error = 1;
854                         break;
855
856                 case CdlID:
857                         AddIrqQueue(CdlID + 0x100, 20480);
858                         break;
859
860                 case CdlID + 0x100:
861                         SetResultSize(8);
862                         cdr.Result[0] = cdr.StatP;
863                         cdr.Result[1] = 0;
864                         cdr.Result[2] = 0;
865                         cdr.Result[3] = 0;
866
867                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
868                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
869                                 cdr.Result[1] = 0xc0;
870                         }
871                         else {
872                                 if (stat.Type == 2)
873                                         cdr.Result[1] |= 0x10;
874                                 if (CdromId[0] == '\0')
875                                         cdr.Result[1] |= 0x80;
876                         }
877                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
878
879                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
880                         cdr.Stat = Complete;
881                         break;
882
883                 case CdlReset:
884                         // yes, it really sets STATUS_SHELLOPEN
885                         cdr.StatP |= STATUS_SHELLOPEN;
886                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
887                         CDRLID_INT(20480);
888                         no_busy_error = 1;
889                         start_rotating = 1;
890                         break;
891
892                 case CdlGetQ:
893                         // TODO?
894                         CDR_LOG_I("got CdlGetQ\n");
895                         break;
896
897                 case CdlReadToc:
898                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
899                         no_busy_error = 1;
900                         start_rotating = 1;
901                         break;
902
903                 case CdlReadToc + 0x100:
904                         cdr.Stat = Complete;
905                         no_busy_error = 1;
906                         break;
907
908                 case CdlReadN:
909                 case CdlReadS:
910                         if (cdr.SetlocPending) {
911                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
912                                 cdr.SetlocPending = 0;
913                         }
914                         Find_CurTrack(cdr.SetSectorPlay);
915
916                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
917                                 // Read* acts as play for cdda tracks in cdda mode
918                                 goto do_CdlPlay;
919
920                         cdr.Reading = 1;
921                         cdr.FirstSector = 1;
922
923                         // Fighting Force 2 - update subq time immediately
924                         // - fixes new game
925                         ReadTrack(cdr.SetSectorPlay);
926
927
928                         // Crusaders of Might and Magic - update getlocl now
929                         // - fixes cutscene speech
930                         {
931                                 u8 *buf = CDR_getBuffer();
932                                 if (buf != NULL)
933                                         memcpy(cdr.Transfer, buf, 8);
934                         }
935
936                         /*
937                         Duke Nukem: Land of the Babes - seek then delay read for one frame
938                         - fixes cutscenes
939                         C-12 - Final Resistance - doesn't like seek
940                         */
941
942                         if (cdr.Seeked != SEEK_DONE) {
943                                 cdr.StatP |= STATUS_SEEK;
944                                 cdr.StatP &= ~STATUS_READ;
945
946                                 // Crusaders of Might and Magic - use short time
947                                 // - fix cutscene speech (startup)
948
949                                 // ??? - use more accurate seek time later
950                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
951                         } else {
952                                 cdr.StatP |= STATUS_READ;
953                                 cdr.StatP &= ~STATUS_SEEK;
954
955                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
956                         }
957
958                         cdr.Result[0] = cdr.StatP;
959                         start_rotating = 1;
960                         break;
961
962                 default:
963                         CDR_LOG_I("Invalid command: %02x\n", Irq);
964                         error = ERROR_INVALIDCMD;
965                         // FALLTHROUGH
966
967                 set_error:
968                         SetResultSize(2);
969                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
970                         cdr.Result[1] = error;
971                         cdr.Stat = DiskError;
972                         break;
973         }
974
975         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
976                 cdr.DriveState = DRIVESTATE_STANDBY;
977                 cdr.StatP |= STATUS_ROTATING;
978         }
979
980         if (!no_busy_error) {
981                 switch (cdr.DriveState) {
982                 case DRIVESTATE_LID_OPEN:
983                 case DRIVESTATE_RESCAN_CD:
984                 case DRIVESTATE_PREPARE_CD:
985                         SetResultSize(2);
986                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
987                         cdr.Result[1] = ERROR_NOT_READY;
988                         cdr.Stat = DiskError;
989                         break;
990                 }
991         }
992
993 finish:
994         setIrq();
995         cdr.ParamC = 0;
996
997 #ifdef CDR_LOG_CMD_IRQ
998         {
999                 int i;
1000                 SysPrintf("CDR IRQ %d cmd %02x stat %02x: ",
1001                         !!(cdr.Stat & cdr.Reg2), Irq, cdr.Stat);
1002                 for (i = 0; i < cdr.ResultC; i++)
1003                         SysPrintf("%02x ", cdr.Result[i]);
1004                 SysPrintf("\n");
1005         }
1006 #endif
1007 }
1008
1009 #ifdef __ARM_ARCH_7A__
1010  #define ssat32_to_16(v) \
1011   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1012 #else
1013  #define ssat32_to_16(v) do { \
1014   if (v < -32768) v = -32768; \
1015   else if (v > 32767) v = 32767; \
1016  } while (0)
1017 #endif
1018
1019 void cdrAttenuate(s16 *buf, int samples, int stereo)
1020 {
1021         int i, l, r;
1022         int ll = cdr.AttenuatorLeftToLeft;
1023         int lr = cdr.AttenuatorLeftToRight;
1024         int rl = cdr.AttenuatorRightToLeft;
1025         int rr = cdr.AttenuatorRightToRight;
1026
1027         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1028                 return;
1029
1030         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1031                 return;
1032
1033         if (stereo) {
1034                 for (i = 0; i < samples; i++) {
1035                         l = buf[i * 2];
1036                         r = buf[i * 2 + 1];
1037                         l = (l * ll + r * rl) >> 7;
1038                         r = (r * rr + l * lr) >> 7;
1039                         ssat32_to_16(l);
1040                         ssat32_to_16(r);
1041                         buf[i * 2] = l;
1042                         buf[i * 2 + 1] = r;
1043                 }
1044         }
1045         else {
1046                 for (i = 0; i < samples; i++) {
1047                         l = buf[i];
1048                         l = l * (ll + rl) >> 7;
1049                         //r = r * (rr + lr) >> 7;
1050                         ssat32_to_16(l);
1051                         //ssat32_to_16(r);
1052                         buf[i] = l;
1053                 }
1054         }
1055 }
1056
1057 void cdrReadInterrupt() {
1058         u8 *buf;
1059
1060         if (!cdr.Reading)
1061                 return;
1062
1063         if (cdr.Irq || cdr.Stat) {
1064                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1065                 CDREAD_INT(0x1000);
1066                 return;
1067         }
1068
1069         cdr.OCUP = 1;
1070         SetResultSize(1);
1071         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1072         cdr.StatP &= ~STATUS_SEEK;
1073         cdr.Result[0] = cdr.StatP;
1074         cdr.Seeked = SEEK_DONE;
1075
1076         ReadTrack(cdr.SetSectorPlay);
1077
1078         buf = CDR_getBuffer();
1079         if (buf == NULL)
1080                 cdr.RErr = -1;
1081
1082         if (cdr.RErr == -1) {
1083                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1084                 memset(cdr.Transfer, 0, DATA_SIZE);
1085                 cdr.Stat = DiskError;
1086                 cdr.Result[0] |= STATUS_ERROR;
1087                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1088                 return;
1089         }
1090
1091         memcpy(cdr.Transfer, buf, DATA_SIZE);
1092         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1093
1094
1095         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1096
1097         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1098                 // Firemen 2: Multi-XA files - briefings, cutscenes
1099                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1100                         cdr.File = cdr.Transfer[4 + 0];
1101                         cdr.Channel = cdr.Transfer[4 + 1];
1102                 }
1103
1104                 if((cdr.Transfer[4 + 2] & 0x4) &&
1105                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1106                          (cdr.Transfer[4 + 0] == cdr.File)) {
1107                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1108                         if (!ret) {
1109                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1110                                 SPU_playADPCMchannel(&cdr.Xa);
1111                                 cdr.FirstSector = 0;
1112                         }
1113                         else cdr.FirstSector = -1;
1114                 }
1115         }
1116
1117         cdr.SetSectorPlay[2]++;
1118         if (cdr.SetSectorPlay[2] == 75) {
1119                 cdr.SetSectorPlay[2] = 0;
1120                 cdr.SetSectorPlay[1]++;
1121                 if (cdr.SetSectorPlay[1] == 60) {
1122                         cdr.SetSectorPlay[1] = 0;
1123                         cdr.SetSectorPlay[0]++;
1124                 }
1125         }
1126
1127         cdr.Readed = 0;
1128
1129         CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1130
1131         /*
1132         Croc 2: $40 - only FORM1 (*)
1133         Judge Dredd: $C8 - only FORM1 (*)
1134         Sim Theme Park - no adpcm at all (zero)
1135         */
1136
1137         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1138                 cdr.Stat = DataReady;
1139                 setIrq();
1140         }
1141
1142         // update for CdlGetlocP
1143         ReadTrack(cdr.SetSectorPlay);
1144 }
1145
1146 /*
1147 cdrRead0:
1148         bit 0,1 - mode
1149         bit 2 - unknown
1150         bit 3 - unknown
1151         bit 4 - unknown
1152         bit 5 - 1 result ready
1153         bit 6 - 1 dma ready
1154         bit 7 - 1 command being processed
1155 */
1156
1157 unsigned char cdrRead0(void) {
1158         if (cdr.ResultReady)
1159                 cdr.Ctrl |= 0x20;
1160         else
1161                 cdr.Ctrl &= ~0x20;
1162
1163         if (cdr.OCUP)
1164                 cdr.Ctrl |= 0x40;
1165 //  else
1166 //              cdr.Ctrl &= ~0x40;
1167
1168         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1169         cdr.Ctrl |= 0x18;
1170
1171         CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl);
1172
1173         return psxHu8(0x1800) = cdr.Ctrl;
1174 }
1175
1176 void cdrWrite0(unsigned char rt) {
1177         CDR_LOG_IO("cdr w0: %02x\n", rt);
1178
1179         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1180 }
1181
1182 unsigned char cdrRead1(void) {
1183         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1184                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1185         else
1186                 psxHu8(0x1801) = 0;
1187         cdr.ResultP++;
1188         if (cdr.ResultP == cdr.ResultC)
1189                 cdr.ResultReady = 0;
1190
1191         CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801));
1192
1193         return psxHu8(0x1801);
1194 }
1195
1196 void cdrWrite1(unsigned char rt) {
1197         u8 set_loc[3];
1198         int i;
1199
1200         CDR_LOG_IO("cdr w1: %02x\n", rt);
1201
1202         switch (cdr.Ctrl & 3) {
1203         case 0:
1204                 break;
1205         case 3:
1206                 cdr.AttenuatorRightToRightT = rt;
1207                 return;
1208         default:
1209                 return;
1210         }
1211
1212         cdr.Cmd = rt;
1213         cdr.OCUP = 0;
1214
1215 #ifdef CDR_LOG_CMD_IRQ
1216         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1217         if (cdr.ParamC) {
1218                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1219                 for (i = 0; i < cdr.ParamC; i++)
1220                         SysPrintf(" %x,", cdr.Param[i]);
1221                 SysPrintf("}\n");
1222         } else {
1223                 SysPrintf("\n");
1224         }
1225 #endif
1226
1227         cdr.ResultReady = 0;
1228         cdr.Ctrl |= 0x80;
1229         // cdr.Stat = NoIntr; 
1230         AddIrqQueue(cdr.Cmd, 0x800);
1231
1232         switch (cdr.Cmd) {
1233         case CdlSetloc:
1234                 for (i = 0; i < 3; i++)
1235                         set_loc[i] = btoi(cdr.Param[i]);
1236
1237                 i = msf2sec(cdr.SetSectorPlay);
1238                 i = abs(i - msf2sec(set_loc));
1239                 if (i > 16)
1240                         cdr.Seeked = SEEK_PENDING;
1241
1242                 memcpy(cdr.SetSector, set_loc, 3);
1243                 cdr.SetSector[3] = 0;
1244                 cdr.SetlocPending = 1;
1245                 break;
1246
1247         case CdlReadN:
1248         case CdlReadS:
1249         case CdlPause:
1250                 StopCdda();
1251                 StopReading();
1252                 break;
1253
1254         case CdlReset:
1255         case CdlInit:
1256                 cdr.Seeked = SEEK_DONE;
1257                 StopCdda();
1258                 StopReading();
1259                 break;
1260
1261         case CdlSetmode:
1262                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1263
1264                 cdr.Mode = cdr.Param[0];
1265
1266                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1267                 // - fixes choppy movie sound
1268                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1269                         StopCdda();
1270                 break;
1271         }
1272 }
1273
1274 unsigned char cdrRead2(void) {
1275         unsigned char ret;
1276
1277         if (cdr.Readed == 0) {
1278                 ret = 0;
1279         } else {
1280                 ret = *pTransfer++;
1281         }
1282
1283         CDR_LOG_IO("cdr r2: %02x\n", ret);
1284         return ret;
1285 }
1286
1287 void cdrWrite2(unsigned char rt) {
1288         CDR_LOG_IO("cdr w2: %02x\n", rt);
1289
1290         switch (cdr.Ctrl & 3) {
1291         case 0:
1292                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1293                         cdr.Param[cdr.ParamC++] = rt;
1294                 return;
1295         case 1:
1296                 cdr.Reg2 = rt;
1297                 setIrq();
1298                 return;
1299         case 2:
1300                 cdr.AttenuatorLeftToLeftT = rt;
1301                 return;
1302         case 3:
1303                 cdr.AttenuatorRightToLeftT = rt;
1304                 return;
1305         }
1306 }
1307
1308 unsigned char cdrRead3(void) {
1309         if (cdr.Ctrl & 0x1)
1310                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1311         else
1312                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1313
1314         CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803));
1315         return psxHu8(0x1803);
1316 }
1317
1318 void cdrWrite3(unsigned char rt) {
1319         CDR_LOG_IO("cdr w3: %02x\n", rt);
1320
1321         switch (cdr.Ctrl & 3) {
1322         case 0:
1323                 break; // transfer
1324         case 1:
1325                 cdr.Stat &= ~rt;
1326
1327                 if (rt & 0x40)
1328                         cdr.ParamC = 0;
1329                 return;
1330         case 2:
1331                 cdr.AttenuatorLeftToRightT = rt;
1332                 return;
1333         case 3:
1334                 if (rt & 0x20) {
1335                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1336                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1337                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1338                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1339                 }
1340                 return;
1341         }
1342
1343         if ((rt & 0x80) && cdr.Readed == 0) {
1344                 cdr.Readed = 1;
1345                 pTransfer = cdr.Transfer;
1346
1347                 switch (cdr.Mode & 0x30) {
1348                         case MODE_SIZE_2328:
1349                         case 0x00:
1350                                 pTransfer += 12;
1351                                 break;
1352
1353                         case MODE_SIZE_2340:
1354                                 pTransfer += 0;
1355                                 break;
1356
1357                         default:
1358                                 break;
1359                 }
1360         }
1361 }
1362
1363 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1364         u32 cdsize;
1365         int size;
1366         u8 *ptr;
1367
1368         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1369
1370         switch (chcr) {
1371                 case 0x11000000:
1372                 case 0x11400100:
1373                         if (cdr.Readed == 0) {
1374                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1375                                 break;
1376                         }
1377
1378                         cdsize = (bcr & 0xffff) * 4;
1379
1380                         // Ape Escape: bcr = 0001 / 0000
1381                         // - fix boot
1382                         if( cdsize == 0 )
1383                         {
1384                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1385                                         case MODE_SIZE_2340: cdsize = 2340; break;
1386                                         case MODE_SIZE_2328: cdsize = 2328; break;
1387                                         default:
1388                                         case MODE_SIZE_2048: cdsize = 2048; break;
1389                                 }
1390                         }
1391
1392
1393                         ptr = (u8 *)PSXM(madr);
1394                         if (ptr == NULL) {
1395                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1396                                 break;
1397                         }
1398
1399                         /*
1400                         GS CDX: Enhancement CD crash
1401                         - Setloc 0:0:0
1402                         - CdlPlay
1403                         - Spams DMA3 and gets buffer overrun
1404                         */
1405                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1406                         if (size > cdsize)
1407                                 size = cdsize;
1408                         if (size > 0)
1409                         {
1410                                 memcpy(ptr, pTransfer, size);
1411                         }
1412
1413                         psxCpu->Clear(madr, cdsize / 4);
1414                         pTransfer += cdsize;
1415
1416
1417                         // burst vs normal
1418                         if( chcr == 0x11400100 ) {
1419                                 CDRDMA_INT( (cdsize/4) / 4 );
1420                         }
1421                         else if( chcr == 0x11000000 ) {
1422                                 CDRDMA_INT( (cdsize/4) * 1 );
1423                         }
1424                         return;
1425
1426                 default:
1427                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1428                         break;
1429         }
1430
1431         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1432         DMA_INTERRUPT(3);
1433 }
1434
1435 void cdrDmaInterrupt()
1436 {
1437         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1438         {
1439                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1440                 DMA_INTERRUPT(3);
1441         }
1442 }
1443
1444 static void getCdInfo(void)
1445 {
1446         u8 tmp;
1447
1448         CDR_getTN(cdr.ResultTN);
1449         CDR_getTD(0, cdr.SetSectorEnd);
1450         tmp = cdr.SetSectorEnd[0];
1451         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1452         cdr.SetSectorEnd[2] = tmp;
1453 }
1454
1455 void cdrReset() {
1456         memset(&cdr, 0, sizeof(cdr));
1457         cdr.CurTrack = 1;
1458         cdr.File = 1;
1459         cdr.Channel = 1;
1460         cdr.Reg2 = 0x1f;
1461         cdr.Stat = NoIntr;
1462         cdr.DriveState = DRIVESTATE_STANDBY;
1463         cdr.StatP = STATUS_ROTATING;
1464         pTransfer = cdr.Transfer;
1465
1466         // BIOS player - default values
1467         cdr.AttenuatorLeftToLeft = 0x80;
1468         cdr.AttenuatorLeftToRight = 0x00;
1469         cdr.AttenuatorRightToLeft = 0x00;
1470         cdr.AttenuatorRightToRight = 0x80;
1471
1472         getCdInfo();
1473 }
1474
1475 int cdrFreeze(void *f, int Mode) {
1476         u32 tmp;
1477         u8 tmpp[3];
1478
1479         if (Mode == 0 && !Config.Cdda)
1480                 CDR_stop();
1481         
1482         cdr.freeze_ver = 0x63647202;
1483         gzfreeze(&cdr, sizeof(cdr));
1484         
1485         if (Mode == 1) {
1486                 cdr.ParamP = cdr.ParamC;
1487                 tmp = pTransfer - cdr.Transfer;
1488         }
1489
1490         gzfreeze(&tmp, sizeof(tmp));
1491
1492         if (Mode == 0) {
1493                 getCdInfo();
1494
1495                 pTransfer = cdr.Transfer + tmp;
1496
1497                 // read right sub data
1498                 memcpy(tmpp, cdr.Prev, 3);
1499                 cdr.Prev[0]++;
1500                 ReadTrack(tmpp);
1501
1502                 if (cdr.Play) {
1503                         if (cdr.freeze_ver < 0x63647202)
1504                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1505
1506                         Find_CurTrack(cdr.SetSectorPlay);
1507                         if (!Config.Cdda)
1508                                 CDR_play(cdr.SetSectorPlay);
1509                 }
1510
1511                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1512                         // old versions did not latch Reg2, have to fixup..
1513                         if (cdr.Reg2 == 0) {
1514                                 SysPrintf("cdrom: fixing up old savestate\n");
1515                                 cdr.Reg2 = 7;
1516                         }
1517                 }
1518         }
1519
1520         return 0;
1521 }
1522
1523 void LidInterrupt() {
1524         getCdInfo();
1525         StopCdda();
1526         cdrLidSeekInterrupt();
1527 }