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