cdrom: don't reschedule irqs
[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 unused4;
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 unsigned char *pTransfer;
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         cdr.Readed = 0;
1181 }
1182
1183 static void cdrReadInterrupt(void)
1184 {
1185         u8 *buf = NULL, *hdr;
1186
1187         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1188
1189         ReadTrack(cdr.SetSectorPlay);
1190         if (cdr.NoErr)
1191                 buf = CDR_getBuffer();
1192         if (buf == NULL)
1193                 cdr.NoErr = 0;
1194
1195         if (!cdr.NoErr) {
1196                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1197                 memset(cdr.Transfer, 0, DATA_SIZE);
1198                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1199                 return;
1200         }
1201
1202         if (!cdr.Irq1Pending)
1203                 cdrUpdateTransferBuf(buf);
1204
1205         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1206                 hdr = buf + 4;
1207                 // Firemen 2: Multi-XA files - briefings, cutscenes
1208                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1209                         cdr.File = hdr[0];
1210                         cdr.Channel = hdr[1];
1211                 }
1212
1213                 /* Gameblabla 
1214                  * Skips playing on channel 255.
1215                  * Fixes missing audio in Blue's Clues : Blue's Big Musical. (Should also fix Taxi 2)
1216                  * TODO : Check if this is the proper behaviour.
1217                  * */
1218                 if ((hdr[2] & 0x4) && hdr[0] == cdr.File && hdr[1] == cdr.Channel && cdr.Channel != 255) {
1219                         int ret = xa_decode_sector(&cdr.Xa, buf + 4, cdr.FirstSector);
1220                         if (!ret) {
1221                                 cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1222                                 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
1223                                 cdr.FirstSector = 0;
1224                         }
1225                         else cdr.FirstSector = -1;
1226                 }
1227         }
1228
1229         /*
1230         Croc 2: $40 - only FORM1 (*)
1231         Judge Dredd: $C8 - only FORM1 (*)
1232         Sim Theme Park - no adpcm at all (zero)
1233         */
1234
1235         if (!(cdr.Mode & MODE_STRSND) || !(buf[4+2] & 0x4))
1236                 cdrReadInterruptSetResult(cdr.StatP);
1237
1238         cdr.SetSectorPlay[2]++;
1239         if (cdr.SetSectorPlay[2] == 75) {
1240                 cdr.SetSectorPlay[2] = 0;
1241                 cdr.SetSectorPlay[1]++;
1242                 if (cdr.SetSectorPlay[1] == 60) {
1243                         cdr.SetSectorPlay[1] = 0;
1244                         cdr.SetSectorPlay[0]++;
1245                 }
1246         }
1247
1248         if (!cdr.Irq1Pending) {
1249                 // update for CdlGetlocP
1250                 ReadTrack(cdr.SetSectorPlay);
1251         }
1252
1253         CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1254 }
1255
1256 static void doMissedIrqs(void)
1257 {
1258         if (cdr.Irq1Pending)
1259         {
1260                 // hand out the "newest" sector, according to nocash
1261                 cdrUpdateTransferBuf(CDR_getBuffer());
1262                 CDR_LOG_I("cdrom: %x:%02x:%02x loaded on ack\n",
1263                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1264                 SetResultSize(1);
1265                 cdr.Result[0] = cdr.Irq1Pending;
1266                 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
1267                 cdr.Irq1Pending = 0;
1268                 setIrq(0x205);
1269                 return;
1270         }
1271         if (!(psxRegs.interrupt & (1 << PSXINT_CDR)) && cdr.CmdInProgress)
1272                 CDR_INT(256);
1273 }
1274
1275 /*
1276 cdrRead0:
1277         bit 0,1 - mode
1278         bit 2 - unknown
1279         bit 3 - unknown
1280         bit 4 - unknown
1281         bit 5 - 1 result ready
1282         bit 6 - 1 dma ready
1283         bit 7 - 1 command being processed
1284 */
1285
1286 unsigned char cdrRead0(void) {
1287         if (cdr.ResultReady)
1288                 cdr.Ctrl |= 0x20;
1289         else
1290                 cdr.Ctrl &= ~0x20;
1291
1292         cdr.Ctrl |= 0x40; // data fifo not empty
1293
1294         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1295         cdr.Ctrl |= 0x18;
1296
1297         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1298
1299         return psxHu8(0x1800) = cdr.Ctrl;
1300 }
1301
1302 void cdrWrite0(unsigned char rt) {
1303         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1304
1305         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1306 }
1307
1308 unsigned char cdrRead1(void) {
1309         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1310                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1311         else
1312                 psxHu8(0x1801) = 0;
1313         cdr.ResultP++;
1314         if (cdr.ResultP == cdr.ResultC)
1315                 cdr.ResultReady = 0;
1316
1317         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1318
1319         return psxHu8(0x1801);
1320 }
1321
1322 void cdrWrite1(unsigned char rt) {
1323         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1324         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1325
1326         switch (cdr.Ctrl & 3) {
1327         case 0:
1328                 break;
1329         case 3:
1330                 cdr.AttenuatorRightToRightT = rt;
1331                 return;
1332         default:
1333                 return;
1334         }
1335
1336 #ifdef CDR_LOG_CMD_IRQ
1337         SysPrintf("CD1 write: %x (%s)", rt, CmdName[rt]);
1338         if (cdr.ParamC) {
1339                 int i;
1340                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1341                 for (i = 0; i < cdr.ParamC; i++)
1342                         SysPrintf(" %x,", cdr.Param[i]);
1343                 SysPrintf("}\n");
1344         } else {
1345                 SysPrintf("\n");
1346         }
1347 #endif
1348
1349         cdr.ResultReady = 0;
1350         cdr.Ctrl |= 0x80;
1351
1352         if (!cdr.CmdInProgress) {
1353                 cdr.CmdInProgress = rt;
1354                 // should be something like 12k + controller delays
1355                 CDR_INT(5000);
1356         }
1357         else {
1358                 CDR_LOG_I("cdr: cmd while busy: %02x, prev %02x, busy %02x\n",
1359                         rt, cdr.Cmd, cdr.CmdInProgress);
1360                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1361                         cdr.CmdInProgress = rt;
1362         }
1363
1364         cdr.Cmd = rt;
1365 }
1366
1367 unsigned char cdrRead2(void) {
1368         unsigned char ret;
1369
1370         if (cdr.Readed == 0) {
1371                 ret = 0;
1372         } else {
1373                 ret = *pTransfer++;
1374         }
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         if ((rt & 0x80) && cdr.Readed == 0) {
1444                 cdr.Readed = 1;
1445                 pTransfer = cdr.Transfer;
1446
1447                 switch (cdr.Mode & 0x30) {
1448                         case MODE_SIZE_2328:
1449                         case 0x00:
1450                                 pTransfer += 12;
1451                                 break;
1452
1453                         case MODE_SIZE_2340:
1454                                 pTransfer += 0;
1455                                 break;
1456
1457                         default:
1458                                 break;
1459                 }
1460         }
1461 }
1462
1463 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1464         u32 cdsize;
1465         int size;
1466         u8 *ptr;
1467
1468         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1469
1470         switch (chcr) {
1471                 case 0x11000000:
1472                 case 0x11400100:
1473                         if (cdr.Readed == 0) {
1474                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1475                                 break;
1476                         }
1477
1478                         cdsize = (bcr & 0xffff) * 4;
1479
1480                         // Ape Escape: bcr = 0001 / 0000
1481                         // - fix boot
1482                         if( cdsize == 0 )
1483                         {
1484                                 switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
1485                                         case MODE_SIZE_2340: cdsize = 2340; break;
1486                                         case MODE_SIZE_2328: cdsize = 2328; break;
1487                                         default:
1488                                         case MODE_SIZE_2048: cdsize = 2048; break;
1489                                 }
1490                         }
1491
1492
1493                         ptr = (u8 *)PSXM(madr);
1494                         if (ptr == NULL) {
1495                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1496                                 break;
1497                         }
1498
1499                         /*
1500                         GS CDX: Enhancement CD crash
1501                         - Setloc 0:0:0
1502                         - CdlPlay
1503                         - Spams DMA3 and gets buffer overrun
1504                         */
1505                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1506                         if (size > cdsize)
1507                                 size = cdsize;
1508                         if (size > 0)
1509                         {
1510                                 memcpy(ptr, pTransfer, size);
1511                         }
1512
1513                         psxCpu->Clear(madr, cdsize / 4);
1514                         pTransfer += cdsize;
1515
1516                         if( chcr == 0x11400100 ) {
1517                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1518                                 CDRDMA_INT( (cdsize/4) / 4 );
1519                         }
1520                         else if( chcr == 0x11000000 ) {
1521                                 // CDRDMA_INT( (cdsize/4) * 1 );
1522                                 // halted
1523                                 psxRegs.cycle += (cdsize/4) * 24/2;
1524                                 CDRDMA_INT(16);
1525                         }
1526                         return;
1527
1528                 default:
1529                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1530                         break;
1531         }
1532
1533         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1534         DMA_INTERRUPT(3);
1535 }
1536
1537 void cdrDmaInterrupt(void)
1538 {
1539         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1540         {
1541                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1542                 DMA_INTERRUPT(3);
1543         }
1544 }
1545
1546 static void getCdInfo(void)
1547 {
1548         u8 tmp;
1549
1550         CDR_getTN(cdr.ResultTN);
1551         CDR_getTD(0, cdr.SetSectorEnd);
1552         tmp = cdr.SetSectorEnd[0];
1553         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1554         cdr.SetSectorEnd[2] = tmp;
1555 }
1556
1557 void cdrReset() {
1558         memset(&cdr, 0, sizeof(cdr));
1559         cdr.CurTrack = 1;
1560         cdr.File = 1;
1561         cdr.Channel = 1;
1562         cdr.Reg2 = 0x1f;
1563         cdr.Stat = NoIntr;
1564         cdr.DriveState = DRIVESTATE_STANDBY;
1565         cdr.StatP = STATUS_ROTATING;
1566         pTransfer = cdr.Transfer;
1567         
1568         // BIOS player - default values
1569         cdr.AttenuatorLeftToLeft = 0x80;
1570         cdr.AttenuatorLeftToRight = 0x00;
1571         cdr.AttenuatorRightToLeft = 0x00;
1572         cdr.AttenuatorRightToRight = 0x80;
1573
1574         getCdInfo();
1575 }
1576
1577 int cdrFreeze(void *f, int Mode) {
1578         u32 tmp;
1579         u8 tmpp[3];
1580
1581         if (Mode == 0 && !Config.Cdda)
1582                 CDR_stop();
1583         
1584         cdr.freeze_ver = 0x63647202;
1585         gzfreeze(&cdr, sizeof(cdr));
1586         
1587         if (Mode == 1) {
1588                 cdr.ParamP = cdr.ParamC;
1589                 tmp = pTransfer - cdr.Transfer;
1590         }
1591
1592         gzfreeze(&tmp, sizeof(tmp));
1593
1594         if (Mode == 0) {
1595                 getCdInfo();
1596
1597                 pTransfer = cdr.Transfer + tmp;
1598
1599                 // read right sub data
1600                 tmpp[0] = btoi(cdr.Prev[0]);
1601                 tmpp[1] = btoi(cdr.Prev[1]);
1602                 tmpp[2] = btoi(cdr.Prev[2]);
1603                 cdr.Prev[0]++;
1604                 ReadTrack(tmpp);
1605
1606                 if (cdr.Play) {
1607                         if (cdr.freeze_ver < 0x63647202)
1608                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1609
1610                         Find_CurTrack(cdr.SetSectorPlay);
1611                         if (!Config.Cdda)
1612                                 CDR_play(cdr.SetSectorPlay);
1613                         if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
1614                                 CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
1615                 }
1616
1617                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1618                         // old versions did not latch Reg2, have to fixup..
1619                         if (cdr.Reg2 == 0) {
1620                                 SysPrintf("cdrom: fixing up old savestate\n");
1621                                 cdr.Reg2 = 7;
1622                         }
1623                         // also did not save Attenuator..
1624                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1625                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1626                         {
1627                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1628                         }
1629                 }
1630         }
1631
1632         return 0;
1633 }
1634
1635 void LidInterrupt(void) {
1636         getCdInfo();
1637         cdrLidSeekInterrupt();
1638 }