cdrom: implement xa buffering somewhat
[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 <assert.h>
25 #include "cdrom.h"
26 #include "misc.h"
27 #include "ppf.h"
28 #include "psxdma.h"
29 #include "psxevents.h"
30 #include "arm_features.h"
31
32 /* logging */
33 #if 0
34 #define CDR_LOG SysPrintf
35 #else
36 #define CDR_LOG(...)
37 #endif
38 #if 0
39 #define CDR_LOG_I SysPrintf
40 #else
41 #define CDR_LOG_I(fmt, ...) \
42         log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
43 #endif
44 #if 0
45 #define CDR_LOG_IO SysPrintf
46 #else
47 #define CDR_LOG_IO(...)
48 #endif
49 //#define CDR_LOG_CMD_IRQ
50
51 static struct {
52         // unused members maintain savesate compatibility
53         unsigned char unused0;
54         unsigned char unused1;
55         unsigned char Reg2;
56         unsigned char unused2;
57         unsigned char Ctrl;
58         unsigned char Stat;
59
60         unsigned char StatP;
61
62         unsigned char Transfer[DATA_SIZE];
63         struct {
64                 unsigned char Track;
65                 unsigned char Index;
66                 unsigned char Relative[3];
67                 unsigned char Absolute[3];
68         } subq;
69         unsigned char TrackChanged;
70         unsigned char ReportDelay;
71         unsigned char unused3;
72         unsigned short sectorsRead;
73         unsigned int  freeze_ver;
74
75         unsigned char Prev[4];
76         unsigned char Param[8];
77         unsigned char Result[16];
78
79         unsigned char ParamC;
80         unsigned char ParamP;
81         unsigned char ResultC;
82         unsigned char ResultP;
83         unsigned char ResultReady;
84         unsigned char Cmd;
85         unsigned char SubqForwardSectors;
86         unsigned char SetlocPending;
87         u32 Reading;
88
89         unsigned char ResultTN[6];
90         unsigned char ResultTD[4];
91         unsigned char SetSectorPlay[4];
92         unsigned char SetSectorEnd[4];
93         unsigned char SetSector[4];
94         unsigned char Track;
95         boolean Play, Muted;
96         int CurTrack;
97         unsigned char Mode;
98         unsigned char FileChannelSelected;
99         unsigned char CurFile, CurChannel;
100         int FilterFile, FilterChannel;
101         unsigned char LocL[8];
102         int unused4;
103
104         xa_decode_t Xa;
105
106         u16 FifoOffset;
107         u16 FifoSize;
108
109         u16 CmdInProgress;
110         u8 Irq1Pending;
111         u8 AdpcmActive;
112         u32 LastReadSeekCycles;
113
114         u8 unused7;
115
116         u8 DriveState;
117         u8 FastForward;
118         u8 FastBackward;
119         u8 errorRetryhack;
120
121         u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
122         u8 AttenuatorRightToRight, AttenuatorRightToLeft;
123         u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
124         u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
125 } cdr;
126 static s16 read_buf[CD_FRAMESIZE_RAW/2];
127
128 /* CD-ROM magic numbers */
129 #define CdlSync        0  /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
130 #define CdlNop         1
131 #define CdlSetloc      2
132 #define CdlPlay        3
133 #define CdlForward     4
134 #define CdlBackward    5
135 #define CdlReadN       6
136 #define CdlStandby     7
137 #define CdlStop        8
138 #define CdlPause       9
139 #define CdlReset       10
140 #define CdlMute        11
141 #define CdlDemute      12
142 #define CdlSetfilter   13
143 #define CdlSetmode     14
144 #define CdlGetparam    15
145 #define CdlGetlocL     16
146 #define CdlGetlocP     17
147 #define CdlReadT       18
148 #define CdlGetTN       19
149 #define CdlGetTD       20
150 #define CdlSeekL       21
151 #define CdlSeekP       22
152 #define CdlSetclock    23
153 #define CdlGetclock    24
154 #define CdlTest        25
155 #define CdlID          26
156 #define CdlReadS       27
157 #define CdlInit        28
158 #define CdlGetQ        29
159 #define CdlReadToc     30
160
161 #ifdef CDR_LOG_CMD_IRQ
162 static const char * const CmdName[0x100] = {
163     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
164     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
165     "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
166     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
167     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
168     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
169     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
170     "CdlInit",     NULL,           "CDlReadToc", NULL
171 };
172 #endif
173
174 unsigned char Test04[] = { 0 };
175 unsigned char Test05[] = { 0 };
176 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
177 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
178 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
179
180 // cdr.Stat:
181 #define NoIntr          0
182 #define DataReady       1
183 #define Complete        2
184 #define Acknowledge     3
185 #define DataEnd         4
186 #define DiskError       5
187
188 /* Modes flags */
189 #define MODE_SPEED       (1<<7) // 0x80
190 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
191 #define MODE_SIZE_2340   (1<<5) // 0x20
192 #define MODE_SIZE_2328   (1<<4) // 0x10
193 #define MODE_SIZE_2048   (0<<4) // 0x00
194 #define MODE_SF          (1<<3) // 0x08 channel on/off
195 #define MODE_REPORT      (1<<2) // 0x04
196 #define MODE_AUTOPAUSE   (1<<1) // 0x02
197 #define MODE_CDDA        (1<<0) // 0x01
198
199 /* Status flags */
200 #define STATUS_PLAY      (1<<7) // 0x80
201 #define STATUS_SEEK      (1<<6) // 0x40
202 #define STATUS_READ      (1<<5) // 0x20
203 #define STATUS_SHELLOPEN (1<<4) // 0x10
204 #define STATUS_UNKNOWN3  (1<<3) // 0x08
205 #define STATUS_SEEKERROR (1<<2) // 0x04
206 #define STATUS_ROTATING  (1<<1) // 0x02
207 #define STATUS_ERROR     (1<<0) // 0x01
208
209 /* Errors */
210 #define ERROR_NOTREADY   (1<<7) // 0x80
211 #define ERROR_INVALIDCMD (1<<6) // 0x40
212 #define ERROR_INVALIDARG (1<<5) // 0x20
213 #define ERROR_SHELLOPEN  (1<<3) // 0x08
214
215 // 1x = 75 sectors per second
216 // PSXCLK = 1 sec in the ps
217 // so (PSXCLK / 75) = cdr read time (linuzappz)
218 #define cdReadTime (PSXCLK / 75)
219
220 #define LOCL_INVALID 0xff
221 #define SUBQ_FORWARD_SECTORS 2u
222
223 enum drive_state {
224         DRIVESTATE_STANDBY = 0, // pause, play, read
225         DRIVESTATE_LID_OPEN,
226         DRIVESTATE_RESCAN_CD,
227         DRIVESTATE_PREPARE_CD,
228         DRIVESTATE_STOPPED,
229 };
230
231 static struct CdrStat stat;
232
233 static unsigned int msf2sec(const u8 *msf) {
234         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
235 }
236
237 // for that weird psemu API..
238 static unsigned int fsm2sec(const u8 *msf) {
239         return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
240 }
241
242 static void sec2msf(unsigned int s, u8 *msf) {
243         msf[0] = s / 75 / 60;
244         s = s - msf[0] * 75 * 60;
245         msf[1] = s / 75;
246         s = s - msf[1] * 75;
247         msf[2] = s;
248 }
249
250 // cdrPlayReadInterrupt
251 #define CDRPLAYREAD_INT(eCycle, isFirst) { \
252         u32 e_ = eCycle; \
253         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
254         if (isFirst) \
255                 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
256         else \
257                 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
258         psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
259         set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
260 }
261
262 #define StopReading() { \
263         cdr.Reading = 0; \
264         psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
265 }
266
267 #define StopCdda() { \
268         if (cdr.Play && !Config.Cdda) CDR_stop(); \
269         cdr.Play = FALSE; \
270         cdr.FastForward = 0; \
271         cdr.FastBackward = 0; \
272 }
273
274 #define SetPlaySeekRead(x, f) { \
275         x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
276         x |= f; \
277 }
278
279 #define SetResultSize(size) { \
280         cdr.ResultP = 0; \
281         cdr.ResultC = size; \
282         cdr.ResultReady = 1; \
283 }
284
285 static void setIrq(int log_cmd)
286 {
287         if (cdr.Stat & cdr.Reg2)
288                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
289
290 #ifdef CDR_LOG_CMD_IRQ
291         if (cdr.Stat)
292         {
293                 int i;
294                 CDR_LOG_I("CDR IRQ=%d cmd %02x stat %02x: ",
295                         !!(cdr.Stat & cdr.Reg2), log_cmd, cdr.Stat);
296                 for (i = 0; i < cdr.ResultC; i++)
297                         SysPrintf("%02x ", cdr.Result[i]);
298                 SysPrintf("\n");
299         }
300 #endif
301 }
302
303 // timing used in this function was taken from tests on real hardware
304 // (yes it's slow, but you probably don't want to modify it)
305 void cdrLidSeekInterrupt(void)
306 {
307         CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
308
309         switch (cdr.DriveState) {
310         default:
311         case DRIVESTATE_STANDBY:
312                 StopCdda();
313                 //StopReading();
314                 SetPlaySeekRead(cdr.StatP, 0);
315
316                 if (CDR_getStatus(&stat) == -1)
317                         return;
318
319                 if (stat.Status & STATUS_SHELLOPEN)
320                 {
321                         memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
322                         cdr.DriveState = DRIVESTATE_LID_OPEN;
323                         set_event(PSXINT_CDRLID, 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                         SetPlaySeekRead(cdr.StatP, 0);
334                         cdr.StatP |= STATUS_SHELLOPEN;
335
336                         // IIRC this sometimes doesn't happen on real hw
337                         // (when lots of commands are sent?)
338                         if (cdr.Reading) {
339                                 StopReading();
340                                 SetResultSize(2);
341                                 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
342                                 cdr.Result[1] = ERROR_SHELLOPEN;
343                                 cdr.Stat = DiskError;
344                                 setIrq(0x1006);
345                         }
346                         if (cdr.CmdInProgress) {
347                                 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
348                                 cdr.CmdInProgress = 0;
349                                 SetResultSize(2);
350                                 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
351                                 cdr.Result[1] = ERROR_NOTREADY;
352                                 cdr.Stat = DiskError;
353                                 setIrq(0x1007);
354                         }
355
356                         set_event(PSXINT_CDRLID, cdReadTime * 30);
357                         break;
358                 }
359                 else if (cdr.StatP & STATUS_ROTATING) {
360                         cdr.StatP &= ~STATUS_ROTATING;
361                 }
362                 else if (!(stat.Status & STATUS_SHELLOPEN)) {
363                         // closed now
364                         CheckCdrom();
365
366                         // cdr.StatP STATUS_SHELLOPEN is "sticky"
367                         // and is only cleared by CdlNop
368
369                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
370                         set_event(PSXINT_CDRLID, cdReadTime * 105);
371                         break;
372                 }
373
374                 // recheck for close
375                 set_event(PSXINT_CDRLID, cdReadTime * 3);
376                 break;
377
378         case DRIVESTATE_RESCAN_CD:
379                 cdr.StatP |= STATUS_ROTATING;
380                 cdr.DriveState = DRIVESTATE_PREPARE_CD;
381
382                 // this is very long on real hardware, over 6 seconds
383                 // make it a bit faster here...
384                 set_event(PSXINT_CDRLID, cdReadTime * 150);
385                 break;
386
387         case DRIVESTATE_PREPARE_CD:
388                 if (cdr.StatP & STATUS_SEEK) {
389                         SetPlaySeekRead(cdr.StatP, 0);
390                         cdr.DriveState = DRIVESTATE_STANDBY;
391                 }
392                 else {
393                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
394                         set_event(PSXINT_CDRLID, cdReadTime * 26);
395                 }
396                 break;
397         }
398 }
399
400 static void Find_CurTrack(const u8 *time)
401 {
402         int current, sect;
403
404         current = msf2sec(time);
405
406         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
407                 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
408                 sect = fsm2sec(cdr.ResultTD);
409                 if (sect - current >= 150)
410                         break;
411         }
412 }
413
414 static void generate_subq(const u8 *time)
415 {
416         unsigned char start[3], next[3];
417         unsigned int this_s, start_s, next_s, pregap;
418         int relative_s;
419
420         CDR_getTD(cdr.CurTrack, start);
421         if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
422                 pregap = 150;
423                 CDR_getTD(cdr.CurTrack + 1, next);
424         }
425         else {
426                 // last track - cd size
427                 pregap = 0;
428                 next[0] = cdr.SetSectorEnd[2];
429                 next[1] = cdr.SetSectorEnd[1];
430                 next[2] = cdr.SetSectorEnd[0];
431         }
432
433         this_s = msf2sec(time);
434         start_s = fsm2sec(start);
435         next_s = fsm2sec(next);
436
437         cdr.TrackChanged = FALSE;
438
439         if (next_s - this_s < pregap) {
440                 cdr.TrackChanged = TRUE;
441                 cdr.CurTrack++;
442                 start_s = next_s;
443         }
444
445         cdr.subq.Index = 1;
446
447         relative_s = this_s - start_s;
448         if (relative_s < 0) {
449                 cdr.subq.Index = 0;
450                 relative_s = -relative_s;
451         }
452         sec2msf(relative_s, cdr.subq.Relative);
453
454         cdr.subq.Track = itob(cdr.CurTrack);
455         cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
456         cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
457         cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
458         cdr.subq.Absolute[0] = itob(time[0]);
459         cdr.subq.Absolute[1] = itob(time[1]);
460         cdr.subq.Absolute[2] = itob(time[2]);
461 }
462
463 static int ReadTrack(const u8 *time)
464 {
465         unsigned char tmp[3];
466         int read_ok;
467
468         tmp[0] = itob(time[0]);
469         tmp[1] = itob(time[1]);
470         tmp[2] = itob(time[2]);
471
472         CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
473
474         if (memcmp(cdr.Prev, tmp, 3) == 0)
475                 return 1;
476
477         read_ok = CDR_readTrack(tmp);
478         if (read_ok)
479                 memcpy(cdr.Prev, tmp, 3);
480         return read_ok;
481 }
482
483 static void UpdateSubq(const u8 *time)
484 {
485         const struct SubQ *subq;
486         int s = MSF2SECT(time[0], time[1], time[2]);
487         u16 crc;
488
489         if (CheckSBI(s))
490                 return;
491
492         subq = (struct SubQ *)CDR_getBufferSub(s);
493         if (subq != NULL && cdr.CurTrack == 1) {
494                 crc = calcCrc((u8 *)subq + 12, 10);
495                 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
496                         cdr.subq.Track = subq->TrackNumber;
497                         cdr.subq.Index = subq->IndexNumber;
498                         memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
499                         memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
500                 }
501                 else {
502                         CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
503                                 time[0], time[1], time[2]);
504                 }
505         }
506         else {
507                 generate_subq(time);
508         }
509
510         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
511                 cdr.subq.Track, cdr.subq.Index,
512                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
513                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
514 }
515
516 static void cdrPlayInterrupt_Autopause()
517 {
518         u32 abs_lev_max = 0;
519         boolean abs_lev_chselect;
520         u32 i;
521
522         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
523                 CDR_LOG_I("autopause\n");
524
525                 SetResultSize(1);
526                 cdr.Result[0] = cdr.StatP;
527                 cdr.Stat = DataEnd;
528                 setIrq(0x1000); // 0x1000 just for logging purposes
529
530                 StopCdda();
531                 SetPlaySeekRead(cdr.StatP, 0);
532         }
533         else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
534                  ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
535         {
536                 SetResultSize(8);
537                 cdr.Result[0] = cdr.StatP;
538                 cdr.Result[1] = cdr.subq.Track;
539                 cdr.Result[2] = cdr.subq.Index;
540                 
541                 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
542                 
543                 /* 8 is a hack. For accuracy, it should be 588. */
544                 for (i = 0; i < 8; i++)
545                 {
546                         abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
547                 }
548                 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
549                 abs_lev_max |= abs_lev_chselect << 15;
550
551                 if (cdr.subq.Absolute[2] & 0x10) {
552                         cdr.Result[3] = cdr.subq.Relative[0];
553                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
554                         cdr.Result[5] = cdr.subq.Relative[2];
555                 }
556                 else {
557                         cdr.Result[3] = cdr.subq.Absolute[0];
558                         cdr.Result[4] = cdr.subq.Absolute[1];
559                         cdr.Result[5] = cdr.subq.Absolute[2];
560                 }
561                 cdr.Result[6] = abs_lev_max >> 0;
562                 cdr.Result[7] = abs_lev_max >> 8;
563
564                 cdr.Stat = DataReady;
565                 setIrq(0x1001);
566         }
567
568         if (cdr.ReportDelay)
569                 cdr.ReportDelay--;
570 }
571
572 static int cdrSeekTime(unsigned char *target)
573 {
574         int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
575         int seekTime = abs(diff) * (cdReadTime / 2000);
576         int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
577         seekTime = MAX_VALUE(seekTime, 20000);
578
579         // need this stupidly long penalty or else Spyro2 intro desyncs
580         // note: if misapplied this breaks MGS cutscenes among other things
581         if (cyclesSinceRS > cdReadTime * 50)
582                 seekTime += cdReadTime * 25;
583         // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
584         // and then wants some slack time
585         else if (cyclesSinceRS < cdReadTime *3/2)
586                 seekTime += cdReadTime;
587
588         seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
589         CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
590                 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
591         return seekTime;
592 }
593
594 static u32 cdrAlignTimingHack(u32 cycles)
595 {
596         /*
597          * timing hack for T'ai Fu - Wrath of the Tiger:
598          * The game has a bug where it issues some cdc commands from a low priority
599          * vint handler, however there is a higher priority default bios handler
600          * that acks the vint irq and returns, so game's handler is not reached
601          * (see bios irq handler chains at e004 and the game's irq handling func
602          * at 80036810). For the game to work, vint has to arrive after the bios
603          * vint handler rejects some other irq (of which only cd and rcnt2 are
604          * active), but before the game's handler loop reads I_STAT. The time
605          * window for this is quite small (~1k cycles of so). Apparently this
606          * somehow happens naturally on the real hardware.
607          *
608          * Note: always enforcing this breaks other games like Crash PAL version
609          * (inputs get dropped because bios handler doesn't see interrupts).
610          */
611         u32 vint_rel;
612         if (psxRegs.cycle - rcnts[3].cycleStart > 250000)
613                 return cycles;
614         vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
615         vint_rel += PSXCLK / 60;
616         while ((s32)(vint_rel - cycles) < 0)
617                 vint_rel += PSXCLK / 60;
618         return vint_rel;
619 }
620
621 static void cdrUpdateTransferBuf(const u8 *buf);
622 static void cdrReadInterrupt(void);
623 static void cdrPrepCdda(s16 *buf, int samples);
624 static void cdrAttenuate(s16 *buf, int samples, int stereo);
625
626 static void msfiAdd(u8 *msfi, u32 count)
627 {
628         assert(count < 75);
629         msfi[2] += count;
630         if (msfi[2] >= 75) {
631                 msfi[2] -= 75;
632                 msfi[1]++;
633                 if (msfi[1] == 60) {
634                         msfi[1] = 0;
635                         msfi[0]++;
636                 }
637         }
638 }
639
640 static void msfiSub(u8 *msfi, u32 count)
641 {
642         assert(count < 75);
643         msfi[2] -= count;
644         if ((s8)msfi[2] < 0) {
645                 msfi[2] += 75;
646                 msfi[1]--;
647                 if ((s8)msfi[1] < 0) {
648                         msfi[1] = 60;
649                         msfi[0]--;
650                 }
651         }
652 }
653
654 void cdrPlayReadInterrupt(void)
655 {
656         cdr.LastReadSeekCycles = psxRegs.cycle;
657
658         if (cdr.Reading) {
659                 cdrReadInterrupt();
660                 return;
661         }
662
663         if (!cdr.Play) return;
664
665         CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
666                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
667
668         SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
669         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
670                 CDR_LOG_I("end stop\n");
671                 StopCdda();
672                 SetPlaySeekRead(cdr.StatP, 0);
673                 cdr.TrackChanged = TRUE;
674         }
675         else {
676                 CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
677         }
678
679         if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
680                 cdrPlayInterrupt_Autopause();
681
682         if (!cdr.Muted && cdr.Play && !Config.Cdda) {
683                 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
684                 cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
685                 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
686         }
687
688         msfiAdd(cdr.SetSectorPlay, 1);
689
690         // update for CdlGetlocP/autopause
691         generate_subq(cdr.SetSectorPlay);
692
693         CDRPLAYREAD_INT(cdReadTime, 0);
694 }
695
696 #define CMD_PART2           0x100
697 #define CMD_WHILE_NOT_READY 0x200
698
699 void cdrInterrupt(void) {
700         int start_rotating = 0;
701         int error = 0;
702         u32 cycles, seekTime = 0;
703         u32 second_resp_time = 0;
704         const void *buf;
705         u8 ParamC;
706         u8 set_loc[3];
707         int read_ok;
708         u16 not_ready = 0;
709         u16 Cmd;
710         int i;
711
712         if (cdr.Stat) {
713                 CDR_LOG_I("cmd %02x with irqstat %x\n",
714                         cdr.CmdInProgress, cdr.Stat);
715                 return;
716         }
717         if (cdr.Irq1Pending) {
718                 // hand out the "newest" sector, according to nocash
719                 cdrUpdateTransferBuf(CDR_getBuffer());
720                 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
721                         cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
722                         cdr.CmdInProgress, cdr.Irq1Pending);
723                 SetResultSize(1);
724                 cdr.Result[0] = cdr.Irq1Pending;
725                 cdr.Stat = (cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady;
726                 cdr.Irq1Pending = 0;
727                 setIrq(0x1003);
728                 return;
729         }
730
731         // default response
732         SetResultSize(1);
733         cdr.Result[0] = cdr.StatP;
734         cdr.Stat = Acknowledge;
735
736         Cmd = cdr.CmdInProgress;
737         cdr.CmdInProgress = 0;
738         ParamC = cdr.ParamC;
739
740         if (Cmd < 0x100) {
741                 cdr.Ctrl &= ~0x80;
742                 cdr.ParamC = 0;
743                 cdr.Cmd = 0;
744         }
745
746         switch (cdr.DriveState) {
747         case DRIVESTATE_PREPARE_CD:
748                 if (Cmd > 2) {
749                         // Syphon filter 2 expects commands to work shortly after it sees
750                         // STATUS_ROTATING, so give up trying to emulate the startup seq
751                         cdr.DriveState = DRIVESTATE_STANDBY;
752                         cdr.StatP &= ~STATUS_SEEK;
753                         psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
754                         break;
755                 }
756                 // fallthrough
757         case DRIVESTATE_LID_OPEN:
758         case DRIVESTATE_RESCAN_CD:
759                 // no disk or busy with the initial scan, allowed cmds are limited
760                 not_ready = CMD_WHILE_NOT_READY;
761                 break;
762         }
763
764         switch (Cmd | not_ready) {
765                 case CdlNop:
766                 case CdlNop + CMD_WHILE_NOT_READY:
767                         if (cdr.DriveState != DRIVESTATE_LID_OPEN)
768                                 cdr.StatP &= ~STATUS_SHELLOPEN;
769                         break;
770
771                 case CdlSetloc:
772                 // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
773                         CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
774
775                         // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
776                         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))
777                         {
778                                 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
779                                 if (++cdr.errorRetryhack > 100)
780                                         break;
781                                 error = ERROR_INVALIDARG;
782                                 goto set_error;
783                         }
784                         else
785                         {
786                                 for (i = 0; i < 3; i++)
787                                         set_loc[i] = btoi(cdr.Param[i]);
788                                 memcpy(cdr.SetSector, set_loc, 3);
789                                 cdr.SetSector[3] = 0;
790                                 cdr.SetlocPending = 1;
791                                 cdr.errorRetryhack = 0;
792                         }
793                         break;
794
795                 do_CdlPlay:
796                 case CdlPlay:
797                         StopCdda();
798                         StopReading();
799
800                         cdr.FastBackward = 0;
801                         cdr.FastForward = 0;
802
803                         // BIOS CD Player
804                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
805
806                         if (ParamC != 0 && cdr.Param[0] != 0) {
807                                 int track = btoi( cdr.Param[0] );
808
809                                 if (track <= cdr.ResultTN[1])
810                                         cdr.CurTrack = track;
811
812                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
813
814                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
815                                         for (i = 0; i < 3; i++)
816                                                 set_loc[i] = cdr.ResultTD[2 - i];
817                                         seekTime = cdrSeekTime(set_loc);
818                                         memcpy(cdr.SetSectorPlay, set_loc, 3);
819                                 }
820                         }
821                         else if (cdr.SetlocPending) {
822                                 seekTime = cdrSeekTime(cdr.SetSector);
823                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
824                         }
825                         else {
826                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
827                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
828                         }
829                         cdr.SetlocPending = 0;
830
831                         /*
832                         Rayman: detect track changes
833                         - fixes logo freeze
834
835                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
836                         - plays tracks without retry play
837
838                         Wild 9: skip PREGAP + starting accurate SubQ
839                         - plays tracks without retry play
840                         */
841                         Find_CurTrack(cdr.SetSectorPlay);
842                         generate_subq(cdr.SetSectorPlay);
843                         cdr.LocL[0] = LOCL_INVALID;
844                         cdr.SubqForwardSectors = 1;
845                         cdr.TrackChanged = FALSE;
846                         cdr.FileChannelSelected = 0;
847                         cdr.AdpcmActive = 0;
848                         cdr.ReportDelay = 60;
849                         cdr.sectorsRead = 0;
850
851                         if (!Config.Cdda)
852                                 CDR_play(cdr.SetSectorPlay);
853
854                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
855                         
856                         // BIOS player - set flag again
857                         cdr.Play = TRUE;
858
859                         CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
860                         start_rotating = 1;
861                         break;
862
863                 case CdlForward:
864                         // TODO: error 80 if stopped
865                         cdr.Stat = Complete;
866
867                         // GameShark CD Player: Calls 2x + Play 2x
868                         cdr.FastForward = 1;
869                         cdr.FastBackward = 0;
870                         break;
871
872                 case CdlBackward:
873                         cdr.Stat = Complete;
874
875                         // GameShark CD Player: Calls 2x + Play 2x
876                         cdr.FastBackward = 1;
877                         cdr.FastForward = 0;
878                         break;
879
880                 case CdlStandby:
881                         if (cdr.DriveState != DRIVESTATE_STOPPED) {
882                                 error = ERROR_INVALIDARG;
883                                 goto set_error;
884                         }
885                         second_resp_time = cdReadTime * 125 / 2;
886                         start_rotating = 1;
887                         break;
888
889                 case CdlStandby + CMD_PART2:
890                         cdr.Stat = Complete;
891                         break;
892
893                 case CdlStop:
894                         if (cdr.Play) {
895                                 // grab time for current track
896                                 CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
897
898                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
899                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
900                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
901                         }
902
903                         StopCdda();
904                         StopReading();
905                         SetPlaySeekRead(cdr.StatP, 0);
906                         cdr.StatP &= ~STATUS_ROTATING;
907                         cdr.LocL[0] = LOCL_INVALID;
908
909                         second_resp_time = 0x800;
910                         if (cdr.DriveState == DRIVESTATE_STANDBY)
911                                 second_resp_time = cdReadTime * 30 / 2;
912
913                         cdr.DriveState = DRIVESTATE_STOPPED;
914                         break;
915
916                 case CdlStop + CMD_PART2:
917                         cdr.Stat = Complete;
918                         break;
919
920                 case CdlPause:
921                         StopCdda();
922                         StopReading();
923
924                         // how the drive maintains the position while paused is quite
925                         // complicated, this is the minimum to make "Bedlam" happy
926                         msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
927                         cdr.sectorsRead = 0;
928
929                         /*
930                         Gundam Battle Assault 2: much slower (*)
931                         - Fixes boot, gameplay
932
933                         Hokuto no Ken 2: slower
934                         - Fixes intro + subtitles
935
936                         InuYasha - Feudal Fairy Tale: slower
937                         - Fixes battles
938                         */
939                         /* Gameblabla - Tightening the timings (as taken from Duckstation). 
940                          * The timings from Duckstation are based upon hardware tests.
941                          * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
942                          * seems to be timing sensitive as it can depend on the CPU's clock speed.
943                          * */
944                         if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
945                         {
946                                 second_resp_time = 7000;
947                         }
948                         else
949                         {
950                                 second_resp_time = (((cdr.Mode & MODE_SPEED) ? 1 : 2) * 1097107);
951                         }
952                         SetPlaySeekRead(cdr.StatP, 0);
953                         break;
954
955                 case CdlPause + CMD_PART2:
956                         cdr.Stat = Complete;
957                         break;
958
959                 case CdlReset:
960                 case CdlReset + CMD_WHILE_NOT_READY:
961                         StopCdda();
962                         StopReading();
963                         SetPlaySeekRead(cdr.StatP, 0);
964                         cdr.LocL[0] = LOCL_INVALID;
965                         cdr.Muted = FALSE;
966                         cdr.Mode = MODE_SIZE_2340; /* This fixes This is Football 2, Pooh's Party lockups */
967                         second_resp_time = not_ready ? 70000 : 4100000;
968                         start_rotating = 1;
969                         break;
970
971                 case CdlReset + CMD_PART2:
972                 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
973                         cdr.Stat = Complete;
974                         break;
975
976                 case CdlMute:
977                         cdr.Muted = TRUE;
978                         break;
979
980                 case CdlDemute:
981                         cdr.Muted = FALSE;
982                         break;
983
984                 case CdlSetfilter:
985                         cdr.FilterFile = cdr.Param[0];
986                         cdr.FilterChannel = cdr.Param[1];
987                         break;
988
989                 case CdlSetmode:
990                 case CdlSetmode + CMD_WHILE_NOT_READY:
991                         CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
992                         cdr.Mode = cdr.Param[0];
993                         break;
994
995                 case CdlGetparam:
996                 case CdlGetparam + CMD_WHILE_NOT_READY:
997                         /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
998                         SetResultSize(5);
999                         cdr.Result[1] = cdr.Mode;
1000                         cdr.Result[2] = 0;
1001                         cdr.Result[3] = cdr.FilterFile;
1002                         cdr.Result[4] = cdr.FilterChannel;
1003                         break;
1004
1005                 case CdlGetlocL:
1006                         if (cdr.LocL[0] == LOCL_INVALID) {
1007                                 error = 0x80;
1008                                 goto set_error;
1009                         }
1010                         SetResultSize(8);
1011                         memcpy(cdr.Result, cdr.LocL, 8);
1012                         break;
1013
1014                 case CdlGetlocP:
1015                         SetResultSize(8);
1016                         memcpy(&cdr.Result, &cdr.subq, 8);
1017                         break;
1018
1019                 case CdlReadT: // SetSession?
1020                         // really long
1021                         second_resp_time = cdReadTime * 290 / 4;
1022                         start_rotating = 1;
1023                         break;
1024
1025                 case CdlReadT + CMD_PART2:
1026                         cdr.Stat = Complete;
1027                         break;
1028
1029                 case CdlGetTN:
1030                         SetResultSize(3);
1031                         if (CDR_getTN(cdr.ResultTN) == -1) {
1032                                 cdr.Stat = DiskError;
1033                                 cdr.Result[0] |= STATUS_ERROR;
1034                         } else {
1035                                 cdr.Stat = Acknowledge;
1036                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
1037                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
1038                         }
1039                         break;
1040
1041                 case CdlGetTD:
1042                         cdr.Track = btoi(cdr.Param[0]);
1043                         SetResultSize(4);
1044                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1045                                 cdr.Stat = DiskError;
1046                                 cdr.Result[0] |= STATUS_ERROR;
1047                         } else {
1048                                 cdr.Stat = Acknowledge;
1049                                 cdr.Result[0] = cdr.StatP;
1050                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
1051                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
1052                                 /* According to Nocash's documentation, the function doesn't care about ff.
1053                                  * This can be seen also in Mednafen's implementation. */
1054                                 //cdr.Result[3] = itob(cdr.ResultTD[0]);
1055                         }
1056                         break;
1057
1058                 case CdlSeekL:
1059                 case CdlSeekP:
1060                         StopCdda();
1061                         StopReading();
1062                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1063
1064                         seekTime = cdrSeekTime(cdr.SetSector);
1065                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1066                         /*
1067                         Crusaders of Might and Magic = 0.5x-4x
1068                         - fix cutscene speech start
1069
1070                         Eggs of Steel = 2x-?
1071                         - fix new game
1072
1073                         Medievil = ?-4x
1074                         - fix cutscene speech
1075
1076                         Rockman X5 = 0.5-4x
1077                         - fix capcom logo
1078                         */
1079                         second_resp_time = cdReadTime + seekTime;
1080                         start_rotating = 1;
1081                         break;
1082
1083                 case CdlSeekL + CMD_PART2:
1084                 case CdlSeekP + CMD_PART2:
1085                         SetPlaySeekRead(cdr.StatP, 0);
1086                         cdr.Result[0] = cdr.StatP;
1087                         cdr.Stat = Complete;
1088
1089                         Find_CurTrack(cdr.SetSectorPlay);
1090                         read_ok = ReadTrack(cdr.SetSectorPlay);
1091                         if (read_ok && (buf = CDR_getBuffer()))
1092                                 memcpy(cdr.LocL, buf, 8);
1093                         UpdateSubq(cdr.SetSectorPlay);
1094                         cdr.TrackChanged = FALSE;
1095                         cdr.LastReadSeekCycles = psxRegs.cycle;
1096                         break;
1097
1098                 case CdlTest:
1099                 case CdlTest + CMD_WHILE_NOT_READY:
1100                         switch (cdr.Param[0]) {
1101                                 case 0x20: // System Controller ROM Version
1102                                         SetResultSize(4);
1103                                         memcpy(cdr.Result, Test20, 4);
1104                                         break;
1105                                 case 0x22:
1106                                         SetResultSize(8);
1107                                         memcpy(cdr.Result, Test22, 4);
1108                                         break;
1109                                 case 0x23: case 0x24:
1110                                         SetResultSize(8);
1111                                         memcpy(cdr.Result, Test23, 4);
1112                                         break;
1113                         }
1114                         break;
1115
1116                 case CdlID:
1117                         second_resp_time = 20480;
1118                         break;
1119
1120                 case CdlID + CMD_PART2:
1121                         SetResultSize(8);
1122                         cdr.Result[0] = cdr.StatP;
1123                         cdr.Result[1] = 0;
1124                         cdr.Result[2] = 0;
1125                         cdr.Result[3] = 0;
1126
1127                         // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1128                         if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
1129                                 cdr.Result[1] = 0xc0;
1130                         }
1131                         else {
1132                                 if (stat.Type == 2)
1133                                         cdr.Result[1] |= 0x10;
1134                                 if (CdromId[0] == '\0')
1135                                         cdr.Result[1] |= 0x80;
1136                         }
1137                         cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1138                         CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1139                                 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1140
1141                         /* This adds the string "PCSX" in Playstation bios boot screen */
1142                         memcpy((char *)&cdr.Result[4], "PCSX", 4);
1143                         cdr.Stat = Complete;
1144                         break;
1145
1146                 case CdlInit:
1147                 case CdlInit + CMD_WHILE_NOT_READY:
1148                         StopCdda();
1149                         StopReading();
1150                         SetPlaySeekRead(cdr.StatP, 0);
1151                         // yes, it really sets STATUS_SHELLOPEN
1152                         cdr.StatP |= STATUS_SHELLOPEN;
1153                         cdr.DriveState = DRIVESTATE_RESCAN_CD;
1154                         set_event(PSXINT_CDRLID, 20480);
1155                         start_rotating = 1;
1156                         break;
1157
1158                 case CdlGetQ:
1159                 case CdlGetQ + CMD_WHILE_NOT_READY:
1160                         break;
1161
1162                 case CdlReadToc:
1163                 case CdlReadToc + CMD_WHILE_NOT_READY:
1164                         cdr.LocL[0] = LOCL_INVALID;
1165                         second_resp_time = cdReadTime * 180 / 4;
1166                         start_rotating = 1;
1167                         break;
1168
1169                 case CdlReadToc + CMD_PART2:
1170                 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1171                         cdr.Stat = Complete;
1172                         break;
1173
1174                 case CdlReadN:
1175                 case CdlReadS:
1176                         if (cdr.Reading && !cdr.SetlocPending)
1177                                 break;
1178
1179                         Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1180
1181                         if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
1182                                 // Read* acts as play for cdda tracks in cdda mode
1183                                 goto do_CdlPlay;
1184
1185                         StopCdda();
1186                         if (cdr.SetlocPending) {
1187                                 seekTime = cdrSeekTime(cdr.SetSector);
1188                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1189                                 cdr.SetlocPending = 0;
1190                         }
1191                         cdr.Reading = 1;
1192                         cdr.FileChannelSelected = 0;
1193                         cdr.AdpcmActive = 0;
1194
1195                         // Fighting Force 2 - update subq time immediately
1196                         // - fixes new game
1197                         UpdateSubq(cdr.SetSectorPlay);
1198                         cdr.LocL[0] = LOCL_INVALID;
1199                         cdr.SubqForwardSectors = 1;
1200                         cdr.sectorsRead = 0;
1201
1202                         cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1203                         cycles += seekTime;
1204                         if (Config.hacks.cdr_read_timing)
1205                                 cycles = cdrAlignTimingHack(cycles);
1206                         CDRPLAYREAD_INT(cycles, 1);
1207
1208                         SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1209                         start_rotating = 1;
1210                         break;
1211
1212                 case CdlSync:
1213                 default:
1214                         error = ERROR_INVALIDCMD;
1215                         // FALLTHROUGH
1216
1217                 set_error:
1218                         SetResultSize(2);
1219                         cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1220                         cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1221                         cdr.Stat = DiskError;
1222                         CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1223                         break;
1224         }
1225
1226         if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1227                 cdr.DriveState = DRIVESTATE_STANDBY;
1228                 cdr.StatP |= STATUS_ROTATING;
1229         }
1230
1231         if (second_resp_time) {
1232                 cdr.CmdInProgress = Cmd | 0x100;
1233                 set_event(PSXINT_CDR, second_resp_time);
1234         }
1235         else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1236                 cdr.CmdInProgress = cdr.Cmd;
1237                 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1238         }
1239
1240         setIrq(Cmd);
1241 }
1242
1243 #ifdef HAVE_ARMV7
1244  #define ssat32_to_16(v) \
1245   asm("ssat %0,#16,%1" : "=r" (v) : "r" (v))
1246 #else
1247  #define ssat32_to_16(v) do { \
1248   if (v < -32768) v = -32768; \
1249   else if (v > 32767) v = 32767; \
1250  } while (0)
1251 #endif
1252
1253 static void cdrPrepCdda(s16 *buf, int samples)
1254 {
1255 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1256         int i;
1257         for (i = 0; i < samples; i++) {
1258                 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1259                 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1260         }
1261 #endif
1262 }
1263
1264 static void cdrAttenuate(s16 *buf, int samples, int stereo)
1265 {
1266         int i, l, r;
1267         int ll = cdr.AttenuatorLeftToLeft;
1268         int lr = cdr.AttenuatorLeftToRight;
1269         int rl = cdr.AttenuatorRightToLeft;
1270         int rr = cdr.AttenuatorRightToRight;
1271
1272         if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
1273                 return;
1274
1275         if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
1276                 return;
1277
1278         if (stereo) {
1279                 for (i = 0; i < samples; i++) {
1280                         l = buf[i * 2];
1281                         r = buf[i * 2 + 1];
1282                         l = (l * ll + r * rl) >> 7;
1283                         r = (r * rr + l * lr) >> 7;
1284                         ssat32_to_16(l);
1285                         ssat32_to_16(r);
1286                         buf[i * 2] = l;
1287                         buf[i * 2 + 1] = r;
1288                 }
1289         }
1290         else {
1291                 for (i = 0; i < samples; i++) {
1292                         l = buf[i];
1293                         l = l * (ll + rl) >> 7;
1294                         //r = r * (rr + lr) >> 7;
1295                         ssat32_to_16(l);
1296                         //ssat32_to_16(r);
1297                         buf[i] = l;
1298                 }
1299         }
1300 }
1301
1302 static void cdrReadInterruptSetResult(unsigned char result)
1303 {
1304         if (cdr.Stat) {
1305                 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1306                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1307                         cdr.CmdInProgress, cdr.Stat);
1308                 cdr.Irq1Pending = result;
1309                 return;
1310         }
1311         SetResultSize(1);
1312         cdr.Result[0] = result;
1313         cdr.Stat = (result & STATUS_ERROR) ? DiskError : DataReady;
1314         setIrq(0x1004);
1315 }
1316
1317 static void cdrUpdateTransferBuf(const u8 *buf)
1318 {
1319         if (!buf)
1320                 return;
1321         memcpy(cdr.Transfer, buf, DATA_SIZE);
1322         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1323         CDR_LOG("cdr.Transfer  %02x:%02x:%02x\n",
1324                 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1325         if (cdr.FifoOffset < 2048 + 12)
1326                 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1327 }
1328
1329 static void cdrReadInterrupt(void)
1330 {
1331         const struct { u8 file, chan, mode, coding; } *subhdr;
1332         const u8 *buf = NULL;
1333         int deliver_data = 1;
1334         u8 subqPos[3];
1335         int read_ok;
1336
1337         memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1338         msfiAdd(subqPos, cdr.SubqForwardSectors);
1339         UpdateSubq(subqPos);
1340         if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1341                 cdr.SubqForwardSectors++;
1342                 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1343                 return;
1344         }
1345
1346         // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1347         SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1348         cdr.sectorsRead++;
1349
1350         read_ok = ReadTrack(cdr.SetSectorPlay);
1351         if (read_ok)
1352                 buf = CDR_getBuffer();
1353         if (buf == NULL)
1354                 read_ok = 0;
1355
1356         if (!read_ok) {
1357                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1358                 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1359                 return;
1360         }
1361         memcpy(cdr.LocL, buf, 8);
1362
1363         if (!cdr.Stat && !cdr.Irq1Pending)
1364                 cdrUpdateTransferBuf(buf);
1365
1366         subhdr = (void *)(buf + 4);
1367         do {
1368                 // try to process as adpcm
1369                 if (!(cdr.Mode & MODE_STRSND))
1370                         break;
1371                 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1372                         break;
1373                 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1374                         subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1375                 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1376                         break;
1377                 if (subhdr->chan & 0xe0) { // ?
1378                         if (subhdr->chan != 0xff)
1379                                 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1380                         break;
1381                 }
1382                 if (!cdr.FileChannelSelected) {
1383                         cdr.CurFile = subhdr->file;
1384                         cdr.CurChannel = subhdr->chan;
1385                         cdr.FileChannelSelected = 1;
1386                 }
1387                 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1388                         break;
1389
1390                 // accepted as adpcm
1391                 deliver_data = 0;
1392
1393                 if (Config.Xa)
1394                         break;
1395                 if (!cdr.Muted && cdr.AdpcmActive) {
1396                         cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
1397                         SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 0);
1398                 }
1399                 // decode next
1400                 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, !cdr.AdpcmActive);
1401         } while (0);
1402
1403         if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1404                 deliver_data = 0;
1405
1406         /*
1407         Croc 2: $40 - only FORM1 (*)
1408         Judge Dredd: $C8 - only FORM1 (*)
1409         Sim Theme Park - no adpcm at all (zero)
1410         */
1411
1412         if (deliver_data)
1413                 cdrReadInterruptSetResult(cdr.StatP);
1414
1415         msfiAdd(cdr.SetSectorPlay, 1);
1416
1417         CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1418 }
1419
1420 /*
1421 cdrRead0:
1422         bit 0,1 - reg index
1423         bit 2 - adpcm active
1424         bit 5 - 1 result ready
1425         bit 6 - 1 dma ready
1426         bit 7 - 1 command being processed
1427 */
1428
1429 unsigned char cdrRead0(void) {
1430         cdr.Ctrl &= ~0x24;
1431         cdr.Ctrl |= cdr.AdpcmActive << 2;
1432         cdr.Ctrl |= cdr.ResultReady << 5;
1433
1434         cdr.Ctrl |= 0x40; // data fifo not empty
1435
1436         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1437         cdr.Ctrl |= 0x18;
1438
1439         CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1440
1441         return psxHu8(0x1800) = cdr.Ctrl;
1442 }
1443
1444 void cdrWrite0(unsigned char rt) {
1445         CDR_LOG_IO("cdr w0.idx: %02x\n", rt);
1446
1447         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1448 }
1449
1450 unsigned char cdrRead1(void) {
1451         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1452                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1453         else
1454                 psxHu8(0x1801) = 0;
1455         cdr.ResultP++;
1456         if (cdr.ResultP == cdr.ResultC)
1457                 cdr.ResultReady = 0;
1458
1459         CDR_LOG_IO("cdr r1.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1460
1461         return psxHu8(0x1801);
1462 }
1463
1464 void cdrWrite1(unsigned char rt) {
1465         const char *rnames[] = { "cmd", "smd", "smc", "arr" }; (void)rnames;
1466         CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1467
1468         switch (cdr.Ctrl & 3) {
1469         case 0:
1470                 break;
1471         case 3:
1472                 cdr.AttenuatorRightToRightT = rt;
1473                 return;
1474         default:
1475                 return;
1476         }
1477
1478 #ifdef CDR_LOG_CMD_IRQ
1479         CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1480         if (cdr.ParamC) {
1481                 int i;
1482                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1483                 for (i = 0; i < cdr.ParamC; i++)
1484                         SysPrintf(" %x,", cdr.Param[i]);
1485                 SysPrintf("}\n");
1486         } else {
1487                 SysPrintf("\n");
1488         }
1489 #endif
1490
1491         cdr.ResultReady = 0;
1492         cdr.Ctrl |= 0x80;
1493
1494         if (!cdr.CmdInProgress) {
1495                 cdr.CmdInProgress = rt;
1496                 // should be something like 12k + controller delays
1497                 set_event(PSXINT_CDR, 5000);
1498         }
1499         else {
1500                 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1501                         rt, cdr.Cmd, cdr.CmdInProgress);
1502                 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1503                         cdr.CmdInProgress = rt;
1504         }
1505
1506         cdr.Cmd = rt;
1507 }
1508
1509 unsigned char cdrRead2(void) {
1510         unsigned char ret = cdr.Transfer[0x920];
1511
1512         if (cdr.FifoOffset < cdr.FifoSize)
1513                 ret = cdr.Transfer[cdr.FifoOffset++];
1514         else
1515                 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1516
1517         CDR_LOG_IO("cdr r2.dat: %02x\n", ret);
1518         return ret;
1519 }
1520
1521 void cdrWrite2(unsigned char rt) {
1522         const char *rnames[] = { "prm", "ien", "all", "arl" }; (void)rnames;
1523         CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1524
1525         switch (cdr.Ctrl & 3) {
1526         case 0:
1527                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1528                         cdr.Param[cdr.ParamC++] = rt;
1529                 return;
1530         case 1:
1531                 cdr.Reg2 = rt;
1532                 setIrq(0x1005);
1533                 return;
1534         case 2:
1535                 cdr.AttenuatorLeftToLeftT = rt;
1536                 return;
1537         case 3:
1538                 cdr.AttenuatorRightToLeftT = rt;
1539                 return;
1540         }
1541 }
1542
1543 unsigned char cdrRead3(void) {
1544         if (cdr.Ctrl & 0x1)
1545                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1546         else
1547                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1548
1549         CDR_LOG_IO("cdr r3.%s: %02x\n", (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1550         return psxHu8(0x1803);
1551 }
1552
1553 void cdrWrite3(unsigned char rt) {
1554         const char *rnames[] = { "req", "ifl", "alr", "ava" }; (void)rnames;
1555         CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1556
1557         switch (cdr.Ctrl & 3) {
1558         case 0:
1559                 break; // transfer
1560         case 1:
1561                 if (cdr.Stat & rt) {
1562                         u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1563                                 + psxRegs.intCycle[PSXINT_CDR].cycle;
1564                         int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1565 #ifdef CDR_LOG_CMD_IRQ
1566                         CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n", cdr.Stat & rt, rt,
1567                                 !!pending, cdr.CmdInProgress,
1568                                 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1569 #endif
1570                         // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1571                         if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1572                         {
1573                                 s32 c = 2048;
1574                                 if (cdr.CmdInProgress) {
1575                                         c = 2048 - (psxRegs.cycle - nextCycle);
1576                                         c = MAX_VALUE(c, 512);
1577                                 }
1578                                 set_event(PSXINT_CDR, c);
1579                         }
1580                 }
1581                 cdr.Stat &= ~rt;
1582
1583                 if (rt & 0x40)
1584                         cdr.ParamC = 0;
1585                 return;
1586         case 2:
1587                 cdr.AttenuatorLeftToRightT = rt;
1588                 return;
1589         case 3:
1590                 if (rt & 0x20) {
1591                         memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
1592                         CDR_LOG("CD-XA Volume: %02x %02x | %02x %02x\n",
1593                                 cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1594                                 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
1595                 }
1596                 return;
1597         }
1598
1599         // test: Viewpoint
1600         if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1601                 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1602         }
1603         else if (rt & 0x80) {
1604                 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1605                         case MODE_SIZE_2328:
1606                         case 0x00:
1607                                 cdr.FifoOffset = 12;
1608                                 cdr.FifoSize = 2048 + 12;
1609                                 break;
1610
1611                         case MODE_SIZE_2340:
1612                         default:
1613                                 cdr.FifoOffset = 0;
1614                                 cdr.FifoSize = 2340;
1615                                 break;
1616                 }
1617         }
1618         else if (!(rt & 0xc0))
1619                 cdr.FifoOffset = DATA_SIZE; // fifo empty
1620 }
1621
1622 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1623         u32 cdsize, max_words;
1624         int size;
1625         u8 *ptr;
1626
1627 #if 0
1628         CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1629         if (cdr.FifoOffset == 0) {
1630                 ptr = cdr.Transfer;
1631                 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1632         }
1633         SysPrintf("\n");
1634 #endif
1635
1636         switch (chcr & 0x71000000) {
1637                 case 0x11000000:
1638                         ptr = getDmaRam(madr, &max_words);
1639                         if (ptr == INVALID_PTR) {
1640                                 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1641                                 break;
1642                         }
1643
1644                         cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1645
1646                         /*
1647                         GS CDX: Enhancement CD crash
1648                         - Setloc 0:0:0
1649                         - CdlPlay
1650                         - Spams DMA3 and gets buffer overrun
1651                         */
1652                         size = DATA_SIZE - cdr.FifoOffset;
1653                         if (size > cdsize)
1654                                 size = cdsize;
1655                         if (size > max_words * 4)
1656                                 size = max_words * 4;
1657                         if (size > 0)
1658                         {
1659                                 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1660                                 cdr.FifoOffset += size;
1661                         }
1662                         if (size < cdsize) {
1663                                 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1664                                 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1665                         }
1666                         psxCpu->Clear(madr, cdsize / 4);
1667
1668                         set_event(PSXINT_CDRDMA, (cdsize / 4) * 24);
1669
1670                         HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1671                         if (chcr & 0x100) {
1672                                 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1673                                 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1674                         }
1675                         else {
1676                                 // halted
1677                                 psxRegs.cycle += (cdsize/4) * 24 - 20;
1678                         }
1679                         return;
1680
1681                 default:
1682                         CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1683                         break;
1684         }
1685
1686         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1687         DMA_INTERRUPT(3);
1688 }
1689
1690 void cdrDmaInterrupt(void)
1691 {
1692         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1693         {
1694                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1695                 DMA_INTERRUPT(3);
1696         }
1697 }
1698
1699 static void getCdInfo(void)
1700 {
1701         u8 tmp;
1702
1703         CDR_getTN(cdr.ResultTN);
1704         CDR_getTD(0, cdr.SetSectorEnd);
1705         tmp = cdr.SetSectorEnd[0];
1706         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1707         cdr.SetSectorEnd[2] = tmp;
1708 }
1709
1710 void cdrReset() {
1711         memset(&cdr, 0, sizeof(cdr));
1712         cdr.CurTrack = 1;
1713         cdr.FilterFile = 0;
1714         cdr.FilterChannel = 0;
1715         cdr.Reg2 = 0x1f;
1716         cdr.Stat = NoIntr;
1717         cdr.FifoOffset = DATA_SIZE; // fifo empty
1718
1719         CDR_getStatus(&stat);
1720         if (stat.Status & STATUS_SHELLOPEN) {
1721                 cdr.DriveState = DRIVESTATE_LID_OPEN;
1722                 cdr.StatP = STATUS_SHELLOPEN;
1723         }
1724         else if (CdromId[0] == '\0') {
1725                 cdr.DriveState = DRIVESTATE_STOPPED;
1726                 cdr.StatP = 0;
1727         }
1728         else {
1729                 cdr.DriveState = DRIVESTATE_STANDBY;
1730                 cdr.StatP = STATUS_ROTATING;
1731         }
1732         
1733         // BIOS player - default values
1734         cdr.AttenuatorLeftToLeft = 0x80;
1735         cdr.AttenuatorLeftToRight = 0x00;
1736         cdr.AttenuatorRightToLeft = 0x00;
1737         cdr.AttenuatorRightToRight = 0x80;
1738
1739         getCdInfo();
1740 }
1741
1742 int cdrFreeze(void *f, int Mode) {
1743         u32 tmp;
1744         u8 tmpp[3];
1745
1746         if (Mode == 0 && !Config.Cdda)
1747                 CDR_stop();
1748         
1749         cdr.freeze_ver = 0x63647202;
1750         gzfreeze(&cdr, sizeof(cdr));
1751         
1752         if (Mode == 1) {
1753                 cdr.ParamP = cdr.ParamC;
1754                 tmp = cdr.FifoOffset;
1755         }
1756
1757         gzfreeze(&tmp, sizeof(tmp));
1758
1759         if (Mode == 0) {
1760                 getCdInfo();
1761
1762                 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1763                 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1764                 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1765                         cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1766
1767                 // read right sub data
1768                 tmpp[0] = btoi(cdr.Prev[0]);
1769                 tmpp[1] = btoi(cdr.Prev[1]);
1770                 tmpp[2] = btoi(cdr.Prev[2]);
1771                 cdr.Prev[0]++;
1772                 ReadTrack(tmpp);
1773
1774                 if (cdr.Play) {
1775                         if (cdr.freeze_ver < 0x63647202)
1776                                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1777
1778                         Find_CurTrack(cdr.SetSectorPlay);
1779                         if (!Config.Cdda)
1780                                 CDR_play(cdr.SetSectorPlay);
1781                 }
1782
1783                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1784                         // old versions did not latch Reg2, have to fixup..
1785                         if (cdr.Reg2 == 0) {
1786                                 SysPrintf("cdrom: fixing up old savestate\n");
1787                                 cdr.Reg2 = 7;
1788                         }
1789                         // also did not save Attenuator..
1790                         if ((cdr.AttenuatorLeftToLeft | cdr.AttenuatorLeftToRight
1791                              | cdr.AttenuatorRightToLeft | cdr.AttenuatorRightToRight) == 0)
1792                         {
1793                                 cdr.AttenuatorLeftToLeft = cdr.AttenuatorRightToRight = 0x80;
1794                         }
1795                 }
1796         }
1797
1798         return 0;
1799 }
1800
1801 void LidInterrupt(void) {
1802         getCdInfo();
1803         cdrLidSeekInterrupt();
1804 }