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