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                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
1034                         cdr.Stat = Complete;
1035                         break;
1036
1037                 case CdlInit:
1038                         // yes, it really sets STATUS_SHELLOPEN
1039                         cdr.StatP |= STATUS_SHELLOPEN;
1040                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1041                         CDRLID_INT(20480);
1042                         no_busy_error = 1;
1043                         start_rotating = 1;
1044                         break;
1045
1046                 case CdlGetQ:
1047                         no_busy_error = 1;
1048                         break;
1049
1050                 case CdlReadToc:
1051                         AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
1052                         no_busy_error = 1;
1053                         start_rotating = 1;
1054                         break;
1055
1056                 case CdlReadToc + 0x100:
1057                         cdr.Stat = Complete;
1058                         no_busy_error = 1;
1059                         break;
1060
1061                 case CdlReadN:
1062                 case CdlReadS:
1063                         if (cdr.SetlocPending) {
1064                                 seekTime = abs(msf2sec(cdr.SetSectorPlay) - msf2sec(cdr.SetSector)) * (cdReadTime / 200);
1065                                 /*
1066                                 * Gameblabla :
1067                                 * It was originally set to 1000000 for Driver, however it is not high enough for Worms Pinball
1068                                 * and was unreliable for that game.
1069                                 * I also tested it against Mednafen and Driver's titlescreen music starts 25 frames later, not immediatly.
1070                                 * 
1071                                 * Obviously, this isn't perfect but right now, it should be a bit better.
1072                                 * Games to test this against if you change that setting :
1073                                 * - Driver (titlescreen music delay and retry mission)
1074                                 * - Worms Pinball (Will either not boot or crash in the memory card screen)
1075                                 * - Viewpoint (short pauses if the delay in the ingame music is too long)
1076                                 * 
1077                                 * It seems that 3386880 * 5 is too much for Driver's titlescreen and it starts skipping.
1078                                 * However, 1000000 is not enough for Worms Pinball to reliably boot.
1079                                 */
1080                                 if(seekTime > 3386880 * 2) seekTime = 3386880 * 2;
1081                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1082                                 cdr.SetlocPending = 0;
1083                                 cdr.m_locationChanged = TRUE;
1084                         }
1085                         Find_CurTrack(cdr.SetSectorPlay);
1086
1087                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1088                                 // Read* acts as play for cdda tracks in cdda mode
1089                                 goto do_CdlPlay;
1090
1091                         cdr.Reading = 1;
1092                         cdr.FirstSector = 1;
1093
1094                         // Fighting Force 2 - update subq time immediately
1095                         // - fixes new game
1096                         ReadTrack(cdr.SetSectorPlay);
1097
1098
1099                         // Crusaders of Might and Magic - update getlocl now
1100                         // - fixes cutscene speech
1101                         {
1102                                 u8 *buf = CDR_getBuffer();
1103                                 if (buf != NULL)
1104                                         memcpy(cdr.Transfer, buf, 8);
1105                         }
1106
1107                         /*
1108                         Duke Nukem: Land of the Babes - seek then delay read for one frame
1109                         - fixes cutscenes
1110                         C-12 - Final Resistance - doesn't like seek
1111                         */
1112                         
1113                         /*      
1114                                 By nicolasnoble from PCSX Redux :
1115                                 "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now.
1116                                 For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect
1117                                 the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK
1118                                 flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game
1119                                 tries to protect itself against errors by preventing from issuing a GetLocP while it knows the
1120                                 last status was "seek". But this makes the logic just softlock as it'll never get a notification
1121                                 about the fact the drive is done seeking and the read actually started.
1122
1123                                 In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is
1124                                 done right away. It's rather when it's done seeking, and the read has actually started. This probably
1125                                 requires a bit more work to make sure seek delays are processed properly.
1126                                 Checked with a few games, this seems to work fine."
1127                                 
1128                                 Gameblabla additional notes :
1129                                 This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error.
1130                         */
1131                         cdr.StatP |= STATUS_READ;
1132                         cdr.StatP &= ~STATUS_SEEK;
1133
1134                         CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
1135
1136                         cdr.Result[0] = cdr.StatP;
1137                         start_rotating = 1;
1138                         break;
1139                 case CdlSync:
1140                 default:
1141                         CDR_LOG_I("Invalid command: %02x\n", Irq);
1142                         error = ERROR_INVALIDCMD;
1143                         // FALLTHROUGH
1144
1145                 set_error:
1146                         SetResultSize(2);
1147                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1148                         cdr.Result[1] = error;
1149                         cdr.Stat = DiskError;
1150                         break;
1151         }
1152
1153         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1154                 cdr.DriveState = DRIVESTATE_STANDBY;
1155                 cdr.StatP |= STATUS_ROTATING;
1156         }
1157
1158         if (!no_busy_error) {
1159                 switch (cdr.DriveState) {
1160                 case DRIVESTATE_LID_OPEN:
1161                 case DRIVESTATE_RESCAN_CD:
1162                 case DRIVESTATE_PREPARE_CD:
1163                         SetResultSize(2);
1164                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1165                         cdr.Result[1] = ERROR_NOTREADY;
1166                         cdr.Stat = DiskError;
1167                         break;
1168                 }
1169         }
1170
1171 finish:
1172         setIrq(Irq);
1173         cdr.ParamC = 0;
1174 }
1175
1176 #ifdef HAVE_ARMV7
1177  #define ssat32_to_16(v) \
1178   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1179 #else
1180  #define ssat32_to_16(v) do { \
1181   if (v < -32768) v = -32768; \
1182   else if (v > 32767) v = 32767; \
1183  } while (0)
1184 #endif
1185
1186 void cdrAttenuate(s16 *buf, int samples, int stereo)
1187 {
1188         int i, l, r;
1189         int ll = cdr.AttenuatorLeftToLeft;
1190         int lr = cdr.AttenuatorLeftToRight;
1191         int rl = cdr.AttenuatorRightToLeft;
1192         int rr = cdr.AttenuatorRightToRight;
1193
1194         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1195                 return;
1196
1197         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1198                 return;
1199
1200         if (stereo) {
1201                 for (i = 0; i < samples; i++) {
1202                         l = buf[i * 2];
1203                         r = buf[i * 2 + 1];
1204                         l = (l * ll + r * rl) >> 7;
1205                         r = (r * rr + l * lr) >> 7;
1206                         ssat32_to_16(l);
1207                         ssat32_to_16(r);
1208                         buf[i * 2] = l;
1209                         buf[i * 2 + 1] = r;
1210                 }
1211         }
1212         else {
1213                 for (i = 0; i < samples; i++) {
1214                         l = buf[i];
1215                         l = l * (ll + rl) >> 7;
1216                         //r = r * (rr + lr) >> 7;
1217                         ssat32_to_16(l);
1218                         //ssat32_to_16(r);
1219                         buf[i] = l;
1220                 }
1221         }
1222 }
1223
1224 void cdrReadInterrupt() {
1225         u8 *buf;
1226
1227         if (!cdr.Reading)
1228                 return;
1229
1230         if (cdr.Irq || cdr.Stat) {
1231                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1232                 CDREAD_INT(0x1000);
1233                 return;
1234         }
1235
1236         cdr.OCUP = 1;
1237         SetResultSize(1);
1238         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1239         cdr.StatP &= ~STATUS_SEEK;
1240         cdr.Result[0] = cdr.StatP;
1241         cdr.Seeked = SEEK_DONE;
1242
1243         ReadTrack(cdr.SetSectorPlay);
1244
1245         buf = CDR_getBuffer();
1246         if (buf == NULL)
1247                 cdr.NoErr = 0;
1248
1249         if (!cdr.NoErr) {
1250                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1251                 memset(cdr.Transfer, 0, DATA_SIZE);
1252                 cdr.Stat = DiskError;
1253                 cdr.Result[0] |= STATUS_ERROR;
1254                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1255                 return;
1256         }
1257
1258         memcpy(cdr.Transfer, buf, DATA_SIZE);
1259         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1260
1261
1262         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1263
1264         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1265                 // Firemen 2: Multi-XA files - briefings, cutscenes
1266                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1267                         cdr.File = cdr.Transfer[4 + 0];
1268                         cdr.Channel = cdr.Transfer[4 + 1];
1269                 }
1270
1271                 /* Gameblabla 
1272                  * Skips playing on channel 255.
1273                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1274                  * TODO : Check if this is the proper behaviour.
1275                  * */
1276                 if((cdr.Transfer[4 + 2] & 0x4) &&
1277                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1278                          (cdr.Transfer[4 + 0] == cdr.File) && cdr.Channel != 255) {
1279                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1280                         if (!ret) {
1281                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1282                                 /*
1283                                  * Gameblabla -
1284                                  * This is a hack for Megaman X4, Castlevania etc...
1285                                  * that regressed from the new m_locationChanged and CDROM timings changes.
1286                                  * It is mostly noticeable in Castevania however and the stuttering can be very jarring.
1287                                  * 
1288                                  * According to PCSX redux authors, we shouldn't cause a location change if
1289                                  * the sector difference is too small. 
1290                                  * I attempted to go with that approach but came empty handed.
1291                                  * So for now, let's just set cdr.m_locationChanged to false when playing back any ADPCM samples.
1292                                  * This does not regress Crash Team Racing's intro at least.
1293                                 */
1294                                 cdr.m_locationChanged = FALSE;
1295                                 SPU_playADPCMchannel(&cdr.Xa);
1296                                 cdr.FirstSector = 0;
1297                         }
1298                         else cdr.FirstSector = -1;
1299                 }
1300         }
1301
1302         cdr.SetSectorPlay[2]++;
1303         if (cdr.SetSectorPlay[2] == 75) {
1304                 cdr.SetSectorPlay[2] = 0;
1305                 cdr.SetSectorPlay[1]++;
1306                 if (cdr.SetSectorPlay[1] == 60) {
1307                         cdr.SetSectorPlay[1] = 0;
1308                         cdr.SetSectorPlay[0]++;
1309                 }
1310         }
1311
1312         cdr.Readed = 0;
1313
1314         uint32_t delay = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
1315         if (cdr.m_locationChanged) {
1316                 CDREAD_INT(delay * 30);
1317                 cdr.m_locationChanged = FALSE;
1318         } else {
1319                 CDREAD_INT(delay);
1320         }
1321
1322         /*
1323         Croc 2: $40 - only FORM1 (*)
1324         Judge Dredd: $C8 - only FORM1 (*)
1325         Sim Theme Park - no adpcm at all (zero)
1326         */
1327
1328         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1329                 cdr.Stat = DataReady;
1330                 setIrq(0x203);
1331         }
1332
1333         // update for CdlGetlocP
1334         ReadTrack(cdr.SetSectorPlay);
1335 }
1336
1337 /*
1338 cdrRead0:
1339         bit 0,1 - mode
1340         bit 2 - unknown
1341         bit 3 - unknown
1342         bit 4 - unknown
1343         bit 5 - 1 result ready
1344         bit 6 - 1 dma ready
1345         bit 7 - 1 command being processed
1346 */
1347
1348 unsigned char cdrRead0(void) {
1349         if (cdr.ResultReady)
1350                 cdr.Ctrl |= 0x20;
1351         else
1352                 cdr.Ctrl &= ~0x20;
1353
1354         if (cdr.OCUP)
1355                 cdr.Ctrl |= 0x40;
1356 //  else
1357 //              cdr.Ctrl &= ~0x40;
1358
1359         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1360         cdr.Ctrl |= 0x18;
1361
1362         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1363
1364         return psxHu8(0x1800) = cdr.Ctrl;
1365 }
1366
1367 void cdrWrite0(unsigned char rt) {
1368         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1369
1370         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1371 }
1372
1373 unsigned char cdrRead1(void) {
1374         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1375                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1376         else
1377                 psxHu8(0x1801) = 0;
1378         cdr.ResultP++;
1379         if (cdr.ResultP == cdr.ResultC)
1380                 cdr.ResultReady = 0;
1381
1382         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1383
1384         return psxHu8(0x1801);
1385 }
1386
1387 void cdrWrite1(unsigned char rt) {
1388         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1389         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1390
1391         switch (cdr.Ctrl & 3) {
1392         case 0:
1393                 break;
1394         case 3:
1395                 cdr.AttenuatorRightToRightT = rt;
1396                 return;
1397         default:
1398                 return;
1399         }
1400
1401         cdr.Cmd = rt;
1402         cdr.OCUP = 0;
1403
1404 #ifdef CDR_LOG_CMD_IRQ
1405         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1406         if (cdr.ParamC) {
1407                 int i;
1408                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1409                 for (i = 0; i < cdr.ParamC; i++)
1410                         SysPrintf(" %x,", cdr.Param[i]);
1411                 SysPrintf("}\n");
1412         } else {
1413                 SysPrintf("\n");
1414         }
1415 #endif
1416
1417         cdr.ResultReady = 0;
1418         cdr.Ctrl |= 0x80;
1419         // cdr.Stat = NoIntr; 
1420         AddIrqQueue(cdr.Cmd, 0x800);
1421
1422         switch (cdr.Cmd) {
1423
1424         case CdlReadN:
1425         case CdlReadS:
1426         case CdlPause:
1427                 StopCdda();
1428                 StopReading();
1429                 break;
1430
1431         case CdlInit:
1432         case CdlReset:
1433                 cdr.Seeked = SEEK_DONE;
1434                 StopCdda();
1435                 StopReading();
1436                 break;
1437
1438         case CdlSetmode:
1439                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1440
1441                 cdr.Mode = cdr.Param[0];
1442
1443                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1444                 // - fixes choppy movie sound
1445                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1446                         StopCdda();
1447                 break;
1448         }
1449 }
1450
1451 unsigned char cdrRead2(void) {
1452         unsigned char ret;
1453
1454         if (cdr.Readed == 0) {
1455                 ret = 0;
1456         } else {
1457                 ret = *pTransfer++;
1458         }
1459
1460         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1461         return ret;
1462 }
1463
1464 void cdrWrite2(unsigned char rt) {
1465         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1466         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1467
1468         switch (cdr.Ctrl & 3) {
1469         case 0:
1470                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1471                         cdr.Param[cdr.ParamC++] = rt;
1472                 return;
1473         case 1:
1474                 cdr.Reg2 = rt;
1475                 setIrq(0x204);
1476                 return;
1477         case 2:
1478                 cdr.AttenuatorLeftToLeftT = rt;
1479                 return;
1480         case 3:
1481                 cdr.AttenuatorRightToLeftT = rt;
1482                 return;
1483         }
1484 }
1485
1486 unsigned char cdrRead3(void) {
1487         if (cdr.Ctrl & 0x1)
1488                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1489         else
1490                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1491
1492         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1493         return psxHu8(0x1803);
1494 }
1495
1496 void cdrWrite3(unsigned char rt) {
1497         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1498         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1499
1500         switch (cdr.Ctrl & 3) {
1501         case 0:
1502                 break; // transfer
1503         case 1:
1504                 cdr.Stat &= ~rt;
1505
1506                 if (rt & 0x40)
1507                         cdr.ParamC = 0;
1508                 return;
1509         case 2:
1510                 cdr.AttenuatorLeftToRightT = rt;
1511                 return;
1512         case 3:
1513                 if (rt & 0x20) {
1514                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1515                         CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n",
1516                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1517                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1518                 }
1519                 return;
1520         }
1521
1522         if ((rt & 0x80) && cdr.Readed == 0) {
1523                 cdr.Readed = 1;
1524                 pTransfer = cdr.Transfer;
1525
1526                 switch (cdr.Mode & 0x30) {
1527                         case MODE_SIZE_2328:
1528                         case 0x00:
1529                                 pTransfer += 12;
1530                                 break;
1531
1532                         case MODE_SIZE_2340:
1533                                 pTransfer += 0;
1534                                 break;
1535
1536                         default:
1537                                 break;
1538                 }
1539         }
1540 }
1541
1542 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1543         u32 cdsize;
1544         int size;
1545         u8 *ptr;
1546
1547         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1548
1549         switch (chcr) {
1550                 case 0x11000000:
1551                 case 0x11400100:
1552                         if (cdr.Readed == 0) {
1553                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1554                                 break;
1555                         }
1556
1557                         cdsize = (bcr & 0xffff) * 4;
1558
1559                         // Ape Escape: bcr = 0001 / 0000
1560                         // - fix boot
1561                         if( cdsize == 0 )
1562                         {
1563                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1564                                         case MODE_SIZE_2340: cdsize = 2340; break;
1565                                         case MODE_SIZE_2328: cdsize = 2328; break;
1566                                         default:
1567                                         case MODE_SIZE_2048: cdsize = 2048; break;
1568                                 }
1569                         }
1570
1571
1572                         ptr = (u8 *)PSXM(madr);
1573                         if (ptr == NULL) {
1574                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1575                                 break;
1576                         }
1577
1578                         /*
1579                         GS CDX: Enhancement CD crash
1580                         - Setloc 0:0:0
1581                         - CdlPlay
1582                         - Spams DMA3 and gets buffer overrun
1583                         */
1584                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1585                         if (size > cdsize)
1586                                 size = cdsize;
1587                         if (size > 0)
1588                         {
1589                                 memcpy(ptr, pTransfer, size);
1590                         }
1591
1592                         psxCpu->Clear(madr, cdsize / 4);
1593                         pTransfer += cdsize;
1594
1595                         if( chcr == 0x11400100 ) {
1596                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1597                                 CDRDMA_INT( (cdsize/4) / 4 );
1598                         }
1599                         else if( chcr == 0x11000000 ) {
1600                                 // CDRDMA_INT( (cdsize/4) * 1 );
1601                                 // halted
1602                                 psxRegs.cycle += (cdsize/4) * 24/2;
1603                                 CDRDMA_INT(16);
1604                         }
1605                         return;
1606
1607                 default:
1608                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1609                         break;
1610         }
1611
1612         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1613         DMA_INTERRUPT(3);
1614 }
1615
1616 void cdrDmaInterrupt()
1617 {
1618         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1619         {
1620                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1621                 DMA_INTERRUPT(3);
1622         }
1623 }
1624
1625 static void getCdInfo(void)
1626 {
1627         u8 tmp;
1628
1629         CDR_getTN(cdr.ResultTN);
1630         CDR_getTD(0, cdr.SetSectorEnd);
1631         tmp = cdr.SetSectorEnd[0];
1632         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1633         cdr.SetSectorEnd[2] = tmp;
1634 }
1635
1636 void cdrReset() {
1637         memset(&cdr, 0, sizeof(cdr));
1638         cdr.CurTrack = 1;
1639         cdr.File = 1;
1640         cdr.Channel = 1;
1641         cdr.Reg2 = 0x1f;
1642         cdr.Stat = NoIntr;
1643         cdr.DriveState = DRIVESTATE_STANDBY;
1644         cdr.StatP = STATUS_ROTATING;
1645         pTransfer = cdr.Transfer;
1646         
1647         // BIOS player - default values
1648         cdr.AttenuatorLeftToLeft = 0x80;
1649         cdr.AttenuatorLeftToRight = 0x00;
1650         cdr.AttenuatorRightToLeft = 0x00;
1651         cdr.AttenuatorRightToRight = 0x80;
1652
1653         getCdInfo();
1654 }
1655
1656 int cdrFreeze(void *f, int Mode) {
1657         u32 tmp;
1658         u8 tmpp[3];
1659
1660         if (Mode == 0 && !Config.Cdda)
1661                 CDR_stop();
1662         
1663         cdr.freeze_ver = 0x63647202;
1664         gzfreeze(&cdr, sizeof(cdr));
1665         
1666         if (Mode == 1) {
1667                 cdr.ParamP = cdr.ParamC;
1668                 tmp = pTransfer - cdr.Transfer;
1669         }
1670
1671         gzfreeze(&tmp, sizeof(tmp));
1672
1673         if (Mode == 0) {
1674                 getCdInfo();
1675
1676                 pTransfer = cdr.Transfer + tmp;
1677
1678                 // read right sub data
1679                 tmpp[0] = btoi(cdr.Prev[0]);
1680                 tmpp[1] = btoi(cdr.Prev[1]);
1681                 tmpp[2] = btoi(cdr.Prev[2]);
1682                 cdr.Prev[0]++;
1683                 ReadTrack(tmpp);
1684
1685                 if (cdr.Play) {
1686                         if (cdr.freeze_ver < 0x63647202)
1687                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1688
1689                         Find_CurTrack(cdr.SetSectorPlay);
1690                         if (!Config.Cdda)
1691                                 CDR_play(cdr.SetSectorPlay);
1692                 }
1693
1694                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1695                         // old versions did not latch Reg2, have to fixup..
1696                         if (cdr.Reg2 == 0) {
1697                                 SysPrintf("cdrom: fixing up old savestate\n");
1698                                 cdr.Reg2 = 7;
1699                         }
1700                         // also did not save Attenuator..
1701                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1702                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1703                         {
1704                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1705                         }
1706                 }
1707         }
1708
1709         return 0;
1710 }
1711
1712 void LidInterrupt() {
1713         getCdInfo();
1714         StopCdda();
1715         cdrLidSeekInterrupt();
1716 }