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