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