cdrom: try some different seek times
[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 LastReadSeekCycles;
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         CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
452
453         if (memcmp(cdr.Prev, tmp, 3) == 0)
454                 return 1;
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         int s = MSF2SECT(time[0], time[1], time[2]);
466         u16 crc;
467
468         if (CheckSBI(s))
469                 return;
470
471         subq = (struct SubQ *)CDR_getBufferSub(s);
472         if (subq != NULL && cdr.CurTrack == 1) {
473                 crc = calcCrc((u8 *)subq + 12, 10);
474                 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
475                         cdr.subq.Track = subq->TrackNumber;
476                         cdr.subq.Index = subq->IndexNumber;
477                         memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
478                         memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
479                 }
480                 else {
481                         CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
482                                 time[0], time[1], time[2]);
483                 }
484         }
485         else {
486                 generate_subq(time);
487         }
488
489         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
490                 cdr.subq.Track, cdr.subq.Index,
491                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
492                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
493 }
494
495 static void cdrPlayInterrupt_Autopause()
496 {
497         u32 abs_lev_max = 0;
498         boolean abs_lev_chselect;
499         u32 i;
500
501         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
502                 CDR_LOG( "CDDA STOP\n" );
503
504                 // Magic the Gathering
505                 // - looping territory cdda
506
507                 // ...?
508                 //cdr.ResultReady = 1;
509                 //cdr.Stat = DataReady;
510                 cdr.Stat = DataEnd;
511                 setIrq(0x1000); // 0x1000 just for logging purposes
512
513                 StopCdda();
514                 SetPlaySeekRead(cdr.StatP, 0);
515         }
516         else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
517                  ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
518         {
519                 cdr.Result[0] = cdr.StatP;
520                 cdr.Result[1] = cdr.subq.Track;
521                 cdr.Result[2] = cdr.subq.Index;
522                 
523                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
524                 
525                 /* 8 is a hack. For accuracy, it should be 588. */
526                 for (i = 0; i < 8; i++)
527                 {
528                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
529                 }
530                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
531                 abs_lev_max |= abs_lev_chselect << 15;
532
533                 if (cdr.subq.Absolute[2] & 0x10) {
534                         cdr.Result[3] = cdr.subq.Relative[0];
535                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
536                         cdr.Result[5] = cdr.subq.Relative[2];
537                 }
538                 else {
539                         cdr.Result[3] = cdr.subq.Absolute[0];
540                         cdr.Result[4] = cdr.subq.Absolute[1];
541                         cdr.Result[5] = cdr.subq.Absolute[2];
542                 }
543
544                 cdr.Result[6] = abs_lev_max >> 0;
545                 cdr.Result[7] = abs_lev_max >> 8;
546
547                 // Rayman: Logo freeze (resultready + dataready)
548                 cdr.ResultReady = 1;
549                 cdr.Stat = DataReady;
550
551                 SetResultSize(8);
552                 setIrq(0x1001);
553         }
554
555         if (cdr.ReportDelay)
556                 cdr.ReportDelay--;
557 }
558
559 static int cdrSeekTime(unsigned char *target)
560 {
561         int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
562         int seekTime = abs(diff) * (cdReadTime / 2000);
563         int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
564         seekTime = MAX_VALUE(seekTime, 20000);
565
566         // need this stupidly long penalty or else Spyro2 intro desyncs
567         // note: if misapplied this breaks MGS cutscenes among other things
568         if (cyclesSinceRS > cdReadTime * 50)
569                 seekTime += cdReadTime * 25;
570         // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
571         // and then wants some slack time
572         else if (cyclesSinceRS < cdReadTime *3/2)
573                 seekTime += cdReadTime;
574
575         seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
576         CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
577                 (float)seekTime / cdReadTime, (float)cyclesSinceRS / 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 static void msfiSub(u8 *msfi, u32 count)
628 {
629         assert(count < 75);
630         msfi[2] -= count;
631         if ((s8)msfi[2] < 0) {
632                 msfi[2] += 75;
633                 msfi[1]--;
634                 if ((s8)msfi[1] < 0) {
635                         msfi[1] = 60;
636                         msfi[0]--;
637                 }
638         }
639 }
640
641 void cdrPlayReadInterrupt(void)
642 {
643         cdr.LastReadSeekCycles = psxRegs.cycle;
644
645         if (cdr.Reading) {
646                 cdrReadInterrupt();
647                 return;
648         }
649
650         if (!cdr.Play) return;
651
652         CDR_LOG( "CDDA - %d:%d:%d\n",
653                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
654
655         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
656         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
657                 StopCdda();
658                 SetPlaySeekRead(cdr.StatP, 0);
659                 cdr.TrackChanged = TRUE;
660         }
661         else {
662                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
663         }
664
665         if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
666                 cdrPlayInterrupt_Autopause();
667
668         if (!cdr.Muted && !Config.Cdda) {
669                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
670                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
671                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
672                 cdr.FirstSector = 0;
673         }
674
675         msfiAdd(cdr.SetSectorPlay, 1);
676
677         // update for CdlGetlocP/autopause
678         generate_subq(cdr.SetSectorPlay);
679
680         CDRPLAYREAD_INT(cdReadTime, 0);
681 }
682
683 #define CMD_PART2           0x100
684 #define CMD_WHILE_NOT_READY 0x200
685
686 void cdrInterrupt(void) {
687         int start_rotating = 0;
688         int error = 0;
689         u32 cycles, seekTime = 0;
690         u32 second_resp_time = 0;
691         const void *buf;
692         u8 ParamC;
693         u8 set_loc[3];
694         int read_ok;
695         u16 not_ready = 0;
696         u16 Cmd;
697         int i;
698
699         if (cdr.Stat) {
700                 CDR_LOG_I("cmd %02x with irqstat %x\n",
701                         cdr.CmdInProgress, cdr.Stat);
702                 return;
703         }
704         if (cdr.Irq1Pending) {
705                 // hand out the "newest" sector, according to nocash
706                 cdrUpdateTransferBuf(CDR_getBuffer());
707                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
708                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
709                         cdr.CmdInProgress, cdr.Irq1Pending);
710                 SetResultSize(1);
711                 cdr.Result[0] = cdr.Irq1Pending;
712                 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
713                 cdr.Irq1Pending = 0;
714                 setIrq(0x1003);
715                 return;
716         }
717
718         // default response
719         SetResultSize(1);
720         cdr.Result[0] = cdr.StatP;
721         cdr.Stat = Acknowledge;
722
723         Cmd = cdr.CmdInProgress;
724         cdr.CmdInProgress = 0;
725         ParamC = cdr.ParamC;
726
727         if (Cmd < 0x100) {
728                 cdr.Ctrl &= ~0x80;
729                 cdr.ParamC = 0;
730                 cdr.Cmd = 0;
731         }
732
733         switch (cdr.DriveState) {
734         case DRIVESTATE_PREPARE_CD:
735                 if (Cmd > 2) {
736                         // Syphon filter 2 expects commands to work shortly after it sees
737                         // STATUS_ROTATING, so give up trying to emulate the startup seq
738                         cdr.DriveState = DRIVESTATE_STANDBY;
739                         cdr.StatP &= ~STATUS_SEEK;
740                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
741                         break;
742                 }
743                 // fallthrough
744         case DRIVESTATE_LID_OPEN:
745         case DRIVESTATE_RESCAN_CD:
746                 // no disk or busy with the initial scan, allowed cmds are limited
747                 not_ready = CMD_WHILE_NOT_READY;
748                 break;
749         }
750
751         switch (Cmd | not_ready) {
752                 case CdlNop:
753                 case CdlNop + CMD_WHILE_NOT_READY:
754                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
755                                 cdr.StatP &= ~STATUS_SHELLOPEN;
756                         break;
757
758                 case CdlSetloc:
759                 case CdlSetloc + CMD_WHILE_NOT_READY:
760                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
761
762                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
763                         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))
764                         {
765                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
766                                 if (++cdr.errorRetryhack > 100)
767                                         break;
768                                 error = ERROR_INVALIDARG;
769                                 goto set_error;
770                         }
771                         else
772                         {
773                                 for (i = 0; i < 3; i++)
774                                         set_loc[i] = btoi(cdr.Param[i]);
775                                 memcpy(cdr.SetSector, set_loc, 3);
776                                 cdr.SetSector[3] = 0;
777                                 cdr.SetlocPending = 1;
778                                 cdr.errorRetryhack = 0;
779                         }
780                         break;
781
782                 do_CdlPlay:
783                 case CdlPlay:
784                         StopCdda();
785                         StopReading();
786
787                         cdr.FastBackward = 0;
788                         cdr.FastForward = 0;
789
790                         // BIOS CD Player
791                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
792
793                         if (ParamC != 0 && cdr.Param[0] != 0) {
794                                 int track = btoi( cdr.Param[0] );
795
796                                 if (track <= cdr.ResultTN[1])
797                                         cdr.CurTrack = track;
798
799                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
800
801                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
802                                         for (i = 0; i < 3; i++)
803                                                 set_loc[i] = cdr.ResultTD[2 - i];
804                                         seekTime = cdrSeekTime(set_loc);
805                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
806                                 }
807                         }
808                         else if (cdr.SetlocPending) {
809                                 seekTime = cdrSeekTime(cdr.SetSector);
810                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
811                         }
812                         else {
813                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
814                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
815                         }
816                         cdr.SetlocPending = 0;
817
818                         /*
819                         Rayman: detect track changes
820                         - fixes logo freeze
821
822                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
823                         - plays tracks without retry play
824
825                         Wild 9: skip PREGAP + starting accurate SubQ
826                         - plays tracks without retry play
827                         */
828                         Find_CurTrack(cdr.SetSectorPlay);
829                         generate_subq(cdr.SetSectorPlay);
830                         cdr.LocL[0] = LOCL_INVALID;
831                         cdr.SubqForwardSectors = 1;
832                         cdr.TrackChanged = FALSE;
833                         cdr.FirstSector = 1;
834                         cdr.ReportDelay = 60;
835                         cdr.sectorsRead = 0;
836
837                         if (!Config.Cdda)
838                                 CDR_play(cdr.SetSectorPlay);
839
840                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
841                         
842                         // BIOS player - set flag again
843                         cdr.Play = TRUE;
844
845                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
846                         start_rotating = 1;
847                         break;
848
849                 case CdlForward:
850                         // TODO: error 80 if stopped
851                         cdr.Stat = Complete;
852
853                         // GameShark CD Player: Calls 2x + Play 2x
854                         cdr.FastForward = 1;
855                         cdr.FastBackward = 0;
856                         break;
857
858                 case CdlBackward:
859                         cdr.Stat = Complete;
860
861                         // GameShark CD Player: Calls 2x + Play 2x
862                         cdr.FastBackward = 1;
863                         cdr.FastForward = 0;
864                         break;
865
866                 case CdlStandby:
867                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
868                                 error = ERROR_INVALIDARG;
869                                 goto set_error;
870                         }
871                         second_resp_time = cdReadTime * 125 / 2;
872                         start_rotating = 1;
873                         break;
874
875                 case CdlStandby + CMD_PART2:
876                         cdr.Stat = Complete;
877                         break;
878
879                 case CdlStop:
880                         if (cdr.Play) {
881                                 // grab time for current track
882                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
883
884                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
885                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
886                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
887                         }
888
889                         StopCdda();
890                         StopReading();
891                         SetPlaySeekRead(cdr.StatP, 0);
892                         cdr.StatP &= ~STATUS_ROTATING;
893                         cdr.LocL[0] = LOCL_INVALID;
894
895                         second_resp_time = 0x800;
896                         if (cdr.DriveState == DRIVESTATE_STANDBY)
897                                 second_resp_time = cdReadTime * 30 / 2;
898
899                         cdr.DriveState = DRIVESTATE_STOPPED;
900                         break;
901
902                 case CdlStop + CMD_PART2:
903                         cdr.Stat = Complete;
904                         break;
905
906                 case CdlPause:
907                         StopCdda();
908                         StopReading();
909
910                         // how the drive maintains the position while paused is quite
911                         // complicated, this is the minimum to make "Bedlam" happy
912                         msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
913                         cdr.sectorsRead = 0;
914
915                         /*
916                         Gundam Battle Assault 2: much slower (*)
917                         - Fixes boot, gameplay
918
919                         Hokuto no Ken 2: slower
920                         - Fixes intro + subtitles
921
922                         InuYasha - Feudal Fairy Tale: slower
923                         - Fixes battles
924                         */
925                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
926                          * The timings from Duckstation are based upon hardware tests.
927                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
928                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
929                          * */
930                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
931                         {
932                                 second_resp_time = 7000;
933                         }
934                         else
935                         {
936                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
937                         }
938                         SetPlaySeekRead(cdr.StatP, 0);
939                         break;
940
941                 case CdlPause + CMD_PART2:
942                         cdr.Stat = Complete;
943                         break;
944
945                 case CdlReset:
946                 case CdlReset + CMD_WHILE_NOT_READY:
947                         StopCdda();
948                         StopReading();
949                         SetPlaySeekRead(cdr.StatP, 0);
950                         cdr.LocL[0] = LOCL_INVALID;
951                         cdr.Muted = FALSE;
952                         cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
953                         second_resp_time = not_ready ? 70000 : 4100000;
954                         start_rotating = 1;
955                         break;
956
957                 case CdlReset + CMD_PART2:
958                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
959                         cdr.Stat = Complete;
960                         break;
961
962                 case CdlMute:
963                         cdr.Muted = TRUE;
964                         break;
965
966                 case CdlDemute:
967                         cdr.Muted = FALSE;
968                         break;
969
970                 case CdlSetfilter:
971                         cdr.File = cdr.Param[0];
972                         cdr.Channel = cdr.Param[1];
973                         break;
974
975                 case CdlSetmode:
976                 case CdlSetmode + CMD_WHILE_NOT_READY:
977                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
978                         cdr.Mode = cdr.Param[0];
979                         break;
980
981                 case CdlGetparam:
982                 case CdlGetparam + CMD_WHILE_NOT_READY:
983                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
984                         SetResultSize(5);
985                         cdr.Result[1] = cdr.Mode;
986                         cdr.Result[2] = 0;
987                         cdr.Result[3] = cdr.File;
988                         cdr.Result[4] = cdr.Channel;
989                         break;
990
991                 case CdlGetlocL:
992                         if (cdr.LocL[0] == LOCL_INVALID) {
993                                 error = 0x80;
994                                 goto set_error;
995                         }
996                         SetResultSize(8);
997                         memcpy(cdr.Result, cdr.LocL, 8);
998                         break;
999
1000                 case CdlGetlocP:
1001                         SetResultSize(8);
1002                         memcpy(&cdr.Result, &cdr.subq, 8);
1003                         break;
1004
1005                 case CdlReadT: // SetSession?
1006                         // really long
1007                         second_resp_time = cdReadTime * 290 / 4;
1008                         start_rotating = 1;
1009                         break;
1010
1011                 case CdlReadT + CMD_PART2:
1012                         cdr.Stat = Complete;
1013                         break;
1014
1015                 case CdlGetTN:
1016                         SetResultSize(3);
1017                         if (CDR_getTN(cdr.ResultTN) == -1) {
1018                                 cdr.Stat = DiskError;
1019                                 cdr.Result[0] |= STATUS_ERROR;
1020                         } else {
1021                                 cdr.Stat = Acknowledge;
1022                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
1023                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
1024                         }
1025                         break;
1026
1027                 case CdlGetTD:
1028                         cdr.Track = btoi(cdr.Param[0]);
1029                         SetResultSize(4);
1030                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1031                                 cdr.Stat = DiskError;
1032                                 cdr.Result[0] |= STATUS_ERROR;
1033                         } else {
1034                                 cdr.Stat = Acknowledge;
1035                                 cdr.Result[0] = cdr.StatP;
1036                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
1037                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
1038                                 /* According to Nocash's documentation, the function doesn't care about ff.
1039                                  * This can be seen also in Mednafen's implementation. */
1040                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1041                         }
1042                         break;
1043
1044                 case CdlSeekL:
1045                 case CdlSeekP:
1046                         StopCdda();
1047                         StopReading();
1048                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1049
1050                         seekTime = cdrSeekTime(cdr.SetSector);
1051                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1052                         /*
1053                         Crusaders of Might and Magic = 0.5x-4x
1054                         - fix cutscene speech start
1055
1056                         Eggs of Steel = 2x-?
1057                         - fix new game
1058
1059                         Medievil = ?-4x
1060                         - fix cutscene speech
1061
1062                         Rockman X5 = 0.5-4x
1063                         - fix capcom logo
1064                         */
1065                         second_resp_time = cdReadTime + seekTime;
1066                         start_rotating = 1;
1067                         break;
1068
1069                 case CdlSeekL + CMD_PART2:
1070                 case CdlSeekP + CMD_PART2:
1071                         SetPlaySeekRead(cdr.StatP, 0);
1072                         cdr.Result[0] = cdr.StatP;
1073                         cdr.Stat = Complete;
1074
1075                         Find_CurTrack(cdr.SetSectorPlay);
1076                         read_ok = ReadTrack(cdr.SetSectorPlay);
1077                         if (read_ok && (buf = CDR_getBuffer()))
1078                                 memcpy(cdr.LocL, buf, 8);
1079                         UpdateSubq(cdr.SetSectorPlay);
1080                         cdr.TrackChanged = FALSE;
1081                         cdr.LastReadSeekCycles = psxRegs.cycle;
1082                         break;
1083
1084                 case CdlTest:
1085                 case CdlTest + CMD_WHILE_NOT_READY:
1086                         switch (cdr.Param[0]) {
1087                                 case 0x20: // System Controller ROM Version
1088                                         SetResultSize(4);
1089                                         memcpy(cdr.Result, Test20, 4);
1090                                         break;
1091                                 case 0x22:
1092                                         SetResultSize(8);
1093                                         memcpy(cdr.Result, Test22, 4);
1094                                         break;
1095                                 case 0x23: case 0x24:
1096                                         SetResultSize(8);
1097                                         memcpy(cdr.Result, Test23, 4);
1098                                         break;
1099                         }
1100                         break;
1101
1102                 case CdlID:
1103                         second_resp_time = 20480;
1104                         break;
1105
1106                 case CdlID + CMD_PART2:
1107                         SetResultSize(8);
1108                         cdr.Result[0] = cdr.StatP;
1109                         cdr.Result[1] = 0;
1110                         cdr.Result[2] = 0;
1111                         cdr.Result[3] = 0;
1112
1113                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1114                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1115                                 cdr.Result[1] = 0xc0;
1116                         }
1117                         else {
1118                                 if (stat.Type == 2)
1119                                         cdr.Result[1] |= 0x10;
1120                                 if (CdromId[0] == '\0')
1121                                         cdr.Result[1] |= 0x80;
1122                         }
1123                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1124
1125                         /* This adds the string "PCSX" in Playstation bios boot screen */
1126                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1127                         cdr.Stat = Complete;
1128                         break;
1129
1130                 case CdlInit:
1131                 case CdlInit + CMD_WHILE_NOT_READY:
1132                         StopCdda();
1133                         StopReading();
1134                         SetPlaySeekRead(cdr.StatP, 0);
1135                         // yes, it really sets STATUS_SHELLOPEN
1136                         cdr.StatP |= STATUS_SHELLOPEN;
1137                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1138                         set_event(PSXINT_CDRLID, 20480);
1139                         start_rotating = 1;
1140                         break;
1141
1142                 case CdlGetQ:
1143                 case CdlGetQ + CMD_WHILE_NOT_READY:
1144                         break;
1145
1146                 case CdlReadToc:
1147                 case CdlReadToc + CMD_WHILE_NOT_READY:
1148                         cdr.LocL[0] = LOCL_INVALID;
1149                         second_resp_time = cdReadTime * 180 / 4;
1150                         start_rotating = 1;
1151                         break;
1152
1153                 case CdlReadToc + CMD_PART2:
1154                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1155                         cdr.Stat = Complete;
1156                         break;
1157
1158                 case CdlReadN:
1159                 case CdlReadS:
1160                         if (cdr.Reading && !cdr.SetlocPending)
1161                                 break;
1162
1163                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1164
1165                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1166                                 // Read* acts as play for cdda tracks in cdda mode
1167                                 goto do_CdlPlay;
1168
1169                         StopCdda();
1170                         if (cdr.SetlocPending) {
1171                                 seekTime = cdrSeekTime(cdr.SetSector);
1172                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1173                                 cdr.SetlocPending = 0;
1174                         }
1175                         cdr.Reading = 1;
1176                         cdr.FirstSector = 1;
1177
1178                         // Fighting Force 2 - update subq time immediately
1179                         // - fixes new game
1180                         UpdateSubq(cdr.SetSectorPlay);
1181                         cdr.LocL[0] = LOCL_INVALID;
1182                         cdr.SubqForwardSectors = 1;
1183                         cdr.sectorsRead = 0;
1184
1185                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1186                         cycles += seekTime;
1187                         if (Config.hacks.cdr_read_timing)
1188                                 cycles = cdrAlignTimingHack(cycles);
1189                         CDRPLAYREAD_INT(cycles, 1);
1190
1191                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1192                         start_rotating = 1;
1193                         break;
1194
1195                 case CdlSync:
1196                 default:
1197                         error = ERROR_INVALIDCMD;
1198                         // FALLTHROUGH
1199
1200                 set_error:
1201                         SetResultSize(2);
1202                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1203                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1204                         cdr.Stat = DiskError;
1205                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1206                         break;
1207         }
1208
1209         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1210                 cdr.DriveState = DRIVESTATE_STANDBY;
1211                 cdr.StatP |= STATUS_ROTATING;
1212         }
1213
1214         if (second_resp_time) {
1215                 cdr.CmdInProgress = Cmd | 0x100;
1216                 set_event(PSXINT_CDR, second_resp_time);
1217         }
1218         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1219                 cdr.CmdInProgress = cdr.Cmd;
1220                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1221         }
1222
1223         setIrq(Cmd);
1224 }
1225
1226 #ifdef HAVE_ARMV7
1227  #define ssat32_to_16(v) \
1228   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1229 #else
1230  #define ssat32_to_16(v) do { \
1231   if (v < -32768) v = -32768; \
1232   else if (v > 32767) v = 32767; \
1233  } while (0)
1234 #endif
1235
1236 static void cdrPrepCdda(s16 *buf, int samples)
1237 {
1238 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1239         int i;
1240         for (i = 0; i < samples; i++) {
1241                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1242                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1243         }
1244 #endif
1245 }
1246
1247 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1248 {
1249         int i, l, r;
1250         int ll = cdr.AttenuatorLeftToLeft;
1251         int lr = cdr.AttenuatorLeftToRight;
1252         int rl = cdr.AttenuatorRightToLeft;
1253         int rr = cdr.AttenuatorRightToRight;
1254
1255         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1256                 return;
1257
1258         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1259                 return;
1260
1261         if (stereo) {
1262                 for (i = 0; i < samples; i++) {
1263                         l = buf[i * 2];
1264                         r = buf[i * 2 + 1];
1265                         l = (l * ll + r * rl) >> 7;
1266                         r = (r * rr + l * lr) >> 7;
1267                         ssat32_to_16(l);
1268                         ssat32_to_16(r);
1269                         buf[i * 2] = l;
1270                         buf[i * 2 + 1] = r;
1271                 }
1272         }
1273         else {
1274                 for (i = 0; i < samples; i++) {
1275                         l = buf[i];
1276                         l = l * (ll + rl) >> 7;
1277                         //r = r * (rr + lr) >> 7;
1278                         ssat32_to_16(l);
1279                         //ssat32_to_16(r);
1280                         buf[i] = l;
1281                 }
1282         }
1283 }
1284
1285 static void cdrReadInterruptSetResult(unsigned char result)
1286 {
1287         if (cdr.Stat) {
1288                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1289                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1290                         cdr.CmdInProgress, cdr.Stat);
1291                 cdr.Irq1Pending = result;
1292                 return;
1293         }
1294         SetResultSize(1);
1295         cdr.Result[0] = result;
1296         cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1297         setIrq(0x1004);
1298 }
1299
1300 static void cdrUpdateTransferBuf(const u8 *buf)
1301 {
1302         if (!buf)
1303                 return;
1304         memcpy(cdr.Transfer, buf, DATA_SIZE);
1305         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1306         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1307                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1308         if (cdr.FifoOffset < 2048 + 12)
1309                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1310 }
1311
1312 static void cdrReadInterrupt(void)
1313 {
1314         u8 *buf = NULL, *hdr;
1315         u8 subqPos[3];
1316         int read_ok;
1317
1318         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1319         msfiAdd(subqPos, cdr.SubqForwardSectors);
1320         UpdateSubq(subqPos);
1321         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1322                 cdr.SubqForwardSectors++;
1323                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1324                 return;
1325         }
1326
1327         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1328         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1329         cdr.sectorsRead++;
1330
1331         read_ok = ReadTrack(cdr.SetSectorPlay);
1332         if (read_ok)
1333                 buf = CDR_getBuffer();
1334         if (buf == NULL)
1335                 read_ok = 0;
1336
1337         if (!read_ok) {
1338                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1339                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1340                 return;
1341         }
1342         memcpy(cdr.LocL, buf, 8);
1343
1344         if (!cdr.Stat && !cdr.Irq1Pending)
1345                 cdrUpdateTransferBuf(buf);
1346
1347         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1348                 hdr = buf + 4;
1349                 // Firemen 2: Multi-XA files - briefings, cutscenes
1350                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1351                         cdr.File = hdr[0];
1352                         cdr.Channel = hdr[1];
1353                 }
1354
1355                 /* Gameblabla 
1356                  * Skips playing on channel 255.
1357                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1358                  * TODO : Check if this is the proper behaviour.
1359                  * */
1360                 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1361                         int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1362                         if (!ret) {
1363                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1364                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1365                                 cdr.FirstSector = 0;
1366                         }
1367                         else cdr.FirstSector = -1;
1368                 }
1369         }
1370
1371         /*
1372         Croc 2: $40 - only FORM1 (*)
1373         Judge Dredd: $C8 - only FORM1 (*)
1374         Sim Theme Park - no adpcm at all (zero)
1375         */
1376
1377         if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1378                 cdrReadInterruptSetResult(cdr.StatP);
1379
1380         msfiAdd(cdr.SetSectorPlay, 1);
1381
1382         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1383 }
1384
1385 /*
1386 cdrRead0:
1387         bit 0,1 - mode
1388         bit 2 - unknown
1389         bit 3 - unknown
1390         bit 4 - unknown
1391         bit 5 - 1 result ready
1392         bit 6 - 1 dma ready
1393         bit 7 - 1 command being processed
1394 */
1395
1396 unsigned char cdrRead0(void) {
1397         if (cdr.ResultReady)
1398                 cdr.Ctrl |= 0x20;
1399         else
1400                 cdr.Ctrl &= ~0x20;
1401
1402         cdr.Ctrl |= 0x40; // data fifo not empty
1403
1404         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1405         cdr.Ctrl |= 0x18;
1406
1407         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1408
1409         return psxHu8(0x1800) = cdr.Ctrl;
1410 }
1411
1412 void cdrWrite0(unsigned char rt) {
1413         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1414
1415         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1416 }
1417
1418 unsigned char cdrRead1(void) {
1419         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1420                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1421         else
1422                 psxHu8(0x1801) = 0;
1423         cdr.ResultP++;
1424         if (cdr.ResultP == cdr.ResultC)
1425                 cdr.ResultReady = 0;
1426
1427         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1428
1429         return psxHu8(0x1801);
1430 }
1431
1432 void cdrWrite1(unsigned char rt) {
1433         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1434         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1435
1436         switch (cdr.Ctrl & 3) {
1437         case 0:
1438                 break;
1439         case 3:
1440                 cdr.AttenuatorRightToRightT = rt;
1441                 return;
1442         default:
1443                 return;
1444         }
1445
1446 #ifdef CDR_LOG_CMD_IRQ
1447         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1448         if (cdr.ParamC) {
1449                 int i;
1450                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1451                 for (i = 0; i < cdr.ParamC; i++)
1452                         SysPrintf(" %x,", cdr.Param[i]);
1453                 SysPrintf("}\n");
1454         } else {
1455                 SysPrintf("\n");
1456         }
1457 #endif
1458
1459         cdr.ResultReady = 0;
1460         cdr.Ctrl |= 0x80;
1461
1462         if (!cdr.CmdInProgress) {
1463                 cdr.CmdInProgress = rt;
1464                 // should be something like 12k + controller delays
1465                 set_event(PSXINT_CDR, 5000);
1466         }
1467         else {
1468                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1469                         rt, cdr.Cmd, cdr.CmdInProgress);
1470                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1471                         cdr.CmdInProgress = rt;
1472         }
1473
1474         cdr.Cmd = rt;
1475 }
1476
1477 unsigned char cdrRead2(void) {
1478         unsigned char ret = cdr.Transfer[0x920];
1479
1480         if (cdr.FifoOffset < cdr.FifoSize)
1481                 ret = cdr.Transfer[cdr.FifoOffset++];
1482         else
1483                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1484
1485         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1486         return ret;
1487 }
1488
1489 void cdrWrite2(unsigned char rt) {
1490         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1491         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1492
1493         switch (cdr.Ctrl & 3) {
1494         case 0:
1495                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1496                         cdr.Param[cdr.ParamC++] = rt;
1497                 return;
1498         case 1:
1499                 cdr.Reg2 = rt;
1500                 setIrq(0x1005);
1501                 return;
1502         case 2:
1503                 cdr.AttenuatorLeftToLeftT = rt;
1504                 return;
1505         case 3:
1506                 cdr.AttenuatorRightToLeftT = rt;
1507                 return;
1508         }
1509 }
1510
1511 unsigned char cdrRead3(void) {
1512         if (cdr.Ctrl & 0x1)
1513                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1514         else
1515                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1516
1517         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1518         return psxHu8(0x1803);
1519 }
1520
1521 void cdrWrite3(unsigned char rt) {
1522         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1523         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1524
1525         switch (cdr.Ctrl & 3) {
1526         case 0:
1527                 break; // transfer
1528         case 1:
1529                 if (cdr.Stat & rt) {
1530                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1531                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1532                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1533 #ifdef CDR_LOG_CMD_IRQ
1534                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1535                                 !!pending, cdr.CmdInProgress,
1536                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1537 #endif
1538                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1539                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1540                         {
1541                                 s32 c = 2048;
1542                                 if (cdr.CmdInProgress) {
1543                                         c = 2048 - (psxRegs.cycle - nextCycle);
1544                                         c = MAX_VALUE(c, 512);
1545                                 }
1546                                 set_event(PSXINT_CDR, c);
1547                         }
1548                 }
1549                 cdr.Stat &= ~rt;
1550
1551                 if (rt & 0x40)
1552                         cdr.ParamC = 0;
1553                 return;
1554         case 2:
1555                 cdr.AttenuatorLeftToRightT = rt;
1556                 return;
1557         case 3:
1558                 if (rt & 0x20) {
1559                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1560                         CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1561                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1562                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1563                 }
1564                 return;
1565         }
1566
1567         // test: Viewpoint
1568         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1569                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1570         }
1571         else if (rt & 0x80) {
1572                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1573                         case MODE_SIZE_2328:
1574                         case 0x00:
1575                                 cdr.FifoOffset = 12;
1576                                 cdr.FifoSize = 2048 + 12;
1577                                 break;
1578
1579                         case MODE_SIZE_2340:
1580                         default:
1581                                 cdr.FifoOffset = 0;
1582                                 cdr.FifoSize = 2340;
1583                                 break;
1584                 }
1585         }
1586         else if (!(rt & 0xc0))
1587                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1588 }
1589
1590 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1591         u32 cdsize, max_words;
1592         int size;
1593         u8 *ptr;
1594
1595 #if 0
1596         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1597         if (cdr.FifoOffset == 0) {
1598                 ptr = cdr.Transfer;
1599                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1600         }
1601         SysPrintf("\n");
1602 #endif
1603
1604         switch (chcr & 0x71000000) {
1605                 case 0x11000000:
1606                         ptr = getDmaRam(madr, &max_words);
1607                         if (ptr == INVALID_PTR) {
1608                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1609                                 break;
1610                         }
1611
1612                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1613
1614                         /*
1615                         GS CDX: Enhancement CD crash
1616                         - Setloc 0:0:0
1617                         - CdlPlay
1618                         - Spams DMA3 and gets buffer overrun
1619                         */
1620                         size = DATA_SIZE - cdr.FifoOffset;
1621                         if (size > cdsize)
1622                                 size = cdsize;
1623                         if (size > max_words * 4)
1624                                 size = max_words * 4;
1625                         if (size > 0)
1626                         {
1627                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1628                                 cdr.FifoOffset += size;
1629                         }
1630                         if (size < cdsize) {
1631                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1632                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1633                         }
1634                         psxCpu->Clear(madr, cdsize / 4);
1635
1636                         set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1637
1638                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1639                         if (chcr & 0x100) {
1640                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1641                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1642                         }
1643                         else {
1644                                 // halted
1645                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1646                         }
1647                         return;
1648
1649                 default:
1650                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1651                         break;
1652         }
1653
1654         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1655         DMA_INTERRUPT(3);
1656 }
1657
1658 void cdrDmaInterrupt(void)
1659 {
1660         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1661         {
1662                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1663                 DMA_INTERRUPT(3);
1664         }
1665 }
1666
1667 static void getCdInfo(void)
1668 {
1669         u8 tmp;
1670
1671         CDR_getTN(cdr.ResultTN);
1672         CDR_getTD(0, cdr.SetSectorEnd);
1673         tmp = cdr.SetSectorEnd[0];
1674         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1675         cdr.SetSectorEnd[2] = tmp;
1676 }
1677
1678 void cdrReset() {
1679         memset(&cdr, 0, sizeof(cdr));
1680         cdr.CurTrack = 1;
1681         cdr.File = 1;
1682         cdr.Channel = 1;
1683         cdr.Reg2 = 0x1f;
1684         cdr.Stat = NoIntr;
1685         cdr.FifoOffset = DATA_SIZE; // fifo empty
1686         if (CdromId[0] == '\0') {
1687                 cdr.DriveState = DRIVESTATE_STOPPED;
1688                 cdr.StatP = 0;
1689         }
1690         else {
1691                 cdr.DriveState = DRIVESTATE_STANDBY;
1692                 cdr.StatP = STATUS_ROTATING;
1693         }
1694         
1695         // BIOS player - default values
1696         cdr.AttenuatorLeftToLeft = 0x80;
1697         cdr.AttenuatorLeftToRight = 0x00;
1698         cdr.AttenuatorRightToLeft = 0x00;
1699         cdr.AttenuatorRightToRight = 0x80;
1700
1701         getCdInfo();
1702 }
1703
1704 int cdrFreeze(void *f, int Mode) {
1705         u32 tmp;
1706         u8 tmpp[3];
1707
1708         if (Mode == 0 && !Config.Cdda)
1709                 CDR_stop();
1710         
1711         cdr.freeze_ver = 0x63647202;
1712         gzfreeze(&cdr, sizeof(cdr));
1713         
1714         if (Mode == 1) {
1715                 cdr.ParamP = cdr.ParamC;
1716                 tmp = cdr.FifoOffset;
1717         }
1718
1719         gzfreeze(&tmp, sizeof(tmp));
1720
1721         if (Mode == 0) {
1722                 getCdInfo();
1723
1724                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1725                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1726                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1727                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1728
1729                 // read right sub data
1730                 tmpp[0] = btoi(cdr.Prev[0]);
1731                 tmpp[1] = btoi(cdr.Prev[1]);
1732                 tmpp[2] = btoi(cdr.Prev[2]);
1733                 cdr.Prev[0]++;
1734                 ReadTrack(tmpp);
1735
1736                 if (cdr.Play) {
1737                         if (cdr.freeze_ver < 0x63647202)
1738                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1739
1740                         Find_CurTrack(cdr.SetSectorPlay);
1741                         if (!Config.Cdda)
1742                                 CDR_play(cdr.SetSectorPlay);
1743                 }
1744
1745                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1746                         // old versions did not latch Reg2, have to fixup..
1747                         if (cdr.Reg2 == 0) {
1748                                 SysPrintf("cdrom: fixing up old savestate\n");
1749                                 cdr.Reg2 = 7;
1750                         }
1751                         // also did not save Attenuator..
1752                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1753                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1754                         {
1755                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1756                         }
1757                 }
1758         }
1759
1760         return 0;
1761 }
1762
1763 void LidInterrupt(void) {
1764         getCdInfo();
1765         cdrLidSeekInterrupt();
1766 }