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