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