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