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