frontend: update caanoo port
[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 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /* 
21 * Handles all CD-ROM registers and functions.
22 */
23
24 #include "cdrom.h"
25 #include "ppf.h"
26 #include "psxdma.h"
27
28 cdrStruct cdr;
29 static unsigned char *pTransfer;
30
31 /* CD-ROM magic numbers */
32 #define CdlSync        0
33 #define CdlNop         1
34 #define CdlSetloc      2
35 #define CdlPlay        3
36 #define CdlForward     4
37 #define CdlBackward    5
38 #define CdlReadN       6
39 #define CdlStandby     7
40 #define CdlStop        8
41 #define CdlPause       9
42 #define CdlInit        10
43 #define CdlMute        11
44 #define CdlDemute      12
45 #define CdlSetfilter   13
46 #define CdlSetmode     14
47 #define CdlGetmode     15
48 #define CdlGetlocL     16
49 #define CdlGetlocP     17
50 #define CdlReadT       18
51 #define CdlGetTN       19
52 #define CdlGetTD       20
53 #define CdlSeekL       21
54 #define CdlSeekP       22
55 #define CdlSetclock    23
56 #define CdlGetclock    24
57 #define CdlTest        25
58 #define CdlID          26
59 #define CdlReadS       27
60 #define CdlReset       28
61 #define CdlReadToc     30
62
63 #define AUTOPAUSE      249
64 #define READ_ACK       250
65 #define READ           251
66 #define REPPLAY_ACK    252
67 #define REPPLAY        253
68 #define ASYNC          254
69 /* don't set 255, it's reserved */
70
71 char *CmdName[0x100]= {
72     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
73     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
74     "CdlStop",     "CdlPause",     "CdlInit",    "CdlMute",
75     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetmode",
76     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
77     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
78     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
79     "CdlReset",    NULL,           "CDlReadToc", NULL
80 };
81
82 unsigned char Test04[] = { 0 };
83 unsigned char Test05[] = { 0 };
84 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
85 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
86 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
87
88 // cdr.Stat:
89 #define NoIntr          0
90 #define DataReady       1
91 #define Complete        2
92 #define Acknowledge     3
93 #define DataEnd         4
94 #define DiskError       5
95
96 /* Modes flags */
97 #define MODE_SPEED       (1<<7) // 0x80
98 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
99 #define MODE_SIZE_2340   (1<<5) // 0x20
100 #define MODE_SIZE_2328   (1<<4) // 0x10
101 #define MODE_SF          (1<<3) // 0x08 channel on/off
102 #define MODE_REPORT      (1<<2) // 0x04
103 #define MODE_AUTOPAUSE   (1<<1) // 0x02
104 #define MODE_CDDA        (1<<0) // 0x01
105
106 /* Status flags */
107 #define STATUS_PLAY      (1<<7) // 0x80
108 #define STATUS_SEEK      (1<<6) // 0x40
109 #define STATUS_READ      (1<<5) // 0x20
110 #define STATUS_SHELLOPEN (1<<4) // 0x10
111 #define STATUS_UNKNOWN3  (1<<3) // 0x08
112 #define STATUS_UNKNOWN2  (1<<2) // 0x04
113 #define STATUS_ROTATING  (1<<1) // 0x02
114 #define STATUS_ERROR     (1<<0) // 0x01
115
116
117
118 // 1x = 75 sectors per second
119 // PSXCLK = 1 sec in the ps
120 // so (PSXCLK / 75) = cdr read time (linuzappz)
121 #define cdReadTime (PSXCLK / 75)
122
123 // for cdr.Seeked
124 enum seeked_state {
125         SEEK_PENDING = 0,
126         SEEK_DONE = 1,
127         SEEK_DOING_CMD = 2,
128 };
129
130 static struct CdrStat stat;
131
132 static unsigned int msf2sec(u8 *msf) {
133         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
134 }
135
136 static void sec2msf(unsigned int s, u8 *msf) {
137         msf[0] = s / 75 / 60;
138         s = s - msf[0] * 75 * 60;
139         msf[1] = s / 75;
140         s = s - msf[1] * 75;
141         msf[2] = s;
142 }
143
144 // cdrInterrupt
145 #define CDR_INT(eCycle) { \
146         psxRegs.interrupt |= (1 << PSXINT_CDR); \
147         psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
148         psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
149         new_dyna_set_event(PSXINT_CDR, eCycle); \
150 }
151
152 // cdrReadInterrupt
153 #define CDREAD_INT(eCycle) { \
154         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
155         psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
156         psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
157         new_dyna_set_event(PSXINT_CDREAD, eCycle); \
158 }
159
160 // cdrLidSeekInterrupt
161 #define CDRLID_INT(eCycle) { \
162         psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
163         psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
164         psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
165         new_dyna_set_event(PSXINT_CDRLID, eCycle); \
166 }
167
168 // cdrPlayInterrupt
169 #define CDRMISC_INT(eCycle) { \
170         psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
171         psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
172         psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
173         new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \
174 }
175
176 #define StartReading(type, eCycle) { \
177         cdr.Reading = type; \
178         cdr.FirstSector = 1; \
179         cdr.Readed = 0xff; \
180         AddIrqQueue(READ_ACK, eCycle); \
181 }
182
183 #define StopReading() { \
184         if (cdr.Reading) { \
185                 cdr.Reading = 0; \
186                 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
187         } \
188         cdr.StatP &= ~STATUS_READ;\
189 }
190
191 #define StopCdda() { \
192         if (cdr.Play) { \
193                 if (!Config.Cdda) CDR_stop(); \
194                 cdr.StatP &= ~STATUS_PLAY; \
195                 cdr.Play = FALSE; \
196                 cdr.FastForward = 0; \
197                 cdr.FastBackward = 0; \
198                 /*SPU_registerCallback( SPUirq );*/ \
199         } \
200 }
201
202 #define SetResultSize(size) { \
203     cdr.ResultP = 0; \
204         cdr.ResultC = size; \
205         cdr.ResultReady = 1; \
206 }
207
208
209 void cdrLidSeekInterrupt()
210 {
211         // turn back on checking
212         if( cdr.LidCheck == 0x10 )
213         {
214                 cdr.LidCheck = 0;
215         }
216
217         // official lid close
218         else if( cdr.LidCheck == 0x30 )
219         {
220                 // GS CDX 3.3: $13
221                 cdr.StatP |= STATUS_ROTATING;
222
223
224                 // GS CDX 3.3 - ~50 getlocp tries
225                 CDRLID_INT( cdReadTime * 3 );
226                 cdr.LidCheck = 0x40;
227         }
228
229         // turn off ready
230         else if( cdr.LidCheck == 0x40 )
231         {
232                 // GS CDX 3.3: $01
233                 cdr.StatP &= ~STATUS_SHELLOPEN;
234                 cdr.StatP &= ~STATUS_ROTATING;
235
236
237                 // GS CDX 3.3 - ~50 getlocp tries
238                 CDRLID_INT( cdReadTime * 3 );
239                 cdr.LidCheck = 0x50;
240         }
241
242         // now seek
243         else if( cdr.LidCheck == 0x50 )
244         {
245                 // GameShark Lite: Start seeking ($42)
246                 cdr.StatP |= STATUS_SEEK;
247                 cdr.StatP |= STATUS_ROTATING;
248                 cdr.StatP &= ~STATUS_ERROR;
249
250
251                 CDRLID_INT( cdReadTime * 3 );
252                 cdr.LidCheck = 0x60;
253         }
254
255         // done = cd ready
256         else if( cdr.LidCheck == 0x60 )
257         {
258                 // GameShark Lite: Seek detection done ($02)
259                 cdr.StatP &= ~STATUS_SEEK;
260
261                 cdr.LidCheck = 0;
262         }
263 }
264
265 static void Check_Shell( int Irq )
266 {
267         // check case open/close
268         if (cdr.LidCheck > 0)
269         {
270 #ifdef CDR_LOG
271                 CDR_LOG( "LidCheck\n" );
272 #endif
273
274                 // $20 = check lid state
275                 if( cdr.LidCheck == 0x20 )
276                 {
277                         u32 i;
278
279                         i = stat.Status;
280                         if (CDR_getStatus(&stat) != -1)
281                         {
282                                 // BIOS hangs + BIOS error messages
283                                 //if (stat.Type == 0xff)
284                                         //cdr.Stat = DiskError;
285
286                                 // case now open
287                                 if (stat.Status & STATUS_SHELLOPEN)
288                                 {
289                                         // Vib Ribbon: pre-CD swap
290                                         StopCdda();
291
292
293                                         // GameShark Lite: Death if DiskError happens
294                                         //
295                                         // Vib Ribbon: Needs DiskError for CD swap
296
297                                         if (Irq != CdlNop)
298                                         {
299                                                 cdr.Stat = DiskError;
300
301                                                 cdr.StatP |= STATUS_ERROR;
302                                                 cdr.Result[0] |= STATUS_ERROR;
303                                         }
304
305                                         // GameShark Lite: Wants -exactly- $10
306                                         cdr.StatP |= STATUS_SHELLOPEN;
307                                         cdr.StatP &= ~STATUS_ROTATING;
308
309
310                                         CDRLID_INT( cdReadTime * 3 );
311                                         cdr.LidCheck = 0x10;
312
313
314                                         // GS CDX 3.3 = $11
315                                 }
316
317                                 // case just closed
318                                 else if ( i & STATUS_SHELLOPEN )
319                                 {
320                                         cdr.StatP |= STATUS_ROTATING;
321
322                                         CheckCdrom();
323
324
325                                         if( cdr.Stat == NoIntr )
326                                                 cdr.Stat = Acknowledge;
327
328                                         psxHu32ref(0x1070) |= SWAP32((u32)0x4);
329
330
331                                         // begin close-seek-ready cycle
332                                         CDRLID_INT( cdReadTime * 3 );
333                                         cdr.LidCheck = 0x30;
334
335
336                                         // GameShark Lite: Wants -exactly- $42, then $02
337                                         // GS CDX 3.3: Wants $11/$80, $13/$80, $01/$00
338                                 }
339
340                                 // case still closed - wait for recheck
341                                 else
342                                 {
343                                         CDRLID_INT( cdReadTime * 3 );
344                                         cdr.LidCheck = 0x10;
345                                 }
346                         }
347                 }
348
349
350                 // GS CDX: clear all values but #1,#2
351                 if( (cdr.LidCheck >= 0x30) || (cdr.StatP & STATUS_SHELLOPEN) )
352                 {
353                         SetResultSize(16);
354                         memset( cdr.Result, 0, 16 );
355
356                         cdr.Result[0] = cdr.StatP;
357
358
359                         // GS CDX: special return value
360                         if( cdr.StatP & STATUS_SHELLOPEN )
361                         {
362                                 cdr.Result[1] = 0x80;
363                         }
364
365
366                         if( cdr.Stat == NoIntr )
367                                 cdr.Stat = Acknowledge;
368
369                         psxHu32ref(0x1070) |= SWAP32((u32)0x4);
370                 }
371         }
372 }
373
374
375 void Find_CurTrack() {
376         cdr.CurTrack = 0;
377
378         if (CDR_getTN(cdr.ResultTN) != -1) {
379                 int lcv;
380
381                 for( lcv = 1; lcv <= cdr.ResultTN[1]; lcv++ ) {
382                         if (CDR_getTD((u8)(lcv), cdr.ResultTD) != -1) {
383                                 u32 sect1, sect2;
384
385 #ifdef CDR_LOG___0
386                                 CDR_LOG( "curtrack %d %d %d | %d %d %d | %d\n",
387                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
388                                         cdr.ResultTD[2], cdr.ResultTD[1], cdr.ResultTD[0],
389                                         cdr.CurTrack );
390 #endif
391
392                                 // find next track boundary - only need m:s accuracy
393                                 sect1 = cdr.SetSectorPlay[0] * 60 * 75 + cdr.SetSectorPlay[1] * 75;
394                                 sect2 = cdr.ResultTD[2] * 60 * 75 + cdr.ResultTD[1] * 75;
395
396                                 // Twisted Metal 4 - psx cdda pregap (2-sec)
397                                 // - fix in-game music
398                                 sect2 -= 75 * 2;
399
400                                 if( sect1 >= sect2 ) {
401                                         cdr.CurTrack++;
402                                         continue;
403                                 }
404                         }
405
406                         break;
407                 }
408         }
409 }
410
411 static void ReadTrack( u8 *time ) {
412         cdr.Prev[0] = itob( time[0] );
413         cdr.Prev[1] = itob( time[1] );
414         cdr.Prev[2] = itob( time[2] );
415
416 #ifdef CDR_LOG
417         CDR_LOG("ReadTrack() Log: KEY *** %x:%x:%x\n", cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
418 #endif
419         cdr.RErr = CDR_readTrack(cdr.Prev);
420 }
421
422
423 static void AddIrqQueue(unsigned char irq, unsigned long ecycle) {
424         //if (cdr.Irq != 0 && cdr.Irq != 0xff)
425         //      printf("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
426
427         cdr.Irq = irq;
428         cdr.eCycle = ecycle;
429
430         CDR_INT(ecycle);
431 }
432
433
434 void Set_Track()
435 {
436         if (CDR_getTN(cdr.ResultTN) != -1) {
437                 int lcv;
438
439                 for( lcv = 1; lcv < cdr.ResultTN[1]; lcv++ ) {
440                         if (CDR_getTD((u8)(lcv), cdr.ResultTD) != -1) {
441 #ifdef CDR_LOG___0
442                                 CDR_LOG( "settrack %d %d %d | %d %d %d | %d\n",
443                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
444                                         cdr.ResultTD[2], cdr.ResultTD[1], cdr.ResultTD[0],
445                                         cdr.CurTrack );
446 #endif
447
448                                 // check if time matches track start (only need min, sec accuracy)
449                                 // - m:s:f vs f:s:m
450                                 if( cdr.SetSectorPlay[0] == cdr.ResultTD[2] &&
451                                                 cdr.SetSectorPlay[1] == cdr.ResultTD[1] ) {
452                                         // skip pregap frames
453                                         if( cdr.SetSectorPlay[2] < cdr.ResultTD[0] )
454                                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
455
456                                         break;
457                                 }
458                                 else if( cdr.SetSectorPlay[0] < cdr.ResultTD[2] )
459                                         break;
460                         }
461                 }
462         }
463 }
464
465
466 static u8 fake_subq_local[3], fake_subq_real[3], fake_subq_index, fake_subq_change;
467 static void Create_Fake_Subq()
468 {
469         u8 temp_cur[3], temp_next[3], temp_start[3], pregap;
470         int diff;
471
472         if (CDR_getTN(cdr.ResultTN) == -1) return;
473         if( cdr.CurTrack+1 <= cdr.ResultTN[1] ) {
474                 pregap = 150;
475                 if( CDR_getTD(cdr.CurTrack+1, cdr.ResultTD) == -1 ) return;
476         } else {
477                 // last track - cd size
478                 pregap = 0;
479                 if( CDR_getTD(0, cdr.ResultTD) == -1 ) return;
480         }
481
482         if( cdr.Play == TRUE ) {
483                 temp_cur[0] = cdr.SetSectorPlay[0];
484                 temp_cur[1] = cdr.SetSectorPlay[1];
485                 temp_cur[2] = cdr.SetSectorPlay[2];
486         } else {
487                 temp_cur[0] = btoi( cdr.Prev[0] );
488                 temp_cur[1] = btoi( cdr.Prev[1] );
489                 temp_cur[2] = btoi( cdr.Prev[2] );
490         }
491
492         fake_subq_real[0] = temp_cur[0];
493         fake_subq_real[1] = temp_cur[1];
494         fake_subq_real[2] = temp_cur[2];
495
496         temp_next[0] = cdr.ResultTD[2];
497         temp_next[1] = cdr.ResultTD[1];
498         temp_next[2] = cdr.ResultTD[0];
499
500
501         // flag- next track
502         if( msf2sec(temp_cur) >= msf2sec( temp_next )-pregap ) {
503                 fake_subq_change = 1;
504
505                 cdr.CurTrack++;
506
507                 // end cd
508                 if( pregap == 0 ) StopCdda();
509         }
510
511         //////////////////////////////////////////////////
512         //////////////////////////////////////////////////
513
514         // repair
515         if( cdr.CurTrack <= cdr.ResultTN[1] ) {
516                 if( CDR_getTD(cdr.CurTrack, cdr.ResultTD) == -1 ) return;
517         } else {
518                 // last track - cd size
519                 if( CDR_getTD(0, cdr.ResultTD) == -1 ) return;
520         }
521         
522         temp_start[0] = cdr.ResultTD[2];
523         temp_start[1] = cdr.ResultTD[1];
524         temp_start[2] = cdr.ResultTD[0];
525
526
527 #ifdef CDR_LOG
528         CDR_LOG( "CDDA FAKE SUB - %d:%d:%d / %d:%d:%d / %d:%d:%d\n",
529                 temp_cur[0], temp_cur[1], temp_cur[2],
530                 temp_start[0], temp_start[1], temp_start[2],
531                 temp_next[0], temp_next[1], temp_next[2]);
532 #endif
533
534
535
536         // local time - pregap / real
537         diff = msf2sec(temp_cur) - msf2sec( temp_start );
538         if( diff < 0 ) {
539                 fake_subq_index = 0;
540
541                 sec2msf( -diff, fake_subq_local );
542         } else {
543                 fake_subq_index = 1;
544
545                 sec2msf( diff, fake_subq_local );
546         }
547 }
548
549
550 static void cdrPlayInterrupt_Autopause()
551 {
552         struct SubQ *subq = (struct SubQ *)CDR_getBufferSub();
553         int track_changed = 0;
554         if (subq != NULL ) {
555                 // update subq
556                 ReadTrack( cdr.SetSectorPlay );
557
558 #ifdef CDR_LOG
559                 CDR_LOG( "CDDA SUB - %X:%X:%X\n",
560                         subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
561 #endif
562
563                 /*
564                 CDDA Autopause
565
566                 Silhouette Mirage ($3)
567                 Tomb Raider 1 ($7)
568                 */
569
570                 // .. + 1 is probably wrong, but deals with corrupted subq + good checksum
571                 // (how does real thing handle those?)
572                 if( cdr.CurTrack + 1 == btoi( subq->TrackNumber ) )
573                         track_changed = 1;
574         } else {
575                 Create_Fake_Subq();
576 #ifdef CDR_LOG___0
577                 CDR_LOG( "CDDA FAKE SUB - %d:%d:%d\n",
578                         fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
579 #endif
580
581                 track_changed = fake_subq_change;
582                 fake_subq_change = 0;
583         }
584
585         if ((cdr.Mode & MODE_AUTOPAUSE) && track_changed) {
586 #ifdef CDR_LOG
587                 CDR_LOG( "CDDA STOP\n" );
588 #endif
589
590                 // Magic the Gathering
591                 // - looping territory cdda
592
593                 // ...?
594                 //cdr.ResultReady = 1;
595                 //cdr.Stat = DataReady;
596                 cdr.Stat = DataEnd;
597                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
598
599                 StopCdda();
600         }
601         else if (cdr.Mode & MODE_REPORT) {
602                 if (subq != NULL) {
603 #ifdef CDR_LOG
604                         CDR_LOG( "REPPLAY SUB - %X:%X:%X\n",
605                                 subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
606 #endif
607                         // breaks when .sub doesn't have index 0 for some reason (bad rip?)
608                         //cdr.CurTrack = btoi( subq->TrackNumber );
609
610                         if (subq->AbsoluteAddress[2] & 0xf)
611                                 return;
612
613                         cdr.Result[0] = cdr.StatP;
614                         // BIOS CD Player: data already BCD format
615                         cdr.Result[1] = subq->TrackNumber;
616                         cdr.Result[2] = subq->IndexNumber;
617
618                         cdr.Result[3] = subq->AbsoluteAddress[0];
619                         cdr.Result[4] = subq->AbsoluteAddress[1];
620                         cdr.Result[5] = subq->AbsoluteAddress[2];
621                 } else {
622 #ifdef CDR_LOG___0
623                         CDR_LOG( "REPPLAY FAKE - %d:%d:%d\n",
624                                 fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
625 #endif
626
627                         if (fake_subq_real[2] & 0xf)
628                                 return;
629
630                         cdr.Result[0] = cdr.StatP;
631                         // track # / index #
632                         cdr.Result[1] = itob(cdr.CurTrack);
633                         cdr.Result[2] = itob(fake_subq_index);
634                         // absolute
635                         cdr.Result[3] = itob( fake_subq_real[0] );
636                         cdr.Result[4] = itob( fake_subq_real[1] );
637                         cdr.Result[5] = itob( fake_subq_real[2] );
638                 }
639
640                 // Rayman: Logo freeze (resultready + dataready)
641                 cdr.ResultReady = 1;
642                 cdr.Stat = DataReady;
643
644                 SetResultSize(8);
645                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
646         }
647 }
648
649 // also handles seek
650 void cdrPlayInterrupt()
651 {
652         if (cdr.Seeked == SEEK_DOING_CMD) {
653                 SetResultSize(1);
654                 cdr.StatP |= STATUS_ROTATING;
655                 cdr.StatP &= ~STATUS_SEEK;
656                 cdr.Result[0] = cdr.StatP;
657                 if (cdr.Irq == 0 || cdr.Irq == 0xff) {
658                         cdr.Stat = Complete;
659                         if (cdr.Stat != NoIntr)
660                                 psxHu32ref(0x1070) |= SWAP32(0x4);
661                 }
662
663                 cdr.Seeked = SEEK_PENDING;
664                 CDRMISC_INT(cdReadTime * 20); // ???
665                 return;
666         }
667         else if (cdr.Seeked == SEEK_PENDING) {
668                 cdr.Seeked = SEEK_DONE;
669                 if (!cdr.Play && !cdr.Reading) {
670                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
671                         Find_CurTrack();
672                         ReadTrack(cdr.SetSector);
673                 }
674         }
675
676         if (!cdr.Play) return;
677
678 #ifdef CDR_LOG
679         CDR_LOG( "CDDA - %d:%d:%d\n",
680                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
681 #endif
682         CDRMISC_INT( cdReadTime );
683
684         if (!cdr.Irq && !cdr.Stat && (cdr.Mode & MODE_CDDA) && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
685                 cdrPlayInterrupt_Autopause();
686
687         cdr.SetSectorPlay[2]++;
688         if (cdr.SetSectorPlay[2] == 75) {
689                 cdr.SetSectorPlay[2] = 0;
690                 cdr.SetSectorPlay[1]++;
691                 if (cdr.SetSectorPlay[1] == 60) {
692                         cdr.SetSectorPlay[1] = 0;
693                         cdr.SetSectorPlay[0]++;
694                 }
695         }
696
697         //Check_Shell(0);
698 }
699
700 void cdrInterrupt() {
701         int i;
702         unsigned char Irq = cdr.Irq;
703         struct SubQ *subq;
704
705         // Reschedule IRQ
706         if (cdr.Stat) {
707                 CDR_INT( 0x100 );
708                 return;
709         }
710
711         cdr.Irq = 0xff;
712         cdr.Ctrl &= ~0x80;
713
714         switch (Irq) {
715                 case CdlSync:
716                         SetResultSize(1);
717                         cdr.StatP |= STATUS_ROTATING;
718                         cdr.Result[0] = cdr.StatP;
719                         cdr.Stat = Acknowledge; 
720                         break;
721
722                 case CdlNop:
723                         SetResultSize(1);
724                         cdr.Result[0] = cdr.StatP;
725                         cdr.Stat = Acknowledge;
726
727                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
728                         break;
729
730                 case CdlSetloc:
731                         cdr.CmdProcess = 0;
732                         SetResultSize(1);
733                         cdr.StatP |= STATUS_ROTATING;
734                         cdr.Result[0] = cdr.StatP;
735                         cdr.Stat = Acknowledge;
736                         break;
737
738                 case CdlPlay:
739                         fake_subq_change = 0;
740
741                         if (cdr.Seeked == SEEK_PENDING) {
742                                 // XXX: wrong, should seek instead..
743                                 memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
744                                 cdr.Seeked = SEEK_DONE;
745                         }
746
747                         /*
748                         Rayman: detect track changes
749                         - fixes logo freeze
750
751                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
752                         - plays tracks without retry play
753
754                         Wild 9: skip PREGAP + starting accurate SubQ
755                         - plays tracks without retry play
756                         */
757                         /* unneeded with correct cdriso?
758                         Set_Track();
759                         */
760                         Find_CurTrack();
761                         ReadTrack( cdr.SetSectorPlay );
762
763                         // GameShark CD Player: Calls 2x + Play 2x
764                         if( cdr.FastBackward || cdr.FastForward ) {
765                                 if( cdr.FastForward ) cdr.FastForward--;
766                                 if( cdr.FastBackward ) cdr.FastBackward--;
767
768                                 if( cdr.FastBackward == 0 && cdr.FastForward == 0 ) {
769                                         if( cdr.Play && CDR_getStatus(&stat) != -1 ) {
770                                                 cdr.SetSectorPlay[0] = stat.Time[0];
771                                                 cdr.SetSectorPlay[1] = stat.Time[1];
772                                                 cdr.SetSectorPlay[2] = stat.Time[2];
773                                         }
774                                 }
775                         }
776
777
778                         if (!Config.Cdda) {
779                                 // BIOS CD Player
780                                 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
781
782                                 // GameShark CD Player: Resume play
783                                 if( cdr.ParamC == 0 ) {
784 #ifdef CDR_LOG___0
785                                         CDR_LOG( "PLAY Resume @ %d:%d:%d\n",
786                                                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
787 #endif
788
789                                         //CDR_play( cdr.SetSectorPlay );
790                                 }
791                                 else
792                                 {
793                                         // BIOS CD Player: Resume play
794                                         if( cdr.Param[0] == 0 ) {
795 #ifdef CDR_LOG___0
796                                                 CDR_LOG( "PLAY Resume T0 @ %d:%d:%d\n",
797                                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
798 #endif
799
800                                                 //CDR_play( cdr.SetSectorPlay );
801                                         }
802                                         else {
803 #ifdef CDR_LOG___0
804                                                 CDR_LOG( "PLAY Resume Td @ %d:%d:%d\n",
805                                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
806 #endif
807
808                                                 // BIOS CD Player: Allow track replaying
809                                                 StopCdda();
810
811
812                                                 cdr.CurTrack = btoi( cdr.Param[0] );
813
814                                                 if (CDR_getTN(cdr.ResultTN) != -1) {
815                                                         // check last track
816                                                         if (cdr.CurTrack > cdr.ResultTN[1])
817                                                                 cdr.CurTrack = cdr.ResultTN[1];
818
819                                                         if (CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD) != -1) {
820                                                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
821                                                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
822                                                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
823
824                                                                 // reset data
825                                                                 //Set_Track();
826                                                                 Find_CurTrack();
827                                                                 ReadTrack( cdr.SetSectorPlay );
828
829                                                                 //CDR_play(cdr.SetSectorPlay);
830                                                         }
831                                                 }
832                                         }
833                                 }
834                         }
835
836
837                         // Vib Ribbon: gameplay checks flag
838                         cdr.StatP &= ~STATUS_SEEK;
839
840
841                         cdr.CmdProcess = 0;
842                         SetResultSize(1);
843                         cdr.StatP |= STATUS_ROTATING;
844                         cdr.Result[0] = cdr.StatP;
845                         cdr.Stat = Acknowledge;
846
847                         cdr.StatP |= STATUS_PLAY;
848
849                         
850                         // BIOS player - set flag again
851                         cdr.Play = TRUE;
852
853                         CDRMISC_INT( cdReadTime );
854                         break;
855
856                 case CdlForward:
857                         cdr.CmdProcess = 0;
858                         SetResultSize(1);
859                         cdr.StatP |= STATUS_ROTATING;
860                         cdr.Result[0] = cdr.StatP;
861                         cdr.Stat = Complete;
862
863
864                         // GameShark CD Player: Calls 2x + Play 2x
865                         if( cdr.FastForward == 0 ) cdr.FastForward = 2;
866                         else cdr.FastForward++;
867
868                         cdr.FastBackward = 0;
869                         break;
870
871                 case CdlBackward:
872                         cdr.CmdProcess = 0;
873                         SetResultSize(1);
874                         cdr.StatP |= STATUS_ROTATING;
875                         cdr.Result[0] = cdr.StatP;
876                         cdr.Stat = Complete;
877
878
879                         // GameShark CD Player: Calls 2x + Play 2x
880                         if( cdr.FastBackward == 0 ) cdr.FastBackward = 2;
881                         else cdr.FastBackward++;
882
883                         cdr.FastForward = 0;
884                         break;
885
886                 case CdlStandby:
887                         cdr.CmdProcess = 0;
888                         SetResultSize(1);
889                         cdr.StatP |= STATUS_ROTATING;
890                         cdr.Result[0] = cdr.StatP;
891                         cdr.Stat = Complete;
892                         break;
893
894                 case CdlStop:
895                         cdr.CmdProcess = 0;
896                         SetResultSize(1);
897                         cdr.StatP &= ~STATUS_ROTATING;
898                         cdr.Result[0] = cdr.StatP;
899                         cdr.Stat = Complete;
900 //                      cdr.Stat = Acknowledge;
901
902                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
903                         break;
904
905                 case CdlPause:
906                         SetResultSize(1);
907                         cdr.Result[0] = cdr.StatP;
908                         cdr.Stat = Acknowledge;
909
910                         /*
911                         Gundam Battle Assault 2: much slower (*)
912                         - Fixes boot, gameplay
913
914                         Hokuto no Ken 2: slower
915                         - Fixes intro + subtitles
916
917                         InuYasha - Feudal Fairy Tale: slower
918                         - Fixes battles
919                         */
920                         AddIrqQueue(CdlPause + 0x20, cdReadTime * 3);
921                         cdr.Ctrl |= 0x80;
922                         break;
923
924                 case CdlPause + 0x20:
925                         SetResultSize(1);
926                         cdr.StatP &= ~STATUS_READ;
927                         cdr.StatP |= STATUS_ROTATING;
928                         cdr.Result[0] = cdr.StatP;
929                         cdr.Stat = Complete;
930                         break;
931
932                 case CdlInit:
933                         SetResultSize(1);
934                         cdr.StatP = STATUS_ROTATING;
935                         cdr.Result[0] = cdr.StatP;
936                         cdr.Stat = Acknowledge;
937 //                      if (!cdr.Init) {
938                                 AddIrqQueue(CdlInit + 0x20, 0x800);
939 //                      }
940                 break;
941
942                 case CdlInit + 0x20:
943                         SetResultSize(1);
944                         cdr.Result[0] = cdr.StatP;
945                         cdr.Stat = Complete;
946                         cdr.Init = 1;
947                         break;
948
949                 case CdlMute:
950                         SetResultSize(1);
951                         cdr.StatP |= STATUS_ROTATING;
952                         cdr.Result[0] = cdr.StatP;
953                         cdr.Stat = Acknowledge;
954                         break;
955
956                 case CdlDemute:
957                         SetResultSize(1);
958                         cdr.StatP |= STATUS_ROTATING;
959                         cdr.Result[0] = cdr.StatP;
960                         cdr.Stat = Acknowledge;
961                         break;
962
963                 case CdlSetfilter:
964                         SetResultSize(1);
965                         cdr.StatP |= STATUS_ROTATING;
966                         cdr.Result[0] = cdr.StatP;
967                         cdr.Stat = Acknowledge; 
968                         break;
969
970                 case CdlSetmode:
971                         SetResultSize(1);
972                         cdr.StatP |= STATUS_ROTATING;
973                         cdr.Result[0] = cdr.StatP;
974                         cdr.Stat = Acknowledge;
975                         break;
976
977                 case CdlGetmode:
978                         SetResultSize(6);
979                         cdr.StatP |= STATUS_ROTATING;
980                         cdr.Result[0] = cdr.StatP;
981                         cdr.Result[1] = cdr.Mode;
982                         cdr.Result[2] = cdr.File;
983                         cdr.Result[3] = cdr.Channel;
984                         cdr.Result[4] = 0;
985                         cdr.Result[5] = 0;
986                         cdr.Stat = Acknowledge;
987                         break;
988
989                 case CdlGetlocL:
990                         SetResultSize(8);
991                         for (i = 0; i < 8; i++)
992                                 cdr.Result[i] = cdr.Transfer[i];
993                         cdr.Stat = Acknowledge;
994                         break;
995
996                 case CdlGetlocP:
997                         // GameShark CDX CD Player: uses 17 bytes output (wraps around)
998                         SetResultSize(17);
999                         memset( cdr.Result, 0, 16 );
1000
1001                         subq = (struct SubQ *)CDR_getBufferSub();
1002
1003                         if (subq != NULL) {
1004                                 if( cdr.Play && (cdr.Mode & MODE_CDDA) && !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
1005                                         // update subq
1006                                         ReadTrack( cdr.SetSectorPlay );
1007
1008                                 cdr.Result[0] = subq->TrackNumber;
1009                                 cdr.Result[1] = subq->IndexNumber;
1010                                 memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3);
1011                                 memcpy(cdr.Result + 5, subq->AbsoluteAddress, 3);
1012
1013
1014                                 // subQ integrity check - data only (skip audio)
1015                                 if( subq->TrackNumber == 1 && stat.Type == 0x01 ) {
1016                                 if (calcCrc((u8 *)subq + 12, 10) != (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
1017                                         memset(cdr.Result + 2, 0, 3 + 3); // CRC wrong, wipe out time data
1018                                 }
1019                                 }
1020                         } else {
1021                                 if( cdr.Play == FALSE || !(cdr.Mode & MODE_CDDA) || !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
1022                                         Create_Fake_Subq();
1023
1024
1025                                 // track # / index #
1026                                 cdr.Result[0] = itob(cdr.CurTrack);
1027                                 cdr.Result[1] = itob(fake_subq_index);
1028
1029                                 // local
1030                                 cdr.Result[2] = itob( fake_subq_local[0] );
1031                                 cdr.Result[3] = itob( fake_subq_local[1] );
1032                                 cdr.Result[4] = itob( fake_subq_local[2] );
1033
1034                                 // absolute
1035                                 cdr.Result[5] = itob( fake_subq_real[0] );
1036                                 cdr.Result[6] = itob( fake_subq_real[1] );
1037                                 cdr.Result[7] = itob( fake_subq_real[2] );
1038                         }
1039
1040                         // redump.org - wipe time
1041                         if( !cdr.Play && CheckSBI(cdr.Result+5) ) {
1042                                 memset( cdr.Result+2, 0, 6 );
1043                         }
1044
1045                         cdr.Stat = Acknowledge;
1046                         break;
1047
1048                 case CdlGetTN:
1049                         // 5-Star Racing: don't stop CDDA
1050                         //
1051                         // Vib Ribbon: CD swap
1052                         StopReading();
1053
1054                         cdr.CmdProcess = 0;
1055                         SetResultSize(3);
1056                         cdr.StatP |= STATUS_ROTATING;
1057                         cdr.Result[0] = cdr.StatP;
1058                         if (CDR_getTN(cdr.ResultTN) == -1) {
1059                                 cdr.Stat = DiskError;
1060                                 cdr.Result[0] |= STATUS_ERROR;
1061                         } else {
1062                                 cdr.Stat = Acknowledge;
1063                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
1064                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
1065                         }
1066                         break;
1067
1068                 case CdlGetTD:
1069                         cdr.CmdProcess = 0;
1070                         cdr.Track = btoi(cdr.Param[0]);
1071                         SetResultSize(4);
1072                         cdr.StatP |= STATUS_ROTATING;
1073                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1074                                 cdr.Stat = DiskError;
1075                                 cdr.Result[0] |= STATUS_ERROR;
1076                         } else {
1077                                 cdr.Stat = Acknowledge;
1078                                 cdr.Result[0] = cdr.StatP;
1079                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
1080                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
1081                                 cdr.Result[3] = itob(cdr.ResultTD[0]);
1082                         }
1083                         break;
1084
1085                 case CdlSeekL:
1086                 case CdlSeekP:
1087                         SetResultSize(1);
1088                         cdr.StatP |= STATUS_ROTATING;
1089                         cdr.Result[0] = cdr.StatP;
1090                         cdr.StatP |= STATUS_SEEK;
1091                         cdr.Stat = Acknowledge;
1092
1093                         /*
1094                         Crusaders of Might and Magic = 0.5x-4x
1095                         - fix cutscene speech start
1096
1097                         Eggs of Steel = 2x-?
1098                         - fix new game
1099
1100                         Medievil = ?-4x
1101                         - fix cutscene speech
1102
1103                         Rockman X5 = 0.5-4x
1104                         - fix capcom logo
1105                         */
1106                         CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
1107                         cdr.Seeked = SEEK_DOING_CMD;
1108                         break;
1109
1110                 case CdlTest:
1111                         cdr.Stat = Acknowledge;
1112                         switch (cdr.Param[0]) {
1113                                 case 0x20: // System Controller ROM Version
1114                                         SetResultSize(4);
1115                                         memcpy(cdr.Result, Test20, 4);
1116                                         break;
1117                                 case 0x22:
1118                                         SetResultSize(8);
1119                                         memcpy(cdr.Result, Test22, 4);
1120                                         break;
1121                                 case 0x23: case 0x24:
1122                                         SetResultSize(8);
1123                                         memcpy(cdr.Result, Test23, 4);
1124                                         break;
1125                         }
1126                         break;
1127
1128                 case CdlID:
1129                         SetResultSize(1);
1130                         cdr.StatP |= STATUS_ROTATING;
1131                         cdr.Result[0] = cdr.StatP;
1132                         cdr.Stat = Acknowledge;
1133                         AddIrqQueue(CdlID + 0x20, 0x800);
1134                         break;
1135
1136                 case CdlID + 0x20:
1137                         SetResultSize(8);
1138
1139                         if (CDR_getStatus(&stat) == -1) {
1140                                 cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player
1141                                 cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD
1142                         }
1143                         else {
1144                                 if (stat.Type == 2) {
1145                                         // Music CD
1146                                         cdr.Result[0] = 0x08;
1147                                         cdr.Result[1] = 0x10;
1148
1149                                         cdr.Result[1] |= 0x80;
1150                                 }
1151                                 else {
1152                                         // Data CD
1153                                         if (CdromId[0] == '\0') {
1154                                                 cdr.Result[0] = 0x00;
1155                                                 cdr.Result[1] = 0x80;
1156                                         }
1157                                         else {
1158                                                 cdr.Result[0] = 0x08;
1159                                                 cdr.Result[1] = 0x00;
1160                                         }
1161                                 }
1162                         }
1163
1164                         cdr.Result[2] = 0x00;
1165                         cdr.Result[3] = 0x00;
1166                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
1167                         cdr.Stat = Complete;
1168                         break;
1169
1170                 case CdlReset:
1171                         SetResultSize(1);
1172                         cdr.StatP = STATUS_ROTATING;
1173                         cdr.Result[0] = cdr.StatP;
1174                         cdr.Stat = Acknowledge;
1175                         break;
1176
1177                 case CdlReadT:
1178                         SetResultSize(1);
1179                         cdr.StatP |= STATUS_ROTATING;
1180                         cdr.Result[0] = cdr.StatP;
1181                         cdr.Stat = Acknowledge;
1182                         AddIrqQueue(CdlReadT + 0x20, 0x800);
1183                         break;
1184
1185                 case CdlReadT + 0x20:
1186                         SetResultSize(1);
1187                         cdr.StatP |= STATUS_ROTATING;
1188                         cdr.Result[0] = cdr.StatP;
1189                         cdr.Stat = Complete;
1190                         break;
1191
1192                 case CdlReadToc:
1193                         SetResultSize(1);
1194                         cdr.StatP |= STATUS_ROTATING;
1195                         cdr.Result[0] = cdr.StatP;
1196                         cdr.Stat = Acknowledge;
1197                         AddIrqQueue(CdlReadToc + 0x20, 0x800);
1198                         break;
1199
1200                 case CdlReadToc + 0x20:
1201                         SetResultSize(1);
1202                         cdr.StatP |= STATUS_ROTATING;
1203                         cdr.Result[0] = cdr.StatP;
1204                         cdr.Stat = Complete;
1205                         break;
1206
1207                 case AUTOPAUSE:
1208                         cdr.OCUP = 0;
1209 /*                      SetResultSize(1);
1210                         StopCdda();
1211                         StopReading();
1212                         cdr.OCUP = 0;
1213                         cdr.StatP&=~0x20;
1214                         cdr.StatP|= 0x2;
1215                         cdr.Result[0] = cdr.StatP;
1216                         cdr.Stat = DataEnd;
1217 */                      AddIrqQueue(CdlPause, 0x800);
1218                         break;
1219
1220                 case READ_ACK:
1221                         if (!cdr.Reading) return;
1222
1223
1224                         // Fighting Force 2 - update subq time immediately
1225                         // - fixes new game
1226                         ReadTrack( cdr.SetSector );
1227
1228
1229                         // Crusaders of Might and Magic - update getlocl now
1230                         // - fixes cutscene speech
1231                         {
1232                                 u8 *buf = CDR_getBuffer();
1233                                 if (buf != NULL)
1234                                         memcpy(cdr.Transfer, buf, 8);
1235                         }
1236
1237                         /*
1238                         Duke Nukem: Land of the Babes - seek then delay read for one frame
1239                         - fixes cutscenes
1240                         C-12 - Final Resistance - doesn't like seek
1241                         */
1242
1243                         if (cdr.Seeked != SEEK_DONE) {
1244                                 cdr.StatP |= STATUS_SEEK;
1245                                 cdr.StatP &= ~STATUS_READ;
1246
1247                                 // Crusaders of Might and Magic - use short time
1248                                 // - fix cutscene speech (startup)
1249
1250                                 // ??? - use more accurate seek time later
1251                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1252                         } else {
1253                                 cdr.StatP |= STATUS_READ;
1254                                 cdr.StatP &= ~STATUS_SEEK;
1255
1256                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1257                         }
1258
1259                         SetResultSize(1);
1260                         cdr.StatP |= STATUS_ROTATING;
1261                         cdr.Result[0] = cdr.StatP;
1262                         cdr.Stat = Acknowledge;
1263                         break;
1264
1265                 case 0xff:
1266                         return;
1267
1268                 default:
1269                         cdr.Stat = Complete;
1270                         break;
1271         }
1272
1273         Check_Shell( Irq );
1274
1275         cdr.ParamP = 0;
1276         cdr.ParamC = 0;
1277
1278         if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) {
1279                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1280         }
1281
1282 #ifdef CDR_LOG
1283         printf("cdrInterrupt() Log: CDR Interrupt IRQ %x: ", Irq);
1284         for (i = 0; i < cdr.ResultC; i++)
1285                 printf("%02x ", cdr.Result[i]);
1286         printf("\n");
1287 #endif
1288 }
1289
1290 void cdrReadInterrupt() {
1291         u8 *buf;
1292
1293         if (!cdr.Reading)
1294                 return;
1295
1296         if (cdr.Irq || cdr.Stat) {
1297                 CDREAD_INT(0x100);
1298                 return;
1299         }
1300
1301 #ifdef CDR_LOG
1302         CDR_LOG("cdrReadInterrupt() Log: KEY END");
1303 #endif
1304
1305         cdr.OCUP = 1;
1306         SetResultSize(1);
1307         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1308         cdr.StatP &= ~STATUS_SEEK;
1309         cdr.Result[0] = cdr.StatP;
1310         cdr.Seeked = SEEK_DONE;
1311
1312         ReadTrack( cdr.SetSector );
1313
1314         buf = CDR_getBuffer();
1315         if (buf == NULL)
1316                 cdr.RErr = -1;
1317
1318         if (cdr.RErr == -1) {
1319 #ifdef CDR_LOG
1320                 fprintf(emuLog, "cdrReadInterrupt() Log: err\n");
1321 #endif
1322                 memset(cdr.Transfer, 0, DATA_SIZE);
1323                 cdr.Stat = DiskError;
1324                 cdr.Result[0] |= STATUS_ERROR;
1325                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1326                 return;
1327         }
1328
1329         memcpy(cdr.Transfer, buf, DATA_SIZE);
1330         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1331
1332
1333 #ifdef CDR_LOG
1334         fprintf(emuLog, "cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1335 #endif
1336
1337         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1338                 // Firemen 2: Multi-XA files - briefings, cutscenes
1339                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1340                         cdr.File = cdr.Transfer[4 + 0];
1341                         cdr.Channel = cdr.Transfer[4 + 1];
1342                 }
1343
1344                 if ((cdr.Transfer[4 + 2] & 0x4) &&
1345                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1346                         (cdr.Transfer[4 + 0] == cdr.File)) {
1347                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1348
1349                         if (!ret) {
1350                                 // only handle attenuator basic channel switch for now
1351                                 if (cdr.Xa.stereo) {
1352                                         int i;
1353                                         if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1354                                             && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1355                                         {
1356                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1357                                                         cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2];
1358                                         }
1359                                         else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1360                                             && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1361                                         {
1362                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1363                                                         cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1];
1364                                         }
1365                                 }
1366
1367                                 SPU_playADPCMchannel(&cdr.Xa);
1368                                 cdr.FirstSector = 0;
1369
1370 #if 0
1371                                 // Crash Team Racing: music, speech
1372                                 // - done using cdda decoded buffer (spu irq)
1373                                 // - don't do here
1374
1375                                 // signal ADPCM data ready
1376                                 psxHu32ref(0x1070) |= SWAP32((u32)0x200);
1377 #endif
1378                         }
1379                         else cdr.FirstSector = -1;
1380                 }
1381         }
1382
1383         cdr.SetSector[2]++;
1384         if (cdr.SetSector[2] == 75) {
1385                 cdr.SetSector[2] = 0;
1386                 cdr.SetSector[1]++;
1387                 if (cdr.SetSector[1] == 60) {
1388                         cdr.SetSector[1] = 0;
1389                         cdr.SetSector[0]++;
1390                 }
1391         }
1392
1393         cdr.Readed = 0;
1394
1395         // G-Police: Don't autopause ADPCM even if mode set (music)
1396         if ((cdr.Transfer[4 + 2] & 0x80) && (cdr.Mode & MODE_AUTOPAUSE) &&
1397                         (cdr.Transfer[4 + 2] & 0x4) != 0x4 ) { // EOF
1398 #ifdef CDR_LOG
1399                 CDR_LOG("cdrReadInterrupt() Log: Autopausing read\n");
1400 #endif
1401 //              AddIrqQueue(AUTOPAUSE, 0x2000);
1402                 AddIrqQueue(CdlPause, 0x2000);
1403         }
1404         else {
1405                 CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1406         }
1407
1408         /*
1409         Croc 2: $40 - only FORM1 (*)
1410         Judge Dredd: $C8 - only FORM1 (*)
1411         Sim Theme Park - no adpcm at all (zero)
1412         */
1413
1414         if( (cdr.Mode & MODE_STRSND) == 0 || (cdr.Transfer[4+2] & 0x4) != 0x4 ) {
1415                 cdr.Stat = DataReady;
1416         } else {
1417                 // Breath of Fire 3 - fix inn sleeping
1418                 // Rockman X5 - no music restart problem
1419                 cdr.Stat = NoIntr;
1420         }
1421         psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1422
1423         Check_Shell(0);
1424 }
1425
1426 /*
1427 cdrRead0:
1428         bit 0 - 0 REG1 command send / 1 REG1 data read
1429         bit 1 - 0 data transfer finish / 1 data transfer ready/in progress
1430         bit 2 - unknown
1431         bit 3 - unknown
1432         bit 4 - unknown
1433         bit 5 - 1 result ready
1434         bit 6 - 1 dma ready
1435         bit 7 - 1 command being processed
1436 */
1437
1438 unsigned char cdrRead0(void) {
1439         if (cdr.ResultReady)
1440                 cdr.Ctrl |= 0x20;
1441         else
1442                 cdr.Ctrl &= ~0x20;
1443
1444         if (cdr.OCUP)
1445                 cdr.Ctrl |= 0x40;
1446 //  else
1447 //              cdr.Ctrl &= ~0x40;
1448
1449         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1450         cdr.Ctrl |= 0x18;
1451
1452 #ifdef CDR_LOG
1453         CDR_LOG("cdrRead0() Log: CD0 Read: %x\n", cdr.Ctrl);
1454 #endif
1455
1456         return psxHu8(0x1800) = cdr.Ctrl;
1457 }
1458
1459 /*
1460 cdrWrite0:
1461         0 - to send a command / 1 - to get the result
1462 */
1463
1464 void cdrWrite0(unsigned char rt) {
1465 #ifdef CDR_LOG
1466         CDR_LOG("cdrWrite0() Log: CD0 write: %x\n", rt);
1467 #endif
1468         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1469 }
1470
1471 unsigned char cdrRead1(void) {
1472     if (cdr.ResultReady) { // && cdr.Ctrl & 0x1) {
1473                 // GameShark CDX CD Player: uses 17 bytes output (wraps around)
1474                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1475                 cdr.ResultP++;
1476                 if (cdr.ResultP == cdr.ResultC)
1477                         cdr.ResultReady = 0;
1478         } else {
1479                 psxHu8(0x1801) = 0;
1480         }
1481 #ifdef CDR_LOG
1482         CDR_LOG("cdrRead1() Log: CD1 Read: %x\n", psxHu8(0x1801));
1483 #endif
1484         return psxHu8(0x1801);
1485 }
1486
1487 void cdrWrite1(unsigned char rt) {
1488         u8 set_loc[3];
1489         int i;
1490
1491 #ifdef CDR_LOG
1492         CDR_LOG("cdrWrite1() Log: CD1 write: %x (%s)\n", rt, CmdName[rt]);
1493 #endif
1494
1495
1496         // Tekken: CDXA fade-out
1497         if( (cdr.Ctrl & 3) == 3 ) {
1498                 cdr.AttenuatorRight[0] = rt;
1499         }
1500
1501
1502 //      psxHu8(0x1801) = rt;
1503         cdr.Cmd = rt;
1504         cdr.OCUP = 0;
1505
1506 #ifdef CDRCMD_DEBUG
1507         SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]);
1508         if (cdr.ParamC) {
1509                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1510                 for (i = 0; i < cdr.ParamC; i++)
1511                         SysPrintf(" %x,", cdr.Param[i]);
1512                 SysPrintf("}\n");
1513         } else {
1514                 SysPrintf("\n");
1515         }
1516 #endif
1517
1518         if (cdr.Ctrl & 0x3) return;
1519
1520         cdr.ResultReady = 0;
1521
1522         switch (cdr.Cmd) {
1523         case CdlSync:
1524                 cdr.Ctrl |= 0x80;
1525                 cdr.Stat = NoIntr; 
1526                 AddIrqQueue(cdr.Cmd, 0x800);
1527                 break;
1528
1529         case CdlNop:
1530                 cdr.Ctrl |= 0x80;
1531                 cdr.Stat = NoIntr; 
1532
1533                 // Twisted Metal 3 - fix music
1534                 AddIrqQueue(cdr.Cmd, 0x800);
1535                 break;
1536
1537         case CdlSetloc:
1538                 StopReading();
1539                 for (i = 0; i < 3; i++)
1540                         set_loc[i] = btoi(cdr.Param[i]);
1541
1542                 i = abs(msf2sec(cdr.SetSector) - msf2sec(set_loc));
1543                 if (i > 16)
1544                        cdr.Seeked = SEEK_PENDING;
1545
1546                 memcpy(cdr.SetSector, set_loc, 3);
1547                 cdr.SetSector[3] = 0;
1548
1549                 cdr.Ctrl |= 0x80;
1550                 cdr.Stat = NoIntr;
1551                 AddIrqQueue(cdr.Cmd, 0x800);
1552                 break;
1553
1554         case CdlPlay:
1555                 // Vib Ribbon: try same track again
1556                 StopCdda();
1557
1558                 if (!cdr.SetSector[0] & !cdr.SetSector[1] & !cdr.SetSector[2]) {
1559                         if (CDR_getTN(cdr.ResultTN) != -1) {
1560                                 if (cdr.CurTrack > cdr.ResultTN[1])
1561                                         cdr.CurTrack = cdr.ResultTN[1];
1562                                 if (CDR_getTD((unsigned char)(cdr.CurTrack), cdr.ResultTD) != -1) {
1563                                         int tmp = cdr.ResultTD[2];
1564                                         cdr.ResultTD[2] = cdr.ResultTD[0];
1565                                         cdr.ResultTD[0] = tmp;
1566                                         if (!Config.Cdda) CDR_play(cdr.ResultTD);
1567                                 }
1568                         }
1569                 } else if (!Config.Cdda) {
1570                         CDR_play(cdr.SetSector);
1571                 }
1572
1573                 // Vib Ribbon - decoded buffer IRQ for CDDA reading
1574                 // - fixes ribbon timing + music CD mode
1575                 //TODO?
1576                 //CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
1577
1578
1579                 cdr.Play = TRUE;
1580
1581                 cdr.StatP |= STATUS_SEEK;
1582                 cdr.StatP &= ~STATUS_ROTATING;
1583
1584                 cdr.Ctrl |= 0x80;
1585                 cdr.Stat = NoIntr; 
1586                 AddIrqQueue(cdr.Cmd, 0x800);
1587                 break;
1588
1589         case CdlForward:
1590                 //if (cdr.CurTrack < 0xaa)
1591                 //      cdr.CurTrack++;
1592                 cdr.Ctrl |= 0x80;
1593                 cdr.Stat = NoIntr; 
1594                 AddIrqQueue(cdr.Cmd, 0x800);
1595                 break;
1596
1597         case CdlBackward:
1598                 //if (cdr.CurTrack > 1)
1599                 //cdr.CurTrack--;
1600                 cdr.Ctrl |= 0x80;
1601                 cdr.Stat = NoIntr; 
1602                 AddIrqQueue(cdr.Cmd, 0x800);
1603                 break;
1604
1605         case CdlReadN:
1606                 cdr.Irq = 0;
1607                 StopReading();
1608                 cdr.Ctrl|= 0x80;
1609                 cdr.Stat = NoIntr; 
1610                 StartReading(1, 0x800);
1611                 break;
1612
1613         case CdlStandby:
1614                 StopCdda();
1615                 StopReading();
1616                 cdr.Ctrl |= 0x80;
1617                 cdr.Stat = NoIntr;
1618                 AddIrqQueue(cdr.Cmd, 0x800);
1619                 break;
1620
1621         case CdlStop:
1622                 // GameShark CD Player: Reset CDDA to track start
1623                 if( cdr.Play && CDR_getStatus(&stat) != -1 ) {
1624                         cdr.SetSectorPlay[0] = stat.Time[0];
1625                         cdr.SetSectorPlay[1] = stat.Time[1];
1626                         cdr.SetSectorPlay[2] = stat.Time[2];
1627
1628                         Find_CurTrack();
1629
1630
1631                         // grab time for current track
1632                         CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
1633
1634                         cdr.SetSectorPlay[0] = cdr.ResultTD[2];
1635                         cdr.SetSectorPlay[1] = cdr.ResultTD[1];
1636                         cdr.SetSectorPlay[2] = cdr.ResultTD[0];
1637                 }
1638
1639                 StopCdda();
1640                 StopReading();
1641
1642                 cdr.Ctrl |= 0x80;
1643                 cdr.Stat = NoIntr;
1644                 AddIrqQueue(cdr.Cmd, 0x800);
1645                 break;
1646
1647         case CdlPause:
1648                 /*
1649                    GameShark CD Player: save time for resume
1650
1651                    Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors
1652                 */
1653
1654                 StopCdda();
1655                 StopReading();
1656                 cdr.Ctrl |= 0x80;
1657                 cdr.Stat = NoIntr;
1658
1659                 AddIrqQueue(cdr.Cmd, 0x800);
1660                 break;
1661
1662         case CdlReset:
1663         case CdlInit:
1664                 cdr.Seeked = SEEK_DONE;
1665                 StopCdda();
1666                 StopReading();
1667                 cdr.Ctrl |= 0x80;
1668                 cdr.Stat = NoIntr; 
1669                 AddIrqQueue(cdr.Cmd, 0x800);
1670                 break;
1671
1672         case CdlMute:
1673                 cdr.Muted = TRUE;
1674                 cdr.Ctrl |= 0x80;
1675                 cdr.Stat = NoIntr; 
1676                 AddIrqQueue(cdr.Cmd, 0x800);
1677
1678                         // Duke Nukem - Time to Kill
1679                         // - do not directly set cd-xa volume
1680                         //SPU_writeRegister( H_CDLeft, 0x0000 );
1681                         //SPU_writeRegister( H_CDRight, 0x0000 );
1682                 break;
1683
1684         case CdlDemute:
1685                 cdr.Muted = FALSE;
1686                 cdr.Ctrl |= 0x80;
1687                 cdr.Stat = NoIntr; 
1688                 AddIrqQueue(cdr.Cmd, 0x800);
1689
1690                         // Duke Nukem - Time to Kill
1691                         // - do not directly set cd-xa volume
1692                         //SPU_writeRegister( H_CDLeft, 0x7f00 );
1693                         //SPU_writeRegister( H_CDRight, 0x7f00 );
1694                 break;
1695
1696         case CdlSetfilter:
1697                 cdr.File = cdr.Param[0];
1698                 cdr.Channel = cdr.Param[1];
1699                 cdr.Ctrl |= 0x80;
1700                 cdr.Stat = NoIntr; 
1701                 AddIrqQueue(cdr.Cmd, 0x800);
1702                 break;
1703
1704         case CdlSetmode:
1705 #ifdef CDR_LOG
1706                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1707 #endif 
1708                 cdr.Mode = cdr.Param[0];
1709                 cdr.Ctrl |= 0x80;
1710                 cdr.Stat = NoIntr; 
1711                 AddIrqQueue(cdr.Cmd, 0x800);
1712
1713                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1714                 // - fixes choppy movie sound
1715                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1716                         StopCdda();
1717                 break;
1718
1719         case CdlGetmode:
1720                 cdr.Ctrl |= 0x80;
1721                 cdr.Stat = NoIntr; 
1722                 AddIrqQueue(cdr.Cmd, 0x800);
1723                 break;
1724
1725         case CdlGetlocL:
1726                 cdr.Ctrl |= 0x80;
1727                 cdr.Stat = NoIntr; 
1728
1729                 // Crusaders of Might and Magic - cutscene speech
1730                 AddIrqQueue(cdr.Cmd, 0x800);
1731                 break;
1732
1733         case CdlGetlocP:
1734                 cdr.Ctrl |= 0x80;
1735                 cdr.Stat = NoIntr; 
1736
1737                 // GameShark CDX / Lite Player: pretty narrow time window
1738                 // - doesn't always work due to time inprecision
1739                 //AddIrqQueue(cdr.Cmd, 0x28);
1740
1741                 // Tomb Raider 2 - cdda
1742                 //AddIrqQueue(cdr.Cmd, 0x40);
1743
1744                 // rearmed: the above works in pcsxr-svn, but breaks here
1745                 // (TOCA world touring cars), perhaps some other code is not merged yet
1746                 AddIrqQueue(cdr.Cmd, 0x1000);
1747                 break;
1748
1749         case CdlGetTN:
1750                 cdr.Ctrl |= 0x80;
1751                 cdr.Stat = NoIntr; 
1752                 //AddIrqQueue(cdr.Cmd, 0x800);
1753
1754                 // GameShark CDX CD Player: very long time
1755                 AddIrqQueue(cdr.Cmd, 0x100000);
1756                 break;
1757
1758         case CdlGetTD:
1759                 cdr.Ctrl |= 0x80;
1760                 cdr.Stat = NoIntr; 
1761                 AddIrqQueue(cdr.Cmd, 0x800);
1762                 break;
1763
1764         case CdlSeekL:
1765 //                      ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0];
1766                 cdr.Ctrl |= 0x80;
1767                 cdr.Stat = NoIntr; 
1768                 AddIrqQueue(cdr.Cmd, 0x800);
1769
1770                 StopCdda();
1771                 StopReading();
1772
1773                 break;
1774
1775         case CdlSeekP:
1776 //              ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0];
1777                 cdr.Ctrl |= 0x80;
1778                 cdr.Stat = NoIntr; 
1779
1780                 // Tomb Raider 2 - reset cdda
1781                 StopCdda();
1782                 StopReading();
1783
1784                 AddIrqQueue(cdr.Cmd, 0x800);
1785                 break;
1786
1787         // Destruction Derby: read TOC? GetTD after this
1788         case CdlReadT:
1789                 cdr.Ctrl |= 0x80;
1790                 cdr.Stat = NoIntr;
1791                 AddIrqQueue(cdr.Cmd, 0x800);
1792                 break;
1793
1794         case CdlTest:
1795                 cdr.Ctrl |= 0x80;
1796                 cdr.Stat = NoIntr; 
1797                 AddIrqQueue(cdr.Cmd, 0x800);
1798                 break;
1799
1800         case CdlID:
1801                 cdr.Ctrl |= 0x80;
1802                 cdr.Stat = NoIntr; 
1803                 AddIrqQueue(cdr.Cmd, 0x800);
1804                 break;
1805
1806         case CdlReadS:
1807                 cdr.Irq = 0;
1808                 StopReading();
1809                 cdr.Ctrl |= 0x80;
1810                 cdr.Stat = NoIntr; 
1811                 StartReading(2, 0x800);
1812                 break;
1813
1814         case CdlReadToc:
1815                 cdr.Ctrl |= 0x80;
1816                 cdr.Stat = NoIntr; 
1817                 AddIrqQueue(cdr.Cmd, 0x800);
1818                 break;
1819
1820         default:
1821                 cdr.ParamP = 0;
1822                 cdr.ParamC = 0;
1823 #ifdef CDR_LOG
1824                 CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd);
1825 #endif
1826                 return;
1827         }
1828         if (cdr.Stat != NoIntr) {
1829                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1830         }
1831 }
1832
1833 unsigned char cdrRead2(void) {
1834         unsigned char ret;
1835
1836         if (cdr.Readed == 0) {
1837                 ret = 0;
1838         } else {
1839                 ret = *pTransfer++;
1840         }
1841
1842 #ifdef CDR_LOG
1843         CDR_LOG("cdrRead2() Log: CD2 Read: %x\n", ret);
1844 #endif
1845         return ret;
1846 }
1847
1848 void cdrWrite2(unsigned char rt) {
1849 #ifdef CDR_LOG
1850         CDR_LOG("cdrWrite2() Log: CD2 write: %x\n", rt);
1851 #endif
1852
1853         // Tekken: CDXA fade-out
1854         if( (cdr.Ctrl & 3) == 2 ) {
1855                 cdr.AttenuatorLeft[0] = rt;
1856         }
1857         else if( (cdr.Ctrl & 3) == 3 ) {
1858                 cdr.AttenuatorRight[1] = rt;
1859         }
1860
1861
1862         if (cdr.Ctrl & 0x1) {
1863                 switch (rt) {
1864                         case 0x07:
1865                                 cdr.ParamP = 0;
1866                                 cdr.ParamC = 0;
1867                                 cdr.ResultReady = 1; //0;
1868                                 cdr.Ctrl &= ~3; //cdr.Ctrl = 0;
1869                                 break;
1870
1871                         default:
1872                                 cdr.Reg2 = rt;
1873                                 break;
1874                 }
1875         } else if (!(cdr.Ctrl & 0x3) && cdr.ParamP < 8) {
1876                 cdr.Param[cdr.ParamP++] = rt;
1877                 cdr.ParamC++;
1878         }
1879 }
1880
1881 unsigned char cdrRead3(void) {
1882         if (cdr.Stat) {
1883                 if (cdr.Ctrl & 0x1)
1884                         psxHu8(0x1803) = cdr.Stat | 0xE0;
1885                 else
1886                         psxHu8(0x1803) = 0xff;
1887         } else {
1888                 psxHu8(0x1803) = 0;
1889         }
1890 #ifdef CDR_LOG
1891         CDR_LOG("cdrRead3() Log: CD3 Read: %x\n", psxHu8(0x1803));
1892 #endif
1893         return psxHu8(0x1803);
1894 }
1895
1896 void cdrWrite3(unsigned char rt) {
1897 #ifdef CDR_LOG
1898         CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt);
1899 #endif
1900
1901         // Tekken: CDXA fade-out
1902         if( (cdr.Ctrl & 3) == 2 ) {
1903                 cdr.AttenuatorLeft[1] = rt;
1904         }
1905         else if( (cdr.Ctrl & 3) == 3 && rt == 0x20 ) {
1906 #ifdef CDR_LOG
1907                 CDR_LOG( "CD-XA Volume: %X %X | %X %X\n",
1908                         cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1],
1909                         cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
1910 #endif
1911         }
1912
1913
1914         // GameShark CDX CD Player: Irq timing mania
1915         if( rt == 0 &&
1916                         cdr.Irq != 0 && cdr.Irq != 0xff &&
1917                         cdr.ResultReady == 0 ) {
1918
1919                 // GS CDX: ~0x28 cycle timing - way too precise
1920                 if( cdr.Irq == CdlGetlocP ) {
1921                         cdrInterrupt();
1922
1923                         psxRegs.interrupt &= ~(1 << PSXINT_CDR);
1924                 }
1925         }
1926
1927
1928         if (rt == 0x07 && (cdr.Ctrl & 3) == 1) {
1929                 cdr.Stat = 0;
1930
1931                 if (cdr.Irq == 0xff) {
1932                         cdr.Irq = 0;
1933                         return;
1934                 }
1935
1936                 // XA streaming - incorrect timing because of this reschedule
1937                 // - Final Fantasy Tactics
1938                 // - various other games
1939
1940                 if (cdr.Reading && !cdr.ResultReady) {
1941                         int left = psxRegs.intCycle[PSXINT_CDREAD].sCycle + psxRegs.intCycle[PSXINT_CDREAD].cycle - psxRegs.cycle;
1942                         int time = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
1943                         if (Config.CdrReschedule != 2)
1944                         if (left < time / 2 || Config.CdrReschedule) { // rearmed guesswork hack
1945                                 //printf("-- resched %d -> %d\n", left, time);
1946                                 CDREAD_INT(time);
1947                         }
1948                 }
1949
1950                 return;
1951         }
1952
1953         if (rt == 0x80 && !(cdr.Ctrl & 0x3) && cdr.Readed == 0) {
1954                 cdr.Readed = 1;
1955                 pTransfer = cdr.Transfer;
1956
1957                 switch (cdr.Mode & 0x30) {
1958                         case MODE_SIZE_2328:
1959                         case 0x00:
1960                                 pTransfer += 12;
1961                                 break;
1962
1963                         case MODE_SIZE_2340:
1964                                 pTransfer += 0;
1965                                 break;
1966
1967                         default:
1968                                 break;
1969                 }
1970         }
1971 }
1972
1973 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1974         u32 cdsize;
1975         int size;
1976         u8 *ptr;
1977
1978 #ifdef CDR_LOG
1979         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1980 #endif
1981
1982         switch (chcr) {
1983                 case 0x11000000:
1984                 case 0x11400100:
1985                         if (cdr.Readed == 0) {
1986 #ifdef CDR_LOG
1987                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1988 #endif
1989                                 break;
1990                         }
1991
1992                         cdsize = (bcr & 0xffff) * 4;
1993
1994                         // Ape Escape: bcr = 0001 / 0000
1995                         // - fix boot
1996                         if( cdsize == 0 )
1997                         {
1998                                 switch (cdr.Mode & 0x30) {
1999                                         case 0x00: cdsize = 2048; break;
2000                                         case MODE_SIZE_2328: cdsize = 2328; break;
2001                                         case MODE_SIZE_2340: cdsize = 2340; break;
2002                                 }
2003                         }
2004
2005
2006                         ptr = (u8 *)PSXM(madr);
2007                         if (ptr == NULL) {
2008 #ifdef CPU_LOG
2009                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
2010 #endif
2011                                 break;
2012                         }
2013
2014                         /*
2015                         GS CDX: Enhancement CD crash
2016                         - Setloc 0:0:0
2017                         - CdlPlay
2018                         - Spams DMA3 and gets buffer overrun
2019                         */
2020                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
2021                         if (size > cdsize)
2022                                 size = cdsize;
2023                         if (size > 0)
2024                         {
2025                                 memcpy(ptr, pTransfer, size);
2026                         }
2027
2028                         psxCpu->Clear(madr, cdsize / 4);
2029                         pTransfer += cdsize;
2030
2031
2032                         // burst vs normal
2033                         if( chcr == 0x11400100 ) {
2034                                 CDRDMA_INT( (cdsize/4) / 4 );
2035                         }
2036                         else if( chcr == 0x11000000 ) {
2037                                 CDRDMA_INT( (cdsize/4) * 1 );
2038                         }
2039                         return;
2040
2041                 default:
2042 #ifdef CDR_LOG
2043                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
2044 #endif
2045                         break;
2046         }
2047
2048         HW_DMA3_CHCR &= SWAP32(~0x01000000);
2049         DMA_INTERRUPT(3);
2050 }
2051
2052 void cdrDmaInterrupt()
2053 {
2054         if (HW_DMA3_CHCR & SWAP32(0x01000000))
2055         {
2056                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
2057                 DMA_INTERRUPT(3);
2058         }
2059 }
2060
2061 void cdrReset() {
2062         memset(&cdr, 0, sizeof(cdr));
2063         cdr.CurTrack = 1;
2064         cdr.File = 1;
2065         cdr.Channel = 1;
2066         pTransfer = cdr.Transfer;
2067
2068         // BIOS player - default values
2069         cdr.AttenuatorLeft[0] = 0x80;
2070         cdr.AttenuatorLeft[1] = 0x00;
2071         cdr.AttenuatorRight[0] = 0x80;
2072         cdr.AttenuatorRight[1] = 0x00;
2073 }
2074
2075 int cdrFreeze(gzFile f, int Mode) {
2076         u32 tmp;
2077
2078         if( Mode == 0 ) {
2079                 StopCdda();
2080         }
2081         
2082         gzfreeze(&cdr, sizeof(cdr));
2083         
2084         if (Mode == 1)
2085                 tmp = pTransfer - cdr.Transfer;
2086
2087         gzfreeze(&tmp, sizeof(tmp));
2088
2089         if (Mode == 0) {
2090                 pTransfer = cdr.Transfer + tmp;
2091
2092                 if (cdr.Play && !Config.Cdda)
2093                         CDR_play(cdr.SetSectorPlay);
2094         }
2095
2096         return 0;
2097 }
2098
2099 void LidInterrupt() {
2100         cdr.LidCheck = 0x20; // start checker
2101
2102         CDRLID_INT( cdReadTime * 3 );
2103         
2104         // generate interrupt if none active - open or close
2105         if (cdr.Irq == 0 || cdr.Irq == 0xff) {
2106                 cdr.Ctrl |= 0x80;
2107                 cdr.Stat = NoIntr;
2108                 AddIrqQueue(CdlNop, 0x800);
2109         }
2110 }