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