cdrom: tune and log stat hack
[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                 if (cdr.Stat) {
654                         SysPrintf("cdrom: seek stat hack\n");
655                         CDRMISC_INT(0x1000);
656                         return;
657                 }
658                 SetResultSize(1);
659                 cdr.StatP |= STATUS_ROTATING;
660                 cdr.StatP &= ~STATUS_SEEK;
661                 cdr.Result[0] = cdr.StatP;
662                 if (cdr.Irq == 0 || cdr.Irq == 0xff) {
663                         cdr.Stat = Complete;
664                         if (cdr.Reg2 != 0x18)
665                                 psxHu32ref(0x1070) |= SWAP32(0x4);
666                 }
667
668                 cdr.Seeked = SEEK_PENDING;
669                 CDRMISC_INT(cdReadTime * 20); // ???
670                 return;
671         }
672         else if (cdr.Seeked == SEEK_PENDING) {
673                 cdr.Seeked = SEEK_DONE;
674                 if (!cdr.Play && !cdr.Reading) {
675                         memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
676                         Find_CurTrack();
677                         ReadTrack(cdr.SetSector);
678                 }
679         }
680
681         if (!cdr.Play) return;
682
683 #ifdef CDR_LOG
684         CDR_LOG( "CDDA - %d:%d:%d\n",
685                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
686 #endif
687         CDRMISC_INT( cdReadTime );
688
689         if (!cdr.Irq && !cdr.Stat && (cdr.Mode & MODE_CDDA) && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
690                 cdrPlayInterrupt_Autopause();
691
692         cdr.SetSectorPlay[2]++;
693         if (cdr.SetSectorPlay[2] == 75) {
694                 cdr.SetSectorPlay[2] = 0;
695                 cdr.SetSectorPlay[1]++;
696                 if (cdr.SetSectorPlay[1] == 60) {
697                         cdr.SetSectorPlay[1] = 0;
698                         cdr.SetSectorPlay[0]++;
699                 }
700         }
701
702         //Check_Shell(0);
703 }
704
705 void cdrInterrupt() {
706         int i;
707         unsigned char Irq = cdr.Irq;
708         struct SubQ *subq;
709
710         // Reschedule IRQ
711         if (cdr.Stat) {
712                 SysPrintf("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat);
713                 CDR_INT(0x1000);
714                 return;
715         }
716
717         cdr.Irq = 0xff;
718         cdr.Ctrl &= ~0x80;
719
720         switch (Irq) {
721                 case CdlSync:
722                         SetResultSize(1);
723                         cdr.StatP |= STATUS_ROTATING;
724                         cdr.Result[0] = cdr.StatP;
725                         cdr.Stat = Acknowledge; 
726                         break;
727
728                 case CdlNop:
729                         SetResultSize(1);
730                         cdr.Result[0] = cdr.StatP;
731                         cdr.Stat = Acknowledge;
732
733                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
734                         break;
735
736                 case CdlSetloc:
737                         cdr.CmdProcess = 0;
738                         SetResultSize(1);
739                         cdr.StatP |= STATUS_ROTATING;
740                         cdr.Result[0] = cdr.StatP;
741                         cdr.Stat = Acknowledge;
742                         break;
743
744                 case CdlPlay:
745                         fake_subq_change = 0;
746
747                         if (cdr.Seeked == SEEK_PENDING) {
748                                 // XXX: wrong, should seek instead..
749                                 memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
750                                 cdr.Seeked = SEEK_DONE;
751                         }
752
753                         /*
754                         Rayman: detect track changes
755                         - fixes logo freeze
756
757                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
758                         - plays tracks without retry play
759
760                         Wild 9: skip PREGAP + starting accurate SubQ
761                         - plays tracks without retry play
762                         */
763                         /* unneeded with correct cdriso?
764                         Set_Track();
765                         */
766                         Find_CurTrack();
767                         ReadTrack( cdr.SetSectorPlay );
768
769                         // GameShark CD Player: Calls 2x + Play 2x
770                         if( cdr.FastBackward || cdr.FastForward ) {
771                                 if( cdr.FastForward ) cdr.FastForward--;
772                                 if( cdr.FastBackward ) cdr.FastBackward--;
773
774                                 if( cdr.FastBackward == 0 && cdr.FastForward == 0 ) {
775                                         if( cdr.Play && CDR_getStatus(&stat) != -1 ) {
776                                                 cdr.SetSectorPlay[0] = stat.Time[0];
777                                                 cdr.SetSectorPlay[1] = stat.Time[1];
778                                                 cdr.SetSectorPlay[2] = stat.Time[2];
779                                         }
780                                 }
781                         }
782
783
784                         if (!Config.Cdda) {
785                                 // BIOS CD Player
786                                 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
787
788                                 // GameShark CD Player: Resume play
789                                 if( cdr.ParamC == 0 ) {
790 #ifdef CDR_LOG___0
791                                         CDR_LOG( "PLAY Resume @ %d:%d:%d\n",
792                                                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
793 #endif
794
795                                         //CDR_play( cdr.SetSectorPlay );
796                                 }
797                                 else
798                                 {
799                                         // BIOS CD Player: Resume play
800                                         if( cdr.Param[0] == 0 ) {
801 #ifdef CDR_LOG___0
802                                                 CDR_LOG( "PLAY Resume T0 @ %d:%d:%d\n",
803                                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
804 #endif
805
806                                                 //CDR_play( cdr.SetSectorPlay );
807                                         }
808                                         else {
809 #ifdef CDR_LOG___0
810                                                 CDR_LOG( "PLAY Resume Td @ %d:%d:%d\n",
811                                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
812 #endif
813
814                                                 // BIOS CD Player: Allow track replaying
815                                                 StopCdda();
816
817
818                                                 cdr.CurTrack = btoi( cdr.Param[0] );
819
820                                                 if (CDR_getTN(cdr.ResultTN) != -1) {
821                                                         // check last track
822                                                         if (cdr.CurTrack > cdr.ResultTN[1])
823                                                                 cdr.CurTrack = cdr.ResultTN[1];
824
825                                                         if (CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD) != -1) {
826                                                                 cdr.SetSectorPlay[0] = cdr.ResultTD[2];
827                                                                 cdr.SetSectorPlay[1] = cdr.ResultTD[1];
828                                                                 cdr.SetSectorPlay[2] = cdr.ResultTD[0];
829
830                                                                 // reset data
831                                                                 //Set_Track();
832                                                                 Find_CurTrack();
833                                                                 ReadTrack( cdr.SetSectorPlay );
834
835                                                                 //CDR_play(cdr.SetSectorPlay);
836                                                         }
837                                                 }
838                                         }
839                                 }
840                         }
841
842
843                         // Vib Ribbon: gameplay checks flag
844                         cdr.StatP &= ~STATUS_SEEK;
845
846
847                         cdr.CmdProcess = 0;
848                         SetResultSize(1);
849                         cdr.StatP |= STATUS_ROTATING;
850                         cdr.Result[0] = cdr.StatP;
851                         cdr.Stat = Acknowledge;
852
853                         cdr.StatP |= STATUS_PLAY;
854
855                         
856                         // BIOS player - set flag again
857                         cdr.Play = TRUE;
858
859                         CDRMISC_INT( cdReadTime );
860                         break;
861
862                 case CdlForward:
863                         cdr.CmdProcess = 0;
864                         SetResultSize(1);
865                         cdr.StatP |= STATUS_ROTATING;
866                         cdr.Result[0] = cdr.StatP;
867                         cdr.Stat = Complete;
868
869
870                         // GameShark CD Player: Calls 2x + Play 2x
871                         if( cdr.FastForward == 0 ) cdr.FastForward = 2;
872                         else cdr.FastForward++;
873
874                         cdr.FastBackward = 0;
875                         break;
876
877                 case CdlBackward:
878                         cdr.CmdProcess = 0;
879                         SetResultSize(1);
880                         cdr.StatP |= STATUS_ROTATING;
881                         cdr.Result[0] = cdr.StatP;
882                         cdr.Stat = Complete;
883
884
885                         // GameShark CD Player: Calls 2x + Play 2x
886                         if( cdr.FastBackward == 0 ) cdr.FastBackward = 2;
887                         else cdr.FastBackward++;
888
889                         cdr.FastForward = 0;
890                         break;
891
892                 case CdlStandby:
893                         cdr.CmdProcess = 0;
894                         SetResultSize(1);
895                         cdr.StatP |= STATUS_ROTATING;
896                         cdr.Result[0] = cdr.StatP;
897                         cdr.Stat = Complete;
898                         break;
899
900                 case CdlStop:
901                         cdr.CmdProcess = 0;
902                         SetResultSize(1);
903                         cdr.StatP &= ~STATUS_ROTATING;
904                         cdr.Result[0] = cdr.StatP;
905                         cdr.Stat = Complete;
906 //                      cdr.Stat = Acknowledge;
907
908                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
909                         break;
910
911                 case CdlPause:
912                         SetResultSize(1);
913                         cdr.Result[0] = cdr.StatP;
914                         cdr.Stat = Acknowledge;
915
916                         /*
917                         Gundam Battle Assault 2: much slower (*)
918                         - Fixes boot, gameplay
919
920                         Hokuto no Ken 2: slower
921                         - Fixes intro + subtitles
922
923                         InuYasha - Feudal Fairy Tale: slower
924                         - Fixes battles
925                         */
926                         AddIrqQueue(CdlPause + 0x20, cdReadTime * 3);
927                         cdr.Ctrl |= 0x80;
928                         break;
929
930                 case CdlPause + 0x20:
931                         SetResultSize(1);
932                         cdr.StatP &= ~STATUS_READ;
933                         cdr.StatP |= STATUS_ROTATING;
934                         cdr.Result[0] = cdr.StatP;
935                         cdr.Stat = Complete;
936                         break;
937
938                 case CdlInit:
939                         SetResultSize(1);
940                         cdr.StatP = STATUS_ROTATING;
941                         cdr.Result[0] = cdr.StatP;
942                         cdr.Stat = Acknowledge;
943 //                      if (!cdr.Init) {
944                                 AddIrqQueue(CdlInit + 0x20, 0x800);
945 //                      }
946                 break;
947
948                 case CdlInit + 0x20:
949                         SetResultSize(1);
950                         cdr.Result[0] = cdr.StatP;
951                         cdr.Stat = Complete;
952                         cdr.Init = 1;
953                         break;
954
955                 case CdlMute:
956                         SetResultSize(1);
957                         cdr.StatP |= STATUS_ROTATING;
958                         cdr.Result[0] = cdr.StatP;
959                         cdr.Stat = Acknowledge;
960                         break;
961
962                 case CdlDemute:
963                         SetResultSize(1);
964                         cdr.StatP |= STATUS_ROTATING;
965                         cdr.Result[0] = cdr.StatP;
966                         cdr.Stat = Acknowledge;
967                         break;
968
969                 case CdlSetfilter:
970                         SetResultSize(1);
971                         cdr.StatP |= STATUS_ROTATING;
972                         cdr.Result[0] = cdr.StatP;
973                         cdr.Stat = Acknowledge; 
974                         break;
975
976                 case CdlSetmode:
977                         SetResultSize(1);
978                         cdr.StatP |= STATUS_ROTATING;
979                         cdr.Result[0] = cdr.StatP;
980                         cdr.Stat = Acknowledge;
981                         break;
982
983                 case CdlGetmode:
984                         SetResultSize(6);
985                         cdr.StatP |= STATUS_ROTATING;
986                         cdr.Result[0] = cdr.StatP;
987                         cdr.Result[1] = cdr.Mode;
988                         cdr.Result[2] = cdr.File;
989                         cdr.Result[3] = cdr.Channel;
990                         cdr.Result[4] = 0;
991                         cdr.Result[5] = 0;
992                         cdr.Stat = Acknowledge;
993                         break;
994
995                 case CdlGetlocL:
996                         SetResultSize(8);
997                         for (i = 0; i < 8; i++)
998                                 cdr.Result[i] = cdr.Transfer[i];
999                         cdr.Stat = Acknowledge;
1000                         break;
1001
1002                 case CdlGetlocP:
1003                         // GameShark CDX CD Player: uses 17 bytes output (wraps around)
1004                         SetResultSize(17);
1005                         memset( cdr.Result, 0, 16 );
1006
1007                         subq = (struct SubQ *)CDR_getBufferSub();
1008
1009                         if (subq != NULL) {
1010                                 if( cdr.Play && (cdr.Mode & MODE_CDDA) && !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
1011                                         // update subq
1012                                         ReadTrack( cdr.SetSectorPlay );
1013
1014                                 cdr.Result[0] = subq->TrackNumber;
1015                                 cdr.Result[1] = subq->IndexNumber;
1016                                 memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3);
1017                                 memcpy(cdr.Result + 5, subq->AbsoluteAddress, 3);
1018
1019
1020                                 // subQ integrity check - data only (skip audio)
1021                                 if( subq->TrackNumber == 1 && stat.Type == 0x01 ) {
1022                                 if (calcCrc((u8 *)subq + 12, 10) != (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
1023                                         memset(cdr.Result + 2, 0, 3 + 3); // CRC wrong, wipe out time data
1024                                 }
1025                                 }
1026                         } else {
1027                                 if( cdr.Play == FALSE || !(cdr.Mode & MODE_CDDA) || !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
1028                                         Create_Fake_Subq();
1029
1030
1031                                 // track # / index #
1032                                 cdr.Result[0] = itob(cdr.CurTrack);
1033                                 cdr.Result[1] = itob(fake_subq_index);
1034
1035                                 // local
1036                                 cdr.Result[2] = itob( fake_subq_local[0] );
1037                                 cdr.Result[3] = itob( fake_subq_local[1] );
1038                                 cdr.Result[4] = itob( fake_subq_local[2] );
1039
1040                                 // absolute
1041                                 cdr.Result[5] = itob( fake_subq_real[0] );
1042                                 cdr.Result[6] = itob( fake_subq_real[1] );
1043                                 cdr.Result[7] = itob( fake_subq_real[2] );
1044                         }
1045
1046                         // redump.org - wipe time
1047                         if( !cdr.Play && CheckSBI(cdr.Result+5) ) {
1048                                 memset( cdr.Result+2, 0, 6 );
1049                         }
1050
1051                         cdr.Stat = Acknowledge;
1052                         break;
1053
1054                 case CdlGetTN:
1055                         // 5-Star Racing: don't stop CDDA
1056                         //
1057                         // Vib Ribbon: CD swap
1058                         StopReading();
1059
1060                         cdr.CmdProcess = 0;
1061                         SetResultSize(3);
1062                         cdr.StatP |= STATUS_ROTATING;
1063                         cdr.Result[0] = cdr.StatP;
1064                         if (CDR_getTN(cdr.ResultTN) == -1) {
1065                                 cdr.Stat = DiskError;
1066                                 cdr.Result[0] |= STATUS_ERROR;
1067                         } else {
1068                                 cdr.Stat = Acknowledge;
1069                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
1070                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
1071                         }
1072                         break;
1073
1074                 case CdlGetTD:
1075                         cdr.CmdProcess = 0;
1076                         cdr.Track = btoi(cdr.Param[0]);
1077                         SetResultSize(4);
1078                         cdr.StatP |= STATUS_ROTATING;
1079                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
1080                                 cdr.Stat = DiskError;
1081                                 cdr.Result[0] |= STATUS_ERROR;
1082                         } else {
1083                                 cdr.Stat = Acknowledge;
1084                                 cdr.Result[0] = cdr.StatP;
1085                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
1086                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
1087                                 cdr.Result[3] = itob(cdr.ResultTD[0]);
1088                         }
1089                         break;
1090
1091                 case CdlSeekL:
1092                 case CdlSeekP:
1093                         SetResultSize(1);
1094                         cdr.StatP |= STATUS_ROTATING;
1095                         cdr.Result[0] = cdr.StatP;
1096                         cdr.StatP |= STATUS_SEEK;
1097                         cdr.Stat = Acknowledge;
1098
1099                         /*
1100                         Crusaders of Might and Magic = 0.5x-4x
1101                         - fix cutscene speech start
1102
1103                         Eggs of Steel = 2x-?
1104                         - fix new game
1105
1106                         Medievil = ?-4x
1107                         - fix cutscene speech
1108
1109                         Rockman X5 = 0.5-4x
1110                         - fix capcom logo
1111                         */
1112                         CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
1113                         cdr.Seeked = SEEK_DOING_CMD;
1114                         break;
1115
1116                 case CdlTest:
1117                         cdr.Stat = Acknowledge;
1118                         switch (cdr.Param[0]) {
1119                                 case 0x20: // System Controller ROM Version
1120                                         SetResultSize(4);
1121                                         memcpy(cdr.Result, Test20, 4);
1122                                         break;
1123                                 case 0x22:
1124                                         SetResultSize(8);
1125                                         memcpy(cdr.Result, Test22, 4);
1126                                         break;
1127                                 case 0x23: case 0x24:
1128                                         SetResultSize(8);
1129                                         memcpy(cdr.Result, Test23, 4);
1130                                         break;
1131                         }
1132                         break;
1133
1134                 case CdlID:
1135                         SetResultSize(1);
1136                         cdr.StatP |= STATUS_ROTATING;
1137                         cdr.Result[0] = cdr.StatP;
1138                         cdr.Stat = Acknowledge;
1139                         AddIrqQueue(CdlID + 0x20, 0x800);
1140                         break;
1141
1142                 case CdlID + 0x20:
1143                         SetResultSize(8);
1144
1145                         if (CDR_getStatus(&stat) == -1) {
1146                                 cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player
1147                                 cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD
1148                         }
1149                         else {
1150                                 if (stat.Type == 2) {
1151                                         // Music CD
1152                                         cdr.Result[0] = 0x08;
1153                                         cdr.Result[1] = 0x10;
1154
1155                                         cdr.Result[1] |= 0x80;
1156                                 }
1157                                 else {
1158                                         // Data CD
1159                                         if (CdromId[0] == '\0') {
1160                                                 cdr.Result[0] = 0x00;
1161                                                 cdr.Result[1] = 0x80;
1162                                         }
1163                                         else {
1164                                                 cdr.Result[0] = 0x08;
1165                                                 cdr.Result[1] = 0x00;
1166                                         }
1167                                 }
1168                         }
1169
1170                         cdr.Result[2] = 0x00;
1171                         cdr.Result[3] = 0x00;
1172                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
1173                         cdr.Stat = Complete;
1174                         break;
1175
1176                 case CdlReset:
1177                         SetResultSize(1);
1178                         cdr.StatP = STATUS_ROTATING;
1179                         cdr.Result[0] = cdr.StatP;
1180                         cdr.Stat = Acknowledge;
1181                         break;
1182
1183                 case CdlReadT:
1184                         SetResultSize(1);
1185                         cdr.StatP |= STATUS_ROTATING;
1186                         cdr.Result[0] = cdr.StatP;
1187                         cdr.Stat = Acknowledge;
1188                         AddIrqQueue(CdlReadT + 0x20, 0x800);
1189                         break;
1190
1191                 case CdlReadT + 0x20:
1192                         SetResultSize(1);
1193                         cdr.StatP |= STATUS_ROTATING;
1194                         cdr.Result[0] = cdr.StatP;
1195                         cdr.Stat = Complete;
1196                         break;
1197
1198                 case CdlReadToc:
1199                         SetResultSize(1);
1200                         cdr.StatP |= STATUS_ROTATING;
1201                         cdr.Result[0] = cdr.StatP;
1202                         cdr.Stat = Acknowledge;
1203                         AddIrqQueue(CdlReadToc + 0x20, 0x800);
1204                         break;
1205
1206                 case CdlReadToc + 0x20:
1207                         SetResultSize(1);
1208                         cdr.StatP |= STATUS_ROTATING;
1209                         cdr.Result[0] = cdr.StatP;
1210                         cdr.Stat = Complete;
1211                         break;
1212
1213                 case AUTOPAUSE:
1214                         cdr.OCUP = 0;
1215 /*                      SetResultSize(1);
1216                         StopCdda();
1217                         StopReading();
1218                         cdr.OCUP = 0;
1219                         cdr.StatP&=~0x20;
1220                         cdr.StatP|= 0x2;
1221                         cdr.Result[0] = cdr.StatP;
1222                         cdr.Stat = DataEnd;
1223 */                      AddIrqQueue(CdlPause, 0x800);
1224                         break;
1225
1226                 case READ_ACK:
1227                         if (!cdr.Reading) return;
1228
1229
1230                         // Fighting Force 2 - update subq time immediately
1231                         // - fixes new game
1232                         ReadTrack( cdr.SetSector );
1233
1234
1235                         // Crusaders of Might and Magic - update getlocl now
1236                         // - fixes cutscene speech
1237                         {
1238                                 u8 *buf = CDR_getBuffer();
1239                                 if (buf != NULL)
1240                                         memcpy(cdr.Transfer, buf, 8);
1241                         }
1242
1243                         /*
1244                         Duke Nukem: Land of the Babes - seek then delay read for one frame
1245                         - fixes cutscenes
1246                         C-12 - Final Resistance - doesn't like seek
1247                         */
1248
1249                         if (cdr.Seeked != SEEK_DONE) {
1250                                 cdr.StatP |= STATUS_SEEK;
1251                                 cdr.StatP &= ~STATUS_READ;
1252
1253                                 // Crusaders of Might and Magic - use short time
1254                                 // - fix cutscene speech (startup)
1255
1256                                 // ??? - use more accurate seek time later
1257                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1258                         } else {
1259                                 cdr.StatP |= STATUS_READ;
1260                                 cdr.StatP &= ~STATUS_SEEK;
1261
1262                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1263                         }
1264
1265                         SetResultSize(1);
1266                         cdr.StatP |= STATUS_ROTATING;
1267                         cdr.Result[0] = cdr.StatP;
1268                         cdr.Stat = Acknowledge;
1269                         break;
1270
1271                 case 0xff:
1272                         return;
1273
1274                 default:
1275                         cdr.Stat = Complete;
1276                         break;
1277         }
1278
1279         Check_Shell( Irq );
1280
1281         cdr.ParamP = 0;
1282         cdr.ParamC = 0;
1283
1284         if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) {
1285                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1286         }
1287
1288 #ifdef CDR_LOG
1289         printf("cdrInterrupt() Log: CDR Interrupt IRQ %x: ", Irq);
1290         for (i = 0; i < cdr.ResultC; i++)
1291                 printf("%02x ", cdr.Result[i]);
1292         printf("\n");
1293 #endif
1294 }
1295
1296 void cdrReadInterrupt() {
1297         u8 *buf;
1298
1299         if (!cdr.Reading)
1300                 return;
1301
1302         if (cdr.Irq || cdr.Stat) {
1303                 SysPrintf("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1304                 CDREAD_INT(0x1000);
1305                 return;
1306         }
1307
1308 #ifdef CDR_LOG
1309         CDR_LOG("cdrReadInterrupt() Log: KEY END");
1310 #endif
1311
1312         cdr.OCUP = 1;
1313         SetResultSize(1);
1314         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1315         cdr.StatP &= ~STATUS_SEEK;
1316         cdr.Result[0] = cdr.StatP;
1317         cdr.Seeked = SEEK_DONE;
1318
1319         ReadTrack( cdr.SetSector );
1320
1321         buf = CDR_getBuffer();
1322         if (buf == NULL)
1323                 cdr.RErr = -1;
1324
1325         if (cdr.RErr == -1) {
1326 #ifdef CDR_LOG
1327                 fprintf(emuLog, "cdrReadInterrupt() Log: err\n");
1328 #endif
1329                 memset(cdr.Transfer, 0, DATA_SIZE);
1330                 cdr.Stat = DiskError;
1331                 cdr.Result[0] |= STATUS_ERROR;
1332                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1333                 return;
1334         }
1335
1336         memcpy(cdr.Transfer, buf, DATA_SIZE);
1337         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1338
1339
1340 #ifdef CDR_LOG
1341         fprintf(emuLog, "cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1342 #endif
1343
1344         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1345                 // Firemen 2: Multi-XA files - briefings, cutscenes
1346                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1347                         cdr.File = cdr.Transfer[4 + 0];
1348                         cdr.Channel = cdr.Transfer[4 + 1];
1349                 }
1350
1351                 if ((cdr.Transfer[4 + 2] & 0x4) &&
1352                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1353                         (cdr.Transfer[4 + 0] == cdr.File)) {
1354                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1355
1356                         if (!ret) {
1357                                 // only handle attenuator basic channel switch for now
1358                                 if (cdr.Xa.stereo) {
1359                                         int i;
1360                                         if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1361                                             && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1362                                         {
1363                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1364                                                         cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2];
1365                                         }
1366                                         else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1367                                             && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1368                                         {
1369                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1370                                                         cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1];
1371                                         }
1372                                 }
1373
1374                                 SPU_playADPCMchannel(&cdr.Xa);
1375                                 cdr.FirstSector = 0;
1376
1377 #if 0
1378                                 // Crash Team Racing: music, speech
1379                                 // - done using cdda decoded buffer (spu irq)
1380                                 // - don't do here
1381
1382                                 // signal ADPCM data ready
1383                                 psxHu32ref(0x1070) |= SWAP32((u32)0x200);
1384 #endif
1385                         }
1386                         else cdr.FirstSector = -1;
1387                 }
1388         }
1389
1390         cdr.SetSector[2]++;
1391         if (cdr.SetSector[2] == 75) {
1392                 cdr.SetSector[2] = 0;
1393                 cdr.SetSector[1]++;
1394                 if (cdr.SetSector[1] == 60) {
1395                         cdr.SetSector[1] = 0;
1396                         cdr.SetSector[0]++;
1397                 }
1398         }
1399
1400         cdr.Readed = 0;
1401
1402         // G-Police: Don't autopause ADPCM even if mode set (music)
1403         if ((cdr.Transfer[4 + 2] & 0x80) && (cdr.Mode & MODE_AUTOPAUSE) &&
1404                         (cdr.Transfer[4 + 2] & 0x4) != 0x4 ) { // EOF
1405 #ifdef CDR_LOG
1406                 CDR_LOG("cdrReadInterrupt() Log: Autopausing read\n");
1407 #endif
1408 //              AddIrqQueue(AUTOPAUSE, 0x2000);
1409                 AddIrqQueue(CdlPause, 0x2000);
1410         }
1411         else {
1412                 CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1413         }
1414
1415         /*
1416         Croc 2: $40 - only FORM1 (*)
1417         Judge Dredd: $C8 - only FORM1 (*)
1418         Sim Theme Park - no adpcm at all (zero)
1419         */
1420
1421         if( (cdr.Mode & MODE_STRSND) == 0 || (cdr.Transfer[4+2] & 0x4) != 0x4 ) {
1422                 cdr.Stat = DataReady;
1423         } else {
1424                 // Breath of Fire 3 - fix inn sleeping
1425                 // Rockman X5 - no music restart problem
1426                 cdr.Stat = NoIntr;
1427         }
1428         psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1429
1430         Check_Shell(0);
1431 }
1432
1433 /*
1434 cdrRead0:
1435         bit 0 - 0 REG1 command send / 1 REG1 data read
1436         bit 1 - 0 data transfer finish / 1 data transfer ready/in progress
1437         bit 2 - unknown
1438         bit 3 - unknown
1439         bit 4 - unknown
1440         bit 5 - 1 result ready
1441         bit 6 - 1 dma ready
1442         bit 7 - 1 command being processed
1443 */
1444
1445 unsigned char cdrRead0(void) {
1446         if (cdr.ResultReady)
1447                 cdr.Ctrl |= 0x20;
1448         else
1449                 cdr.Ctrl &= ~0x20;
1450
1451         if (cdr.OCUP)
1452                 cdr.Ctrl |= 0x40;
1453 //  else
1454 //              cdr.Ctrl &= ~0x40;
1455
1456         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1457         cdr.Ctrl |= 0x18;
1458
1459 #ifdef CDR_LOG
1460         CDR_LOG("cdrRead0() Log: CD0 Read: %x\n", cdr.Ctrl);
1461 #endif
1462
1463         return psxHu8(0x1800) = cdr.Ctrl;
1464 }
1465
1466 /*
1467 cdrWrite0:
1468         0 - to send a command / 1 - to get the result
1469 */
1470
1471 void cdrWrite0(unsigned char rt) {
1472 #ifdef CDR_LOG
1473         CDR_LOG("cdrWrite0() Log: CD0 write: %x\n", rt);
1474 #endif
1475         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1476 }
1477
1478 unsigned char cdrRead1(void) {
1479     if (cdr.ResultReady) { // && cdr.Ctrl & 0x1) {
1480                 // GameShark CDX CD Player: uses 17 bytes output (wraps around)
1481                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1482                 cdr.ResultP++;
1483                 if (cdr.ResultP == cdr.ResultC)
1484                         cdr.ResultReady = 0;
1485         } else {
1486                 psxHu8(0x1801) = 0;
1487         }
1488 #ifdef CDR_LOG
1489         CDR_LOG("cdrRead1() Log: CD1 Read: %x\n", psxHu8(0x1801));
1490 #endif
1491         return psxHu8(0x1801);
1492 }
1493
1494 void cdrWrite1(unsigned char rt) {
1495         u8 set_loc[3];
1496         int i;
1497
1498 #ifdef CDR_LOG
1499         CDR_LOG("cdrWrite1() Log: CD1 write: %x (%s)\n", rt, CmdName[rt]);
1500 #endif
1501
1502
1503         // Tekken: CDXA fade-out
1504         if( (cdr.Ctrl & 3) == 3 ) {
1505                 cdr.AttenuatorRight[0] = rt;
1506         }
1507
1508
1509 //      psxHu8(0x1801) = rt;
1510         cdr.Cmd = rt;
1511         cdr.OCUP = 0;
1512
1513 #ifdef CDRCMD_DEBUG
1514         SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]);
1515         if (cdr.ParamC) {
1516                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1517                 for (i = 0; i < cdr.ParamC; i++)
1518                         SysPrintf(" %x,", cdr.Param[i]);
1519                 SysPrintf("}\n");
1520         } else {
1521                 SysPrintf("\n");
1522         }
1523 #endif
1524
1525         if (cdr.Ctrl & 0x3) return;
1526
1527         cdr.ResultReady = 0;
1528
1529         switch (cdr.Cmd) {
1530         case CdlSync:
1531                 cdr.Ctrl |= 0x80;
1532                 cdr.Stat = NoIntr; 
1533                 AddIrqQueue(cdr.Cmd, 0x800);
1534                 break;
1535
1536         case CdlNop:
1537                 cdr.Ctrl |= 0x80;
1538                 cdr.Stat = NoIntr; 
1539
1540                 // Twisted Metal 3 - fix music
1541                 AddIrqQueue(cdr.Cmd, 0x800);
1542                 break;
1543
1544         case CdlSetloc:
1545                 StopReading();
1546                 for (i = 0; i < 3; i++)
1547                         set_loc[i] = btoi(cdr.Param[i]);
1548
1549                 i = abs(msf2sec(cdr.SetSector) - msf2sec(set_loc));
1550                 if (i > 16)
1551                        cdr.Seeked = SEEK_PENDING;
1552
1553                 memcpy(cdr.SetSector, set_loc, 3);
1554                 cdr.SetSector[3] = 0;
1555
1556                 cdr.Ctrl |= 0x80;
1557                 cdr.Stat = NoIntr;
1558                 AddIrqQueue(cdr.Cmd, 0x800);
1559                 break;
1560
1561         case CdlPlay:
1562                 // Vib Ribbon: try same track again
1563                 StopCdda();
1564
1565                 if (!cdr.SetSector[0] & !cdr.SetSector[1] & !cdr.SetSector[2]) {
1566                         if (CDR_getTN(cdr.ResultTN) != -1) {
1567                                 if (cdr.CurTrack > cdr.ResultTN[1])
1568                                         cdr.CurTrack = cdr.ResultTN[1];
1569                                 if (CDR_getTD((unsigned char)(cdr.CurTrack), cdr.ResultTD) != -1) {
1570                                         int tmp = cdr.ResultTD[2];
1571                                         cdr.ResultTD[2] = cdr.ResultTD[0];
1572                                         cdr.ResultTD[0] = tmp;
1573                                         if (!Config.Cdda) CDR_play(cdr.ResultTD);
1574                                 }
1575                         }
1576                 } else if (!Config.Cdda) {
1577                         CDR_play(cdr.SetSector);
1578                 }
1579
1580                 // Vib Ribbon - decoded buffer IRQ for CDDA reading
1581                 // - fixes ribbon timing + music CD mode
1582                 //TODO?
1583                 //CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
1584
1585
1586                 cdr.Play = TRUE;
1587
1588                 cdr.StatP |= STATUS_SEEK;
1589                 cdr.StatP &= ~STATUS_ROTATING;
1590
1591                 cdr.Ctrl |= 0x80;
1592                 cdr.Stat = NoIntr; 
1593                 AddIrqQueue(cdr.Cmd, 0x800);
1594                 break;
1595
1596         case CdlForward:
1597                 //if (cdr.CurTrack < 0xaa)
1598                 //      cdr.CurTrack++;
1599                 cdr.Ctrl |= 0x80;
1600                 cdr.Stat = NoIntr; 
1601                 AddIrqQueue(cdr.Cmd, 0x800);
1602                 break;
1603
1604         case CdlBackward:
1605                 //if (cdr.CurTrack > 1)
1606                 //cdr.CurTrack--;
1607                 cdr.Ctrl |= 0x80;
1608                 cdr.Stat = NoIntr; 
1609                 AddIrqQueue(cdr.Cmd, 0x800);
1610                 break;
1611
1612         case CdlReadN:
1613                 cdr.Irq = 0;
1614                 StopReading();
1615                 cdr.Ctrl|= 0x80;
1616                 cdr.Stat = NoIntr; 
1617                 StartReading(1, 0x800);
1618                 break;
1619
1620         case CdlStandby:
1621                 StopCdda();
1622                 StopReading();
1623                 cdr.Ctrl |= 0x80;
1624                 cdr.Stat = NoIntr;
1625                 AddIrqQueue(cdr.Cmd, 0x800);
1626                 break;
1627
1628         case CdlStop:
1629                 // GameShark CD Player: Reset CDDA to track start
1630                 if( cdr.Play && CDR_getStatus(&stat) != -1 ) {
1631                         cdr.SetSectorPlay[0] = stat.Time[0];
1632                         cdr.SetSectorPlay[1] = stat.Time[1];
1633                         cdr.SetSectorPlay[2] = stat.Time[2];
1634
1635                         Find_CurTrack();
1636
1637
1638                         // grab time for current track
1639                         CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
1640
1641                         cdr.SetSectorPlay[0] = cdr.ResultTD[2];
1642                         cdr.SetSectorPlay[1] = cdr.ResultTD[1];
1643                         cdr.SetSectorPlay[2] = cdr.ResultTD[0];
1644                 }
1645
1646                 StopCdda();
1647                 StopReading();
1648
1649                 cdr.Ctrl |= 0x80;
1650                 cdr.Stat = NoIntr;
1651                 AddIrqQueue(cdr.Cmd, 0x800);
1652                 break;
1653
1654         case CdlPause:
1655                 /*
1656                    GameShark CD Player: save time for resume
1657
1658                    Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors
1659                 */
1660
1661                 StopCdda();
1662                 StopReading();
1663                 cdr.Ctrl |= 0x80;
1664                 cdr.Stat = NoIntr;
1665
1666                 AddIrqQueue(cdr.Cmd, 0x800);
1667                 break;
1668
1669         case CdlReset:
1670         case CdlInit:
1671                 cdr.Seeked = SEEK_DONE;
1672                 StopCdda();
1673                 StopReading();
1674                 cdr.Ctrl |= 0x80;
1675                 cdr.Stat = NoIntr; 
1676                 AddIrqQueue(cdr.Cmd, 0x800);
1677                 break;
1678
1679         case CdlMute:
1680                 cdr.Muted = TRUE;
1681                 cdr.Ctrl |= 0x80;
1682                 cdr.Stat = NoIntr; 
1683                 AddIrqQueue(cdr.Cmd, 0x800);
1684
1685                         // Duke Nukem - Time to Kill
1686                         // - do not directly set cd-xa volume
1687                         //SPU_writeRegister( H_CDLeft, 0x0000 );
1688                         //SPU_writeRegister( H_CDRight, 0x0000 );
1689                 break;
1690
1691         case CdlDemute:
1692                 cdr.Muted = FALSE;
1693                 cdr.Ctrl |= 0x80;
1694                 cdr.Stat = NoIntr; 
1695                 AddIrqQueue(cdr.Cmd, 0x800);
1696
1697                         // Duke Nukem - Time to Kill
1698                         // - do not directly set cd-xa volume
1699                         //SPU_writeRegister( H_CDLeft, 0x7f00 );
1700                         //SPU_writeRegister( H_CDRight, 0x7f00 );
1701                 break;
1702
1703         case CdlSetfilter:
1704                 cdr.File = cdr.Param[0];
1705                 cdr.Channel = cdr.Param[1];
1706                 cdr.Ctrl |= 0x80;
1707                 cdr.Stat = NoIntr; 
1708                 AddIrqQueue(cdr.Cmd, 0x800);
1709                 break;
1710
1711         case CdlSetmode:
1712 #ifdef CDR_LOG
1713                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1714 #endif 
1715                 cdr.Mode = cdr.Param[0];
1716                 cdr.Ctrl |= 0x80;
1717                 cdr.Stat = NoIntr; 
1718                 AddIrqQueue(cdr.Cmd, 0x800);
1719
1720                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1721                 // - fixes choppy movie sound
1722                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1723                         StopCdda();
1724                 break;
1725
1726         case CdlGetmode:
1727                 cdr.Ctrl |= 0x80;
1728                 cdr.Stat = NoIntr; 
1729                 AddIrqQueue(cdr.Cmd, 0x800);
1730                 break;
1731
1732         case CdlGetlocL:
1733                 cdr.Ctrl |= 0x80;
1734                 cdr.Stat = NoIntr; 
1735
1736                 // Crusaders of Might and Magic - cutscene speech
1737                 AddIrqQueue(cdr.Cmd, 0x800);
1738                 break;
1739
1740         case CdlGetlocP:
1741                 cdr.Ctrl |= 0x80;
1742                 cdr.Stat = NoIntr; 
1743
1744                 // GameShark CDX / Lite Player: pretty narrow time window
1745                 // - doesn't always work due to time inprecision
1746                 //AddIrqQueue(cdr.Cmd, 0x28);
1747
1748                 // Tomb Raider 2 - cdda
1749                 //AddIrqQueue(cdr.Cmd, 0x40);
1750
1751                 // rearmed: the above works in pcsxr-svn, but breaks here
1752                 // (TOCA world touring cars), perhaps some other code is not merged yet
1753                 AddIrqQueue(cdr.Cmd, 0x1000);
1754                 break;
1755
1756         case CdlGetTN:
1757                 cdr.Ctrl |= 0x80;
1758                 cdr.Stat = NoIntr; 
1759                 //AddIrqQueue(cdr.Cmd, 0x800);
1760
1761                 // GameShark CDX CD Player: very long time
1762                 AddIrqQueue(cdr.Cmd, 0x100000);
1763                 break;
1764
1765         case CdlGetTD:
1766                 cdr.Ctrl |= 0x80;
1767                 cdr.Stat = NoIntr; 
1768                 AddIrqQueue(cdr.Cmd, 0x800);
1769                 break;
1770
1771         case CdlSeekL:
1772 //                      ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0];
1773                 cdr.Ctrl |= 0x80;
1774                 cdr.Stat = NoIntr; 
1775                 AddIrqQueue(cdr.Cmd, 0x800);
1776
1777                 StopCdda();
1778                 StopReading();
1779
1780                 break;
1781
1782         case CdlSeekP:
1783 //              ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0];
1784                 cdr.Ctrl |= 0x80;
1785                 cdr.Stat = NoIntr; 
1786
1787                 // Tomb Raider 2 - reset cdda
1788                 StopCdda();
1789                 StopReading();
1790
1791                 AddIrqQueue(cdr.Cmd, 0x800);
1792                 break;
1793
1794         // Destruction Derby: read TOC? GetTD after this
1795         case CdlReadT:
1796                 cdr.Ctrl |= 0x80;
1797                 cdr.Stat = NoIntr;
1798                 AddIrqQueue(cdr.Cmd, 0x800);
1799                 break;
1800
1801         case CdlTest:
1802                 cdr.Ctrl |= 0x80;
1803                 cdr.Stat = NoIntr; 
1804                 AddIrqQueue(cdr.Cmd, 0x800);
1805                 break;
1806
1807         case CdlID:
1808                 cdr.Ctrl |= 0x80;
1809                 cdr.Stat = NoIntr; 
1810                 AddIrqQueue(cdr.Cmd, 0x800);
1811                 break;
1812
1813         case CdlReadS:
1814                 cdr.Irq = 0;
1815                 StopReading();
1816                 cdr.Ctrl |= 0x80;
1817                 cdr.Stat = NoIntr; 
1818                 StartReading(2, 0x800);
1819                 break;
1820
1821         case CdlReadToc:
1822                 cdr.Ctrl |= 0x80;
1823                 cdr.Stat = NoIntr; 
1824                 AddIrqQueue(cdr.Cmd, 0x800);
1825                 break;
1826
1827         default:
1828                 cdr.ParamP = 0;
1829                 cdr.ParamC = 0;
1830 #ifdef CDR_LOG
1831                 CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd);
1832 #endif
1833                 return;
1834         }
1835         if (cdr.Stat != NoIntr) {
1836                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
1837         }
1838 }
1839
1840 unsigned char cdrRead2(void) {
1841         unsigned char ret;
1842
1843         if (cdr.Readed == 0) {
1844                 ret = 0;
1845         } else {
1846                 ret = *pTransfer++;
1847         }
1848
1849 #ifdef CDR_LOG
1850         CDR_LOG("cdrRead2() Log: CD2 Read: %x\n", ret);
1851 #endif
1852         return ret;
1853 }
1854
1855 void cdrWrite2(unsigned char rt) {
1856 #ifdef CDR_LOG
1857         CDR_LOG("cdrWrite2() Log: CD2 write: %x\n", rt);
1858 #endif
1859
1860         // Tekken: CDXA fade-out
1861         if( (cdr.Ctrl & 3) == 2 ) {
1862                 cdr.AttenuatorLeft[0] = rt;
1863         }
1864         else if( (cdr.Ctrl & 3) == 3 ) {
1865                 cdr.AttenuatorRight[1] = rt;
1866         }
1867
1868
1869         if (cdr.Ctrl & 0x1) {
1870                 switch (rt) {
1871                         case 0x07:
1872                                 cdr.ParamP = 0;
1873                                 cdr.ParamC = 0;
1874                                 cdr.ResultReady = 1; //0;
1875                                 cdr.Ctrl &= ~3; //cdr.Ctrl = 0;
1876                                 break;
1877
1878                         default:
1879                                 cdr.Reg2 = rt;
1880                                 break;
1881                 }
1882         } else if (!(cdr.Ctrl & 0x3) && cdr.ParamP < 8) {
1883                 cdr.Param[cdr.ParamP++] = rt;
1884                 cdr.ParamC++;
1885         }
1886 }
1887
1888 unsigned char cdrRead3(void) {
1889         if (cdr.Stat) {
1890                 if (cdr.Ctrl & 0x1)
1891                         psxHu8(0x1803) = cdr.Stat | 0xE0;
1892                 else
1893                         psxHu8(0x1803) = 0xff;
1894         } else {
1895                 psxHu8(0x1803) = 0;
1896         }
1897 #ifdef CDR_LOG
1898         CDR_LOG("cdrRead3() Log: CD3 Read: %x\n", psxHu8(0x1803));
1899 #endif
1900         return psxHu8(0x1803);
1901 }
1902
1903 void cdrWrite3(unsigned char rt) {
1904 #ifdef CDR_LOG
1905         CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt);
1906 #endif
1907
1908         // Tekken: CDXA fade-out
1909         if( (cdr.Ctrl & 3) == 2 ) {
1910                 cdr.AttenuatorLeft[1] = rt;
1911         }
1912         else if( (cdr.Ctrl & 3) == 3 && rt == 0x20 ) {
1913 #ifdef CDR_LOG
1914                 CDR_LOG( "CD-XA Volume: %X %X | %X %X\n",
1915                         cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1],
1916                         cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
1917 #endif
1918         }
1919
1920
1921         // GameShark CDX CD Player: Irq timing mania
1922         if( rt == 0 &&
1923                         cdr.Irq != 0 && cdr.Irq != 0xff &&
1924                         cdr.ResultReady == 0 ) {
1925
1926                 // GS CDX: ~0x28 cycle timing - way too precise
1927                 if( cdr.Irq == CdlGetlocP ) {
1928                         cdrInterrupt();
1929
1930                         psxRegs.interrupt &= ~(1 << PSXINT_CDR);
1931                 }
1932         }
1933
1934
1935         if (rt == 0x07 && (cdr.Ctrl & 3) == 1) {
1936                 cdr.Stat = 0;
1937
1938                 if (cdr.Irq == 0xff) {
1939                         cdr.Irq = 0;
1940                         return;
1941                 }
1942
1943                 // XA streaming - incorrect timing because of this reschedule
1944                 // - Final Fantasy Tactics
1945                 // - various other games
1946
1947                 if (cdr.Reading && !cdr.ResultReady) {
1948                         int left = psxRegs.intCycle[PSXINT_CDREAD].sCycle + psxRegs.intCycle[PSXINT_CDREAD].cycle - psxRegs.cycle;
1949                         int time = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
1950                         if (Config.CdrReschedule != 2)
1951                         if (left < time / 2 || Config.CdrReschedule) { // rearmed guesswork hack
1952                                 //printf("-- resched %d -> %d\n", left, time);
1953                                 CDREAD_INT(time);
1954                         }
1955                 }
1956
1957                 return;
1958         }
1959
1960         if (rt == 0x80 && !(cdr.Ctrl & 0x3) && cdr.Readed == 0) {
1961                 cdr.Readed = 1;
1962                 pTransfer = cdr.Transfer;
1963
1964                 switch (cdr.Mode & 0x30) {
1965                         case MODE_SIZE_2328:
1966                         case 0x00:
1967                                 pTransfer += 12;
1968                                 break;
1969
1970                         case MODE_SIZE_2340:
1971                                 pTransfer += 0;
1972                                 break;
1973
1974                         default:
1975                                 break;
1976                 }
1977         }
1978 }
1979
1980 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1981         u32 cdsize;
1982         int size;
1983         u8 *ptr;
1984
1985 #ifdef CDR_LOG
1986         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1987 #endif
1988
1989         switch (chcr) {
1990                 case 0x11000000:
1991                 case 0x11400100:
1992                         if (cdr.Readed == 0) {
1993 #ifdef CDR_LOG
1994                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1995 #endif
1996                                 break;
1997                         }
1998
1999                         cdsize = (bcr & 0xffff) * 4;
2000
2001                         // Ape Escape: bcr = 0001 / 0000
2002                         // - fix boot
2003                         if( cdsize == 0 )
2004                         {
2005                                 switch (cdr.Mode & 0x30) {
2006                                         case 0x00: cdsize = 2048; break;
2007                                         case MODE_SIZE_2328: cdsize = 2328; break;
2008                                         case MODE_SIZE_2340: cdsize = 2340; break;
2009                                 }
2010                         }
2011
2012
2013                         ptr = (u8 *)PSXM(madr);
2014                         if (ptr == NULL) {
2015 #ifdef CPU_LOG
2016                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
2017 #endif
2018                                 break;
2019                         }
2020
2021                         /*
2022                         GS CDX: Enhancement CD crash
2023                         - Setloc 0:0:0
2024                         - CdlPlay
2025                         - Spams DMA3 and gets buffer overrun
2026                         */
2027                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
2028                         if (size > cdsize)
2029                                 size = cdsize;
2030                         if (size > 0)
2031                         {
2032                                 memcpy(ptr, pTransfer, size);
2033                         }
2034
2035                         psxCpu->Clear(madr, cdsize / 4);
2036                         pTransfer += cdsize;
2037
2038
2039                         // burst vs normal
2040                         if( chcr == 0x11400100 ) {
2041                                 CDRDMA_INT( (cdsize/4) / 4 );
2042                         }
2043                         else if( chcr == 0x11000000 ) {
2044                                 CDRDMA_INT( (cdsize/4) * 1 );
2045                         }
2046                         return;
2047
2048                 default:
2049 #ifdef CDR_LOG
2050                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
2051 #endif
2052                         break;
2053         }
2054
2055         HW_DMA3_CHCR &= SWAP32(~0x01000000);
2056         DMA_INTERRUPT(3);
2057 }
2058
2059 void cdrDmaInterrupt()
2060 {
2061         if (HW_DMA3_CHCR & SWAP32(0x01000000))
2062         {
2063                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
2064                 DMA_INTERRUPT(3);
2065         }
2066 }
2067
2068 void cdrReset() {
2069         memset(&cdr, 0, sizeof(cdr));
2070         cdr.CurTrack = 1;
2071         cdr.File = 1;
2072         cdr.Channel = 1;
2073         pTransfer = cdr.Transfer;
2074
2075         // BIOS player - default values
2076         cdr.AttenuatorLeft[0] = 0x80;
2077         cdr.AttenuatorLeft[1] = 0x00;
2078         cdr.AttenuatorRight[0] = 0x80;
2079         cdr.AttenuatorRight[1] = 0x00;
2080 }
2081
2082 int cdrFreeze(void *f, int Mode) {
2083         u32 tmp;
2084
2085         if( Mode == 0 ) {
2086                 StopCdda();
2087         }
2088         
2089         gzfreeze(&cdr, sizeof(cdr));
2090         
2091         if (Mode == 1)
2092                 tmp = pTransfer - cdr.Transfer;
2093
2094         gzfreeze(&tmp, sizeof(tmp));
2095
2096         if (Mode == 0) {
2097                 pTransfer = cdr.Transfer + tmp;
2098
2099                 if (cdr.Play && !Config.Cdda)
2100                         CDR_play(cdr.SetSectorPlay);
2101         }
2102
2103         return 0;
2104 }
2105
2106 void LidInterrupt() {
2107         cdr.LidCheck = 0x20; // start checker
2108
2109         CDRLID_INT( cdReadTime * 3 );
2110         
2111         // generate interrupt if none active - open or close
2112         if (cdr.Irq == 0 || cdr.Irq == 0xff) {
2113                 cdr.Ctrl |= 0x80;
2114                 cdr.Stat = NoIntr;
2115                 AddIrqQueue(CdlNop, 0x800);
2116         }
2117 }