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