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