CdlSync should be NULL and return an error.
[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                         // TODO?
910                         CDR_LOG_I("got CdlGetQ\n");
911                         break;
912
913                 case CdlReadToc:
914                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
915                         no_busy_error = 1;
916                         start_rotating = 1;
917                         break;
918
919                 case CdlReadToc + 0x100:
920                         cdr.Stat = Complete;
921                         cdr.RErr = 1;
922                         no_busy_error = 1;
923                         break;
924
925                 case CdlReadN:
926                 case CdlReadS:
927                         if (cdr.SetlocPending) {
928                                 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(cdr.SetSector)) * (cdReadTime / 200);
929                                 /*
930                                 * Gameblabla :
931                                 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
932                                 * and was unreliable for that game.
933                                 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
934                                 * 
935                                 * Obviously, this isn't perfect but right now, it should be a bit better.
936                                 * Games to test this against if you change that setting :
937                                 * - Driver (titlescreen music delay and retry mission)
938                                 * - Worms Pinball (Will either not boot or crash in the memory card screen)
939                                 * - Viewpoint (short pauses if the delay in the ingame music is too long)
940                                 * 
941                                 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
942                                 * However, 1000000 is not enough for Worms Pinball to reliably boot.
943                                 */
944                                 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
945                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
946                                 cdr.SetlocPending = 0;
947                                 cdr.m_locationChanged = TRUE;
948                         }
949                         Find_CurTrack(cdr.SetSectorPlay);
950
951                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
952                                 // Read* acts as play for cdda tracks in cdda mode
953                                 goto do_CdlPlay;
954
955                         cdr.Reading = 1;
956                         cdr.FirstSector = 1;
957
958                         // Fighting Force 2 - update subq time immediately
959                         // - fixes new game
960                         ReadTrack(cdr.SetSectorPlay);
961
962
963                         // Crusaders of Might and Magic - update getlocl now
964                         // - fixes cutscene speech
965                         {
966                                 u8 *buf = CDR_getBuffer();
967                                 if (buf != NULL)
968                                         memcpy(cdr.Transfer, buf, 8);
969                         }
970
971                         /*
972                         Duke Nukem: Land of the Babes - seek then delay read for one frame
973                         - fixes cutscenes
974                         C-12 - Final Resistance - doesn't like seek
975                         */
976
977                         /*      
978                                 By nicolasnoble from PCSX Redux :
979                                 "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now.
980                                 For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect
981                                 the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK
982                                 flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game
983                                 tries to protect itself against errors by preventing from issuing a GetLocP while it knows the
984                                 last status was "seek". But this makes the logic just softlock as it'll never get a notification
985                                 about the fact the drive is done seeking and the read actually started.
986                                 In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is
987                                 done right away. It's rather when it's done seeking, and the read has actually started. This probably
988                                 requires a bit more work to make sure seek delays are processed properly.
989                                 Checked with a few games, this seems to work fine."
990                                 
991                                 Gameblabla additional notes :
992                                 This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error.
993                         */
994                         cdr.StatP |= STATUS_READ;
995                         cdr.StatP &= ~STATUS_SEEK;
996                         CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
997
998                         cdr.Result[0] = cdr.StatP;
999                         start_rotating = 1;
1000                         break;
1001
1002                 case CdlSync:
1003                 default:
1004                         CDR_LOG_I("Invalid command: %02x\n", Irq);
1005                         error = ERROR_INVALIDCMD;
1006                         // FALLTHROUGH
1007
1008                 set_error:
1009                         SetResultSize(2);
1010                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1011                         cdr.Result[1] = error;
1012                         cdr.Stat = DiskError;
1013                         break;
1014         }
1015
1016         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1017                 cdr.DriveState = DRIVESTATE_STANDBY;
1018                 cdr.StatP |= STATUS_ROTATING;
1019         }
1020
1021         if (!no_busy_error) {
1022                 switch (cdr.DriveState) {
1023                 case DRIVESTATE_LID_OPEN:
1024                 case DRIVESTATE_RESCAN_CD:
1025                 case DRIVESTATE_PREPARE_CD:
1026                         SetResultSize(2);
1027                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1028                         cdr.Result[1] = ERROR_NOTREADY;
1029                         cdr.Stat = DiskError;
1030                         break;
1031                 }
1032         }
1033
1034 finish:
1035         setIrq();
1036         cdr.ParamC = 0;
1037
1038 #ifdef CDR_LOG_CMD_IRQ
1039         {
1040                 int i;
1041                 SysPrintf("CDR IRQ %d cmd %02x stat %02x: ",
1042                         !!(cdr.Stat & cdr.Reg2), Irq, cdr.Stat);
1043                 for (i = 0; i < cdr.ResultC; i++)
1044                         SysPrintf("%02x ", cdr.Result[i]);
1045                 SysPrintf("\n");
1046         }
1047 #endif
1048 }
1049
1050 #ifdef HAVE_ARMV7
1051  #define ssat32_to_16(v) \
1052   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1053 #else
1054  #define ssat32_to_16(v) do { \
1055   if (v < -32768) v = -32768; \
1056   else if (v > 32767) v = 32767; \
1057  } while (0)
1058 #endif
1059
1060 void cdrAttenuate(s16 *buf, int samples, int stereo)
1061 {
1062         int i, l, r;
1063         int ll = cdr.AttenuatorLeftToLeft;
1064         int lr = cdr.AttenuatorLeftToRight;
1065         int rl = cdr.AttenuatorRightToLeft;
1066         int rr = cdr.AttenuatorRightToRight;
1067
1068         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1069                 return;
1070
1071         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1072                 return;
1073
1074         if (stereo) {
1075                 for (i = 0; i < samples; i++) {
1076                         l = buf[i * 2];
1077                         r = buf[i * 2 + 1];
1078                         l = (l * ll + r * rl) >> 7;
1079                         r = (r * rr + l * lr) >> 7;
1080                         ssat32_to_16(l);
1081                         ssat32_to_16(r);
1082                         buf[i * 2] = l;
1083                         buf[i * 2 + 1] = r;
1084                 }
1085         }
1086         else {
1087                 for (i = 0; i < samples; i++) {
1088                         l = buf[i];
1089                         l = l * (ll + rl) >> 7;
1090                         //r = r * (rr + lr) >> 7;
1091                         ssat32_to_16(l);
1092                         //ssat32_to_16(r);
1093                         buf[i] = l;
1094                 }
1095         }
1096 }
1097
1098 void cdrReadInterrupt() {
1099         u8 *buf;
1100
1101         if (!cdr.Reading)
1102                 return;
1103
1104         if (cdr.Irq || cdr.Stat) {
1105                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1106                 CDREAD_INT(0x1000);
1107                 return;
1108         }
1109
1110         cdr.OCUP = 1;
1111         SetResultSize(1);
1112         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1113         cdr.StatP &= ~STATUS_SEEK;
1114         cdr.Result[0] = cdr.StatP;
1115         cdr.Seeked = SEEK_DONE;
1116
1117         ReadTrack(cdr.SetSectorPlay);
1118
1119         buf = CDR_getBuffer();
1120         if (buf == NULL)
1121                 cdr.RErr = 0;
1122
1123         if (cdr.RErr == 0) {
1124                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1125                 memset(cdr.Transfer, 0, DATA_SIZE);
1126                 cdr.Stat = DiskError;
1127                 cdr.Result[0] |= STATUS_ERROR;
1128                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1129                 return;
1130         }
1131
1132         memcpy(cdr.Transfer, buf, DATA_SIZE);
1133         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1134
1135
1136         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1137
1138         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1139                 // Firemen 2: Multi-XA files - briefings, cutscenes
1140                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1141                         cdr.File = cdr.Transfer[4 + 0];
1142                         cdr.Channel = cdr.Transfer[4 + 1];
1143                 }
1144
1145                 /* Gameblabla 
1146                  * Skips playing on channel 255.
1147                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1148                  * TODO : Check if this is the proper behaviour.
1149                  * */
1150                 if((cdr.Transfer[4 + 2] & 0x4) &&
1151                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1152                          (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1153                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1154                         if (!ret) {
1155                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1156                                 SPU_playADPCMchannel(&cdr.Xa);
1157                                 cdr.FirstSector = 0;
1158                         }
1159                         else cdr.FirstSector = -1;
1160                 }
1161         }
1162
1163         cdr.SetSectorPlay[2]++;
1164         if (cdr.SetSectorPlay[2] == 75) {
1165                 cdr.SetSectorPlay[2] = 0;
1166                 cdr.SetSectorPlay[1]++;
1167                 if (cdr.SetSectorPlay[1] == 60) {
1168                         cdr.SetSectorPlay[1] = 0;
1169                         cdr.SetSectorPlay[0]++;
1170                 }
1171         }
1172
1173         cdr.Readed = 0;
1174
1175         uint32_t delay = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
1176         if (cdr.m_locationChanged) {
1177                 CDREAD_INT(delay * 30);
1178                 cdr.m_locationChanged = FALSE;
1179         } else {
1180                 CDREAD_INT(delay);
1181         }
1182
1183         /*
1184         Croc 2: $40 - only FORM1 (*)
1185         Judge Dredd: $C8 - only FORM1 (*)
1186         Sim Theme Park - no adpcm at all (zero)
1187         */
1188
1189         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1190                 cdr.Stat = DataReady;
1191                 setIrq();
1192         }
1193
1194         // update for CdlGetlocP
1195         ReadTrack(cdr.SetSectorPlay);
1196 }
1197
1198 /*
1199 cdrRead0:
1200         bit 0,1 - mode
1201         bit 2 - unknown
1202         bit 3 - unknown
1203         bit 4 - unknown
1204         bit 5 - 1 result ready
1205         bit 6 - 1 dma ready
1206         bit 7 - 1 command being processed
1207 */
1208
1209 unsigned char cdrRead0(void) {
1210         if (cdr.ResultReady)
1211                 cdr.Ctrl |= 0x20;
1212         else
1213                 cdr.Ctrl &= ~0x20;
1214
1215         if (cdr.OCUP)
1216                 cdr.Ctrl |= 0x40;
1217 //  else
1218 //              cdr.Ctrl &= ~0x40;
1219
1220         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1221         cdr.Ctrl |= 0x18;
1222
1223         CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl);
1224
1225         return psxHu8(0x1800) = cdr.Ctrl;
1226 }
1227
1228 void cdrWrite0(unsigned char rt) {
1229         CDR_LOG_IO("cdr w0: %02x\n", rt);
1230
1231         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1232 }
1233
1234 unsigned char cdrRead1(void) {
1235         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1236                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1237         else
1238                 psxHu8(0x1801) = 0;
1239         cdr.ResultP++;
1240         if (cdr.ResultP == cdr.ResultC)
1241                 cdr.ResultReady = 0;
1242
1243         CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801));
1244
1245         return psxHu8(0x1801);
1246 }
1247
1248 void cdrWrite1(unsigned char rt) {
1249         u8 set_loc[3];
1250         int i;
1251
1252         CDR_LOG_IO("cdr w1: %02x\n", rt);
1253
1254         switch (cdr.Ctrl & 3) {
1255         case 0:
1256                 break;
1257         case 3:
1258                 cdr.AttenuatorRightToRightT = rt;
1259                 return;
1260         default:
1261                 return;
1262         }
1263
1264         cdr.Cmd = rt;
1265         cdr.OCUP = 0;
1266
1267 #ifdef CDR_LOG_CMD_IRQ
1268         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1269         if (cdr.ParamC) {
1270                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1271                 for (i = 0; i < cdr.ParamC; i++)
1272                         SysPrintf(" %x,", cdr.Param[i]);
1273                 SysPrintf("}\n");
1274         } else {
1275                 SysPrintf("\n");
1276         }
1277 #endif
1278
1279         cdr.ResultReady = 0;
1280         cdr.Ctrl |= 0x80;
1281         // cdr.Stat = NoIntr; 
1282         AddIrqQueue(cdr.Cmd, 0x800);
1283
1284         switch (cdr.Cmd) {
1285         case CdlSetloc:
1286                 for (i = 0; i < 3; i++)
1287                         set_loc[i] = btoi(cdr.Param[i]);
1288
1289                 i = msf2sec(cdr.SetSectorPlay);
1290                 i = abs(i - msf2sec(set_loc));
1291                 if (i > 16)
1292                         cdr.Seeked = SEEK_PENDING;
1293
1294                 memcpy(cdr.SetSector, set_loc, 3);
1295                 cdr.SetSector[3] = 0;
1296                 cdr.SetlocPending = 1;
1297                 break;
1298
1299         case CdlReadN:
1300         case CdlReadS:
1301         case CdlPause:
1302                 StopCdda();
1303                 StopReading();
1304                 break;
1305
1306         case CdlInit:
1307         case CdlReset:
1308                 cdr.Seeked = SEEK_DONE;
1309                 StopCdda();
1310                 StopReading();
1311                 break;
1312
1313         case CdlSetmode:
1314                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1315
1316                 cdr.Mode = cdr.Param[0];
1317
1318                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1319                 // - fixes choppy movie sound
1320                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1321                         StopCdda();
1322                 break;
1323         }
1324 }
1325
1326 unsigned char cdrRead2(void) {
1327         unsigned char ret;
1328
1329         if (cdr.Readed == 0) {
1330                 ret = 0;
1331         } else {
1332                 ret = *pTransfer++;
1333         }
1334
1335         CDR_LOG_IO("cdr r2: %02x\n", ret);
1336         return ret;
1337 }
1338
1339 void cdrWrite2(unsigned char rt) {
1340         CDR_LOG_IO("cdr w2: %02x\n", rt);
1341
1342         switch (cdr.Ctrl & 3) {
1343         case 0:
1344                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1345                         cdr.Param[cdr.ParamC++] = rt;
1346                 return;
1347         case 1:
1348                 cdr.Reg2 = rt;
1349                 setIrq();
1350                 return;
1351         case 2:
1352                 cdr.AttenuatorLeftToLeftT = rt;
1353                 return;
1354         case 3:
1355                 cdr.AttenuatorRightToLeftT = rt;
1356                 return;
1357         }
1358 }
1359
1360 unsigned char cdrRead3(void) {
1361         if (cdr.Ctrl & 0x1)
1362                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1363         else
1364                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1365
1366         CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803));
1367         return psxHu8(0x1803);
1368 }
1369
1370 void cdrWrite3(unsigned char rt) {
1371         CDR_LOG_IO("cdr w3: %02x\n", rt);
1372
1373         switch (cdr.Ctrl & 3) {
1374         case 0:
1375                 break; // transfer
1376         case 1:
1377                 cdr.Stat &= ~rt;
1378
1379                 if (rt & 0x40)
1380                         cdr.ParamC = 0;
1381                 return;
1382         case 2:
1383                 cdr.AttenuatorLeftToRightT = rt;
1384                 return;
1385         case 3:
1386                 if (rt & 0x20) {
1387                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1388                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1389                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1390                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1391                 }
1392                 return;
1393         }
1394
1395         if ((rt & 0x80) && cdr.Readed == 0) {
1396                 cdr.Readed = 1;
1397                 pTransfer = cdr.Transfer;
1398
1399                 switch (cdr.Mode & 0x30) {
1400                         case MODE_SIZE_2328:
1401                         case 0x00:
1402                                 pTransfer += 12;
1403                                 break;
1404
1405                         case MODE_SIZE_2340:
1406                                 pTransfer += 0;
1407                                 break;
1408
1409                         default:
1410                                 break;
1411                 }
1412         }
1413 }
1414
1415 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1416         u32 cdsize;
1417         int size;
1418         u8 *ptr;
1419
1420         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1421
1422         switch (chcr) {
1423                 case 0x11000000:
1424                 case 0x11400100:
1425                         if (cdr.Readed == 0) {
1426                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1427                                 break;
1428                         }
1429
1430                         cdsize = (bcr & 0xffff) * 4;
1431
1432                         // Ape Escape: bcr = 0001 / 0000
1433                         // - fix boot
1434                         if( cdsize == 0 )
1435                         {
1436                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1437                                         case MODE_SIZE_2340: cdsize = 2340; break;
1438                                         case MODE_SIZE_2328: cdsize = 2328; break;
1439                                         default:
1440                                         case MODE_SIZE_2048: cdsize = 2048; break;
1441                                 }
1442                         }
1443
1444
1445                         ptr = (u8 *)PSXM(madr);
1446                         if (ptr == NULL) {
1447                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1448                                 break;
1449                         }
1450
1451                         /*
1452                         GS CDX: Enhancement CD crash
1453                         - Setloc 0:0:0
1454                         - CdlPlay
1455                         - Spams DMA3 and gets buffer overrun
1456                         */
1457                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1458                         if (size > cdsize)
1459                                 size = cdsize;
1460                         if (size > 0)
1461                         {
1462                                 memcpy(ptr, pTransfer, size);
1463                         }
1464
1465                         psxCpu->Clear(madr, cdsize / 4);
1466                         pTransfer += cdsize;
1467
1468                         if( chcr == 0x11400100 ) {
1469                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1470                                 CDRDMA_INT( (cdsize/4) / 4 );
1471                         }
1472                         else if( chcr == 0x11000000 ) {
1473                                 // CDRDMA_INT( (cdsize/4) * 1 );
1474                                 // halted
1475                                 psxRegs.cycle += (cdsize/4) * 24/2;
1476                                 CDRDMA_INT(16);
1477                         }
1478                         return;
1479
1480                 default:
1481                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1482                         break;
1483         }
1484
1485         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1486         DMA_INTERRUPT(3);
1487 }
1488
1489 void cdrDmaInterrupt()
1490 {
1491         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1492         {
1493                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1494                 DMA_INTERRUPT(3);
1495         }
1496 }
1497
1498 static void getCdInfo(void)
1499 {
1500         u8 tmp;
1501
1502         CDR_getTN(cdr.ResultTN);
1503         CDR_getTD(0, cdr.SetSectorEnd);
1504         tmp = cdr.SetSectorEnd[0];
1505         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1506         cdr.SetSectorEnd[2] = tmp;
1507 }
1508
1509 void cdrReset() {
1510         memset(&cdr, 0, sizeof(cdr));
1511         cdr.CurTrack = 1;
1512         cdr.File = 1;
1513         cdr.Channel = 1;
1514         cdr.Reg2 = 0x1f;
1515         cdr.Stat = NoIntr;
1516         cdr.DriveState = DRIVESTATE_STANDBY;
1517         cdr.StatP = STATUS_ROTATING;
1518         pTransfer = cdr.Transfer;
1519         cdr.SetlocPending = 0;
1520         cdr.m_locationChanged = FALSE;
1521
1522         // BIOS player - default values
1523         cdr.AttenuatorLeftToLeft = 0x80;
1524         cdr.AttenuatorLeftToRight = 0x00;
1525         cdr.AttenuatorRightToLeft = 0x00;
1526         cdr.AttenuatorRightToRight = 0x80;
1527
1528         getCdInfo();
1529 }
1530
1531 int cdrFreeze(void *f, int Mode) {
1532         u32 tmp;
1533         u8 tmpp[3];
1534
1535         if (Mode == 0 && !Config.Cdda)
1536                 CDR_stop();
1537         
1538         cdr.freeze_ver = 0x63647202;
1539         gzfreeze(&cdr, sizeof(cdr));
1540         
1541         if (Mode == 1) {
1542                 cdr.ParamP = cdr.ParamC;
1543                 tmp = pTransfer - cdr.Transfer;
1544         }
1545
1546         gzfreeze(&tmp, sizeof(tmp));
1547
1548         if (Mode == 0) {
1549                 getCdInfo();
1550
1551                 pTransfer = cdr.Transfer + tmp;
1552
1553                 // read right sub data
1554                 tmpp[0] = btoi(cdr.Prev[0]);
1555                 tmpp[1] = btoi(cdr.Prev[1]);
1556                 tmpp[2] = btoi(cdr.Prev[2]);
1557                 cdr.Prev[0]++;
1558                 ReadTrack(tmpp);
1559
1560                 if (cdr.Play) {
1561                         if (cdr.freeze_ver < 0x63647202)
1562                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1563
1564                         Find_CurTrack(cdr.SetSectorPlay);
1565                         if (!Config.Cdda)
1566                                 CDR_play(cdr.SetSectorPlay);
1567                 }
1568
1569                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1570                         // old versions did not latch Reg2, have to fixup..
1571                         if (cdr.Reg2 == 0) {
1572                                 SysPrintf("cdrom: fixing up old savestate\n");
1573                                 cdr.Reg2 = 7;
1574                         }
1575                         // also did not save Attenuator..
1576                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1577                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1578                         {
1579                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1580                         }
1581                 }
1582         }
1583
1584         return 0;
1585 }
1586
1587 void LidInterrupt() {
1588         getCdInfo();
1589         StopCdda();
1590         cdrLidSeekInterrupt();
1591 }