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