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