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