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