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