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