fdb38ba9dc312ca05157ce9b47626aae843f21a7
[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 /* logging */
29 #if 0
30 #define CDR_LOG SysPrintf
31 #else
32 #define CDR_LOG(...)
33 #endif
34 #if 0
35 #define CDR_LOG_I SysPrintf
36 #else
37 #define CDR_LOG_I(...)
38 #endif
39 #if 0
40 #define CDR_LOG_IO SysPrintf
41 #else
42 #define CDR_LOG_IO(...)
43 #endif
44 //#define CDR_LOG_CMD_IRQ
45
46 cdrStruct cdr;
47 static unsigned char *pTransfer;
48 static int subq_broken;
49
50 /* CD-ROM magic numbers */
51 #define CdlSync        0
52 #define CdlNop         1
53 #define CdlSetloc      2
54 #define CdlPlay        3
55 #define CdlForward     4
56 #define CdlBackward    5
57 #define CdlReadN       6
58 #define CdlStandby     7
59 #define CdlStop        8
60 #define CdlPause       9
61 #define CdlInit        10
62 #define CdlMute        11
63 #define CdlDemute      12
64 #define CdlSetfilter   13
65 #define CdlSetmode     14
66 #define CdlGetmode     15
67 #define CdlGetlocL     16
68 #define CdlGetlocP     17
69 #define CdlReadT       18
70 #define CdlGetTN       19
71 #define CdlGetTD       20
72 #define CdlSeekL       21
73 #define CdlSeekP       22
74 #define CdlSetclock    23
75 #define CdlGetclock    24
76 #define CdlTest        25
77 #define CdlID          26
78 #define CdlReadS       27
79 #define CdlReset       28
80 #define CdlReadToc     30
81
82 char *CmdName[0x100]= {
83     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
84     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
85     "CdlStop",     "CdlPause",     "CdlInit",    "CdlMute",
86     "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetmode",
87     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
88     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
89     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
90     "CdlReset",    NULL,           "CDlReadToc", NULL
91 };
92
93 unsigned char Test04[] = { 0 };
94 unsigned char Test05[] = { 0 };
95 unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
96 unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
97 unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
98
99 // cdr.Stat:
100 #define NoIntr          0
101 #define DataReady       1
102 #define Complete        2
103 #define Acknowledge     3
104 #define DataEnd         4
105 #define DiskError       5
106
107 /* Modes flags */
108 #define MODE_SPEED       (1<<7) // 0x80
109 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
110 #define MODE_SIZE_2340   (1<<5) // 0x20
111 #define MODE_SIZE_2328   (1<<4) // 0x10
112 #define MODE_SF          (1<<3) // 0x08 channel on/off
113 #define MODE_REPORT      (1<<2) // 0x04
114 #define MODE_AUTOPAUSE   (1<<1) // 0x02
115 #define MODE_CDDA        (1<<0) // 0x01
116
117 /* Status flags */
118 #define STATUS_PLAY      (1<<7) // 0x80
119 #define STATUS_SEEK      (1<<6) // 0x40
120 #define STATUS_READ      (1<<5) // 0x20
121 #define STATUS_SHELLOPEN (1<<4) // 0x10
122 #define STATUS_UNKNOWN3  (1<<3) // 0x08
123 #define STATUS_UNKNOWN2  (1<<2) // 0x04
124 #define STATUS_ROTATING  (1<<1) // 0x02
125 #define STATUS_ERROR     (1<<0) // 0x01
126
127
128
129 // 1x = 75 sectors per second
130 // PSXCLK = 1 sec in the ps
131 // so (PSXCLK / 75) = cdr read time (linuzappz)
132 #define cdReadTime (PSXCLK / 75)
133
134 // for cdr.Seeked
135 enum seeked_state {
136         SEEK_PENDING = 0,
137         SEEK_DONE = 1,
138 };
139
140 static struct CdrStat stat;
141
142 static unsigned int msf2sec(const u8 *msf) {
143         return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
144 }
145
146 // for that weird psemu API..
147 static unsigned int fsm2sec(const u8 *msf) {
148         return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
149 }
150
151 static void sec2msf(unsigned int s, u8 *msf) {
152         msf[0] = s / 75 / 60;
153         s = s - msf[0] * 75 * 60;
154         msf[1] = s / 75;
155         s = s - msf[1] * 75;
156         msf[2] = s;
157 }
158
159 // cdrInterrupt
160 #define CDR_INT(eCycle) { \
161         psxRegs.interrupt |= (1 << PSXINT_CDR); \
162         psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
163         psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
164         new_dyna_set_event(PSXINT_CDR, eCycle); \
165 }
166
167 // cdrReadInterrupt
168 #define CDREAD_INT(eCycle) { \
169         psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
170         psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
171         psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
172         new_dyna_set_event(PSXINT_CDREAD, eCycle); \
173 }
174
175 // cdrLidSeekInterrupt
176 #define CDRLID_INT(eCycle) { \
177         psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
178         psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
179         psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
180         new_dyna_set_event(PSXINT_CDRLID, eCycle); \
181 }
182
183 // cdrPlayInterrupt
184 #define CDRMISC_INT(eCycle) { \
185         psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
186         psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
187         psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
188         new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \
189 }
190
191 #define StopReading() { \
192         if (cdr.Reading) { \
193                 cdr.Reading = 0; \
194                 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
195         } \
196         cdr.StatP &= ~STATUS_READ;\
197 }
198
199 #define StopCdda() { \
200         if (cdr.Play) { \
201                 if (!Config.Cdda) CDR_stop(); \
202                 cdr.StatP &= ~STATUS_PLAY; \
203                 cdr.Play = FALSE; \
204                 cdr.FastForward = 0; \
205                 cdr.FastBackward = 0; \
206                 /*SPU_registerCallback( SPUirq );*/ \
207         } \
208 }
209
210 #define SetResultSize(size) { \
211         cdr.ResultP = 0; \
212         cdr.ResultC = size; \
213         cdr.ResultReady = 1; \
214 }
215
216 static void setIrq(void)
217 {
218         if (cdr.Stat & cdr.Reg2)
219                 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
220 }
221
222 void cdrLidSeekInterrupt()
223 {
224         // turn back on checking
225         if( cdr.LidCheck == 0x10 )
226         {
227                 cdr.LidCheck = 0;
228         }
229
230         // official lid close
231         else if( cdr.LidCheck == 0x30 )
232         {
233                 // GS CDX 3.3: $13
234                 cdr.StatP |= STATUS_ROTATING;
235
236
237                 // GS CDX 3.3 - ~50 getlocp tries
238                 CDRLID_INT( cdReadTime * 3 );
239                 cdr.LidCheck = 0x40;
240         }
241
242         // turn off ready
243         else if( cdr.LidCheck == 0x40 )
244         {
245                 // GS CDX 3.3: $01
246                 cdr.StatP &= ~STATUS_SHELLOPEN;
247                 cdr.StatP &= ~STATUS_ROTATING;
248
249
250                 // GS CDX 3.3 - ~50 getlocp tries
251                 CDRLID_INT( cdReadTime * 3 );
252                 cdr.LidCheck = 0x50;
253         }
254
255         // now seek
256         else if( cdr.LidCheck == 0x50 )
257         {
258                 // GameShark Lite: Start seeking ($42)
259                 cdr.StatP |= STATUS_SEEK;
260                 cdr.StatP |= STATUS_ROTATING;
261                 cdr.StatP &= ~STATUS_ERROR;
262
263
264                 CDRLID_INT( cdReadTime * 3 );
265                 cdr.LidCheck = 0x60;
266         }
267
268         // done = cd ready
269         else if( cdr.LidCheck == 0x60 )
270         {
271                 // GameShark Lite: Seek detection done ($02)
272                 cdr.StatP &= ~STATUS_SEEK;
273
274                 cdr.LidCheck = 0;
275         }
276 }
277
278 static void Check_Shell( int Irq )
279 {
280         // check case open/close
281         if (cdr.LidCheck > 0)
282         {
283                 CDR_LOG( "LidCheck\n" );
284
285                 // $20 = check lid state
286                 if( cdr.LidCheck == 0x20 )
287                 {
288                         u32 i;
289
290                         i = stat.Status;
291                         if (CDR_getStatus(&stat) != -1)
292                         {
293                                 // BIOS hangs + BIOS error messages
294                                 //if (stat.Type == 0xff)
295                                         //cdr.Stat = DiskError;
296
297                                 // case now open
298                                 if (stat.Status & STATUS_SHELLOPEN)
299                                 {
300                                         // Vib Ribbon: pre-CD swap
301                                         StopCdda();
302
303
304                                         // GameShark Lite: Death if DiskError happens
305                                         //
306                                         // Vib Ribbon: Needs DiskError for CD swap
307
308                                         if (Irq != CdlNop)
309                                         {
310                                                 cdr.Stat = DiskError;
311
312                                                 cdr.StatP |= STATUS_ERROR;
313                                                 cdr.Result[0] |= STATUS_ERROR;
314                                         }
315
316                                         // GameShark Lite: Wants -exactly- $10
317                                         cdr.StatP |= STATUS_SHELLOPEN;
318                                         cdr.StatP &= ~STATUS_ROTATING;
319
320
321                                         CDRLID_INT( cdReadTime * 3 );
322                                         cdr.LidCheck = 0x10;
323
324
325                                         // GS CDX 3.3 = $11
326                                 }
327
328                                 // case just closed
329                                 else if ( i & STATUS_SHELLOPEN )
330                                 {
331                                         cdr.StatP |= STATUS_ROTATING;
332
333                                         CheckCdrom();
334
335
336                                         if( cdr.Stat == NoIntr )
337                                                 cdr.Stat = Acknowledge;
338
339                                         setIrq();
340
341                                         // begin close-seek-ready cycle
342                                         CDRLID_INT( cdReadTime * 3 );
343                                         cdr.LidCheck = 0x30;
344
345
346                                         // GameShark Lite: Wants -exactly- $42, then $02
347                                         // GS CDX 3.3: Wants $11/$80, $13/$80, $01/$00
348                                 }
349
350                                 // case still closed - wait for recheck
351                                 else
352                                 {
353                                         CDRLID_INT( cdReadTime * 3 );
354                                         cdr.LidCheck = 0x10;
355                                 }
356                         }
357                 }
358
359
360                 // GS CDX: clear all values but #1,#2
361                 if( (cdr.LidCheck >= 0x30) || (cdr.StatP & STATUS_SHELLOPEN) )
362                 {
363                         SetResultSize(16);
364                         memset( cdr.Result, 0, 16 );
365
366                         cdr.Result[0] = cdr.StatP;
367
368
369                         // GS CDX: special return value
370                         if( cdr.StatP & STATUS_SHELLOPEN )
371                         {
372                                 cdr.Result[1] = 0x80;
373                         }
374
375
376                         if( cdr.Stat == NoIntr )
377                                 cdr.Stat = Acknowledge;
378
379                         setIrq();
380                 }
381         }
382 }
383
384 static void Find_CurTrack(const u8 *time)
385 {
386         int current, sect;
387
388         current = msf2sec(time);
389
390         for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
391                 CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD);
392                 sect = fsm2sec(cdr.ResultTD);
393                 if (sect - current >= 150)
394                         break;
395         }
396 }
397
398 static void ReadTrack(const u8 *time, int after_seek) {
399         unsigned char tmp[3];
400         struct SubQ *subq;
401         int track, old_track;
402         u16 crc;
403
404         tmp[0] = itob(time[0]);
405         tmp[1] = itob(time[1]);
406         tmp[2] = itob(time[2]);
407
408         if (memcmp(cdr.Prev, tmp, 3) == 0)
409                 return;
410
411         CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
412
413         cdr.RErr = CDR_readTrack(tmp);
414         memcpy(cdr.Prev, tmp, 3);
415
416         cdr.TrackChanged = FALSE;
417
418         if (CheckSBI(time))
419                 return;
420
421         subq = (struct SubQ *)CDR_getBufferSub();
422         if (subq != NULL && !subq_broken) {
423                 crc = calcCrc((u8 *)subq + 12, 10);
424                 old_track = btoi(cdr.subq.Track);
425                 track = btoi(subq->TrackNumber);
426
427                 // track checks are probably wrong, but deals with corrupted
428                 // subq + good checksum
429                 // (how does the real thing handle that?)
430                 if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])
431                     && (after_seek || track == old_track || track == old_track + 1)) {
432                         cdr.subq.Track = subq->TrackNumber;
433                         cdr.subq.Index = subq->IndexNumber;
434                         memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3);
435                         memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3);
436
437                         if (track == cdr.CurTrack + 1) {
438                                 cdr.CurTrack++;
439                                 cdr.TrackChanged = TRUE;
440                         }
441                 }
442                 else {
443                         CDR_LOG_I("ignore subq @%02x:%02x:%02x\n",
444                                 tmp[0], tmp[1], tmp[2]);
445                 }
446         }
447         else {
448                 unsigned char start[3], next[3];
449                 unsigned int this_s, start_s, next_s, pregap;
450                 int relative_s;
451
452                 CDR_getTD(cdr.CurTrack, start);
453                 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
454                         pregap = 150;
455                         CDR_getTD(cdr.CurTrack + 1, next);
456                 }
457                 else {
458                         // last track - cd size
459                         pregap = 0;
460                         next[0] = cdr.SetSectorEnd[2];
461                         next[1] = cdr.SetSectorEnd[1];
462                         next[2] = cdr.SetSectorEnd[0];
463                 }
464
465                 this_s = msf2sec(time);
466                 start_s = fsm2sec(start);
467                 next_s = fsm2sec(next);
468
469                 if (next_s - this_s < pregap) {
470                         cdr.TrackChanged = TRUE;
471                         cdr.CurTrack++;
472                         start_s = next_s;
473                 }
474
475                 cdr.subq.Index = 1;
476
477                 relative_s = this_s - start_s;
478                 if (relative_s < 0) {
479                         cdr.subq.Index = 0;
480                         relative_s = -relative_s;
481                 }
482                 sec2msf(relative_s, cdr.subq.Relative);
483
484                 cdr.subq.Track = itob(cdr.CurTrack);
485                 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
486                 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
487                 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
488                 cdr.subq.Absolute[0] = tmp[0];
489                 cdr.subq.Absolute[1] = tmp[1];
490                 cdr.subq.Absolute[2] = tmp[2];
491         }
492
493         CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
494                 cdr.subq.Track, cdr.subq.Index,
495                 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
496                 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
497 }
498
499 static void AddIrqQueue(unsigned char irq, unsigned long ecycle) {
500         if (cdr.Irq != 0)
501                 CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
502
503         cdr.Irq = irq;
504         cdr.eCycle = ecycle;
505
506         CDR_INT(ecycle);
507 }
508
509 static void cdrPlayInterrupt_Autopause()
510 {
511         if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
512                 CDR_LOG( "CDDA STOP\n" );
513
514                 // Magic the Gathering
515                 // - looping territory cdda
516
517                 // ...?
518                 //cdr.ResultReady = 1;
519                 //cdr.Stat = DataReady;
520                 cdr.Stat = DataEnd;
521                 setIrq();
522
523                 StopCdda();
524         }
525         else if (cdr.Mode & MODE_REPORT) {
526
527                 cdr.Result[0] = cdr.StatP;
528                 cdr.Result[1] = cdr.subq.Track;
529                 cdr.Result[2] = cdr.subq.Index;
530
531                 if (cdr.subq.Absolute[2] & 0x10) {
532                         cdr.Result[3] = cdr.subq.Relative[0];
533                         cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
534                         cdr.Result[5] = cdr.subq.Relative[2];
535                 }
536                 else {
537                         cdr.Result[3] = cdr.subq.Absolute[0];
538                         cdr.Result[4] = cdr.subq.Absolute[1];
539                         cdr.Result[5] = cdr.subq.Absolute[2];
540                 }
541
542                 cdr.Result[6] = 0;
543                 cdr.Result[7] = 0;
544
545                 // Rayman: Logo freeze (resultready + dataready)
546                 cdr.ResultReady = 1;
547                 cdr.Stat = DataReady;
548
549                 SetResultSize(8);
550                 setIrq();
551         }
552 }
553
554 // also handles seek
555 void cdrPlayInterrupt()
556 {
557         if (cdr.Seeked == SEEK_PENDING) {
558                 if (cdr.Stat) {
559                         CDR_LOG_I("cdrom: seek stat hack\n");
560                         CDRMISC_INT(0x1000);
561                         return;
562                 }
563                 SetResultSize(1);
564                 cdr.StatP |= STATUS_ROTATING;
565                 cdr.StatP &= ~STATUS_SEEK;
566                 cdr.Result[0] = cdr.StatP;
567                 cdr.Seeked = SEEK_DONE;
568                 if (cdr.Irq == 0) {
569                         cdr.Stat = Complete;
570                         setIrq();
571                 }
572
573                 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
574                 Find_CurTrack(cdr.SetSectorPlay);
575                 ReadTrack(cdr.SetSectorPlay, 1);
576                 cdr.TrackChanged = FALSE;
577         }
578
579         if (!cdr.Play) return;
580
581         CDR_LOG( "CDDA - %d:%d:%d\n",
582                 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
583
584         if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
585                 StopCdda();
586                 cdr.TrackChanged = TRUE;
587         }
588
589         if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
590                 cdrPlayInterrupt_Autopause();
591
592         if (!cdr.Play) return;
593
594         cdr.SetSectorPlay[2]++;
595         if (cdr.SetSectorPlay[2] == 75) {
596                 cdr.SetSectorPlay[2] = 0;
597                 cdr.SetSectorPlay[1]++;
598                 if (cdr.SetSectorPlay[1] == 60) {
599                         cdr.SetSectorPlay[1] = 0;
600                         cdr.SetSectorPlay[0]++;
601                 }
602         }
603
604         CDRMISC_INT(cdReadTime);
605
606         // update for CdlGetlocP/autopause
607         ReadTrack(cdr.SetSectorPlay, 0);
608 }
609
610 void cdrInterrupt() {
611         int i;
612         unsigned char Irq = cdr.Irq;
613
614         // Reschedule IRQ
615         if (cdr.Stat) {
616                 CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat);
617                 CDR_INT(0x1000);
618                 return;
619         }
620
621         cdr.Irq = 0;
622         cdr.Ctrl &= ~0x80;
623
624         switch (Irq) {
625                 case CdlSync:
626                         SetResultSize(1);
627                         cdr.StatP |= STATUS_ROTATING;
628                         cdr.Result[0] = cdr.StatP;
629                         cdr.Stat = Acknowledge; 
630                         break;
631
632                 case CdlNop:
633                         SetResultSize(1);
634                         cdr.Result[0] = cdr.StatP;
635                         cdr.Stat = Acknowledge;
636
637                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
638                         break;
639
640                 case CdlSetloc:
641                         cdr.CmdProcess = 0;
642                         SetResultSize(1);
643                         cdr.StatP |= STATUS_ROTATING;
644                         cdr.Result[0] = cdr.StatP;
645                         cdr.Stat = Acknowledge;
646                         break;
647
648                 case CdlPlay:
649                         if (cdr.Seeked == SEEK_PENDING) {
650                                 // XXX: wrong, should seek instead..
651                                 memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 );
652                                 cdr.Seeked = SEEK_DONE;
653                         }
654
655                         // BIOS CD Player
656                         // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
657
658                         if (cdr.ParamC == 0 || cdr.Param[0] == 0) {
659                                 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
660                                         cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
661                         }
662                         else
663                         {
664                                 int track = btoi( cdr.Param[0] );
665
666                                 if (track <= cdr.ResultTN[1])
667                                         cdr.CurTrack = track;
668
669                                 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
670
671                                 if (CDR_getTD((u8)cdr.CurTrack, cdr.ResultTD) != -1) {
672                                         cdr.SetSectorPlay[0] = cdr.ResultTD[2];
673                                         cdr.SetSectorPlay[1] = cdr.ResultTD[1];
674                                         cdr.SetSectorPlay[2] = cdr.ResultTD[0];
675                                 }
676                         }
677
678                         /*
679                         Rayman: detect track changes
680                         - fixes logo freeze
681
682                         Twisted Metal 2: skip PREGAP + starting accurate SubQ
683                         - plays tracks without retry play
684
685                         Wild 9: skip PREGAP + starting accurate SubQ
686                         - plays tracks without retry play
687                         */
688                         Find_CurTrack(cdr.SetSectorPlay);
689                         ReadTrack(cdr.SetSectorPlay, 1);
690                         cdr.TrackChanged = FALSE;
691
692                         if (!Config.Cdda)
693                                 CDR_play(cdr.SetSectorPlay);
694
695                         // Vib Ribbon: gameplay checks flag
696                         cdr.StatP &= ~STATUS_SEEK;
697
698                         cdr.CmdProcess = 0;
699                         SetResultSize(1);
700                         cdr.StatP |= STATUS_ROTATING;
701                         cdr.Result[0] = cdr.StatP;
702                         cdr.Stat = Acknowledge;
703
704                         cdr.StatP |= STATUS_PLAY;
705
706                         
707                         // BIOS player - set flag again
708                         cdr.Play = TRUE;
709
710                         CDRMISC_INT( cdReadTime );
711                         break;
712
713                 case CdlForward:
714                         cdr.CmdProcess = 0;
715                         SetResultSize(1);
716                         cdr.StatP |= STATUS_ROTATING;
717                         cdr.Result[0] = cdr.StatP;
718                         cdr.Stat = Complete;
719
720
721                         // GameShark CD Player: Calls 2x + Play 2x
722                         if( cdr.FastForward == 0 ) cdr.FastForward = 2;
723                         else cdr.FastForward++;
724
725                         cdr.FastBackward = 0;
726                         break;
727
728                 case CdlBackward:
729                         cdr.CmdProcess = 0;
730                         SetResultSize(1);
731                         cdr.StatP |= STATUS_ROTATING;
732                         cdr.Result[0] = cdr.StatP;
733                         cdr.Stat = Complete;
734
735
736                         // GameShark CD Player: Calls 2x + Play 2x
737                         if( cdr.FastBackward == 0 ) cdr.FastBackward = 2;
738                         else cdr.FastBackward++;
739
740                         cdr.FastForward = 0;
741                         break;
742
743                 case CdlStandby:
744                         cdr.CmdProcess = 0;
745                         SetResultSize(1);
746                         cdr.StatP |= STATUS_ROTATING;
747                         cdr.Result[0] = cdr.StatP;
748                         cdr.Stat = Complete;
749                         break;
750
751                 case CdlStop:
752                         cdr.CmdProcess = 0;
753                         SetResultSize(1);
754                         cdr.StatP &= ~STATUS_ROTATING;
755                         cdr.Result[0] = cdr.StatP;
756                         cdr.Stat = Complete;
757 //                      cdr.Stat = Acknowledge;
758
759                         if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
760                         break;
761
762                 case CdlPause:
763                         SetResultSize(1);
764                         cdr.Result[0] = cdr.StatP;
765                         cdr.Stat = Acknowledge;
766
767                         /*
768                         Gundam Battle Assault 2: much slower (*)
769                         - Fixes boot, gameplay
770
771                         Hokuto no Ken 2: slower
772                         - Fixes intro + subtitles
773
774                         InuYasha - Feudal Fairy Tale: slower
775                         - Fixes battles
776                         */
777                         AddIrqQueue(CdlPause + 0x20, cdReadTime * 3);
778                         cdr.Ctrl |= 0x80;
779                         break;
780
781                 case CdlPause + 0x20:
782                         SetResultSize(1);
783                         cdr.StatP &= ~STATUS_READ;
784                         cdr.StatP |= STATUS_ROTATING;
785                         cdr.Result[0] = cdr.StatP;
786                         cdr.Stat = Complete;
787                         break;
788
789                 case CdlInit:
790                         SetResultSize(1);
791                         cdr.StatP = STATUS_ROTATING;
792                         cdr.Result[0] = cdr.StatP;
793                         cdr.Stat = Acknowledge;
794 //                      if (!cdr.Init) {
795                                 AddIrqQueue(CdlInit + 0x20, 0x800);
796 //                      }
797                 break;
798
799                 case CdlInit + 0x20:
800                         SetResultSize(1);
801                         cdr.Result[0] = cdr.StatP;
802                         cdr.Stat = Complete;
803                         cdr.Init = 1;
804                         break;
805
806                 case CdlMute:
807                         SetResultSize(1);
808                         cdr.StatP |= STATUS_ROTATING;
809                         cdr.Result[0] = cdr.StatP;
810                         cdr.Stat = Acknowledge;
811                         break;
812
813                 case CdlDemute:
814                         SetResultSize(1);
815                         cdr.StatP |= STATUS_ROTATING;
816                         cdr.Result[0] = cdr.StatP;
817                         cdr.Stat = Acknowledge;
818                         break;
819
820                 case CdlSetfilter:
821                         SetResultSize(1);
822                         cdr.StatP |= STATUS_ROTATING;
823                         cdr.Result[0] = cdr.StatP;
824                         cdr.Stat = Acknowledge; 
825                         break;
826
827                 case CdlSetmode:
828                         SetResultSize(1);
829                         cdr.StatP |= STATUS_ROTATING;
830                         cdr.Result[0] = cdr.StatP;
831                         cdr.Stat = Acknowledge;
832                         break;
833
834                 case CdlGetmode:
835                         SetResultSize(6);
836                         cdr.StatP |= STATUS_ROTATING;
837                         cdr.Result[0] = cdr.StatP;
838                         cdr.Result[1] = cdr.Mode;
839                         cdr.Result[2] = cdr.File;
840                         cdr.Result[3] = cdr.Channel;
841                         cdr.Result[4] = 0;
842                         cdr.Result[5] = 0;
843                         cdr.Stat = Acknowledge;
844                         break;
845
846                 case CdlGetlocL:
847                         SetResultSize(8);
848                         for (i = 0; i < 8; i++)
849                                 cdr.Result[i] = cdr.Transfer[i];
850                         cdr.Stat = Acknowledge;
851                         break;
852
853                 case CdlGetlocP:
854                         SetResultSize(8);
855                         memcpy(&cdr.Result, &cdr.subq, 8);
856
857                         if (!cdr.Play && !cdr.Reading)
858                                 cdr.Result[1] = 0; // HACK?
859
860                         cdr.Stat = Acknowledge;
861                         break;
862
863                 case CdlGetTN:
864                         // 5-Star Racing: don't stop CDDA
865                         //
866                         // Vib Ribbon: CD swap
867                         StopReading();
868
869                         cdr.CmdProcess = 0;
870                         SetResultSize(3);
871                         cdr.StatP |= STATUS_ROTATING;
872                         cdr.Result[0] = cdr.StatP;
873                         if (CDR_getTN(cdr.ResultTN) == -1) {
874                                 cdr.Stat = DiskError;
875                                 cdr.Result[0] |= STATUS_ERROR;
876                         } else {
877                                 cdr.Stat = Acknowledge;
878                                 cdr.Result[1] = itob(cdr.ResultTN[0]);
879                                 cdr.Result[2] = itob(cdr.ResultTN[1]);
880                         }
881                         break;
882
883                 case CdlGetTD:
884                         cdr.CmdProcess = 0;
885                         cdr.Track = btoi(cdr.Param[0]);
886                         SetResultSize(4);
887                         cdr.StatP |= STATUS_ROTATING;
888                         if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
889                                 cdr.Stat = DiskError;
890                                 cdr.Result[0] |= STATUS_ERROR;
891                         } else {
892                                 cdr.Stat = Acknowledge;
893                                 cdr.Result[0] = cdr.StatP;
894                                 cdr.Result[1] = itob(cdr.ResultTD[2]);
895                                 cdr.Result[2] = itob(cdr.ResultTD[1]);
896                                 cdr.Result[3] = itob(cdr.ResultTD[0]);
897                         }
898                         break;
899
900                 case CdlSeekL:
901                 case CdlSeekP:
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                         CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
922                         cdr.Seeked = SEEK_PENDING;
923                         break;
924
925                 case CdlTest:
926                         cdr.Stat = Acknowledge;
927                         switch (cdr.Param[0]) {
928                                 case 0x20: // System Controller ROM Version
929                                         SetResultSize(4);
930                                         memcpy(cdr.Result, Test20, 4);
931                                         break;
932                                 case 0x22:
933                                         SetResultSize(8);
934                                         memcpy(cdr.Result, Test22, 4);
935                                         break;
936                                 case 0x23: case 0x24:
937                                         SetResultSize(8);
938                                         memcpy(cdr.Result, Test23, 4);
939                                         break;
940                         }
941                         break;
942
943                 case CdlID:
944                         SetResultSize(1);
945                         cdr.StatP |= STATUS_ROTATING;
946                         cdr.Result[0] = cdr.StatP;
947                         cdr.Stat = Acknowledge;
948                         AddIrqQueue(CdlID + 0x20, 0x800);
949                         break;
950
951                 case CdlID + 0x20:
952                         SetResultSize(8);
953
954                         if (CDR_getStatus(&stat) == -1) {
955                                 cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player
956                                 cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD
957                         }
958                         else {
959                                 if (stat.Type == 2) {
960                                         // Music CD
961                                         cdr.Result[0] = 0x08;
962                                         cdr.Result[1] = 0x10;
963
964                                         cdr.Result[1] |= 0x80;
965                                 }
966                                 else {
967                                         // Data CD
968                                         if (CdromId[0] == '\0') {
969                                                 cdr.Result[0] = 0x00;
970                                                 cdr.Result[1] = 0x80;
971                                         }
972                                         else {
973                                                 cdr.Result[0] = 0x08;
974                                                 cdr.Result[1] = 0x00;
975                                         }
976                                 }
977                         }
978
979                         cdr.Result[2] = 0x00;
980                         cdr.Result[3] = 0x00;
981                         strncpy((char *)&cdr.Result[4], "PCSX", 4);
982                         cdr.Stat = Complete;
983                         break;
984
985                 case CdlReset:
986                         SetResultSize(1);
987                         cdr.StatP = STATUS_ROTATING;
988                         cdr.Result[0] = cdr.StatP;
989                         cdr.Stat = Acknowledge;
990                         break;
991
992                 case CdlReadT:
993                         SetResultSize(1);
994                         cdr.StatP |= STATUS_ROTATING;
995                         cdr.Result[0] = cdr.StatP;
996                         cdr.Stat = Acknowledge;
997                         AddIrqQueue(CdlReadT + 0x20, 0x800);
998                         break;
999
1000                 case CdlReadT + 0x20:
1001                         SetResultSize(1);
1002                         cdr.StatP |= STATUS_ROTATING;
1003                         cdr.Result[0] = cdr.StatP;
1004                         cdr.Stat = Complete;
1005                         break;
1006
1007                 case CdlReadToc:
1008                         SetResultSize(1);
1009                         cdr.StatP |= STATUS_ROTATING;
1010                         cdr.Result[0] = cdr.StatP;
1011                         cdr.Stat = Acknowledge;
1012                         AddIrqQueue(CdlReadToc + 0x20, 0x800);
1013                         break;
1014
1015                 case CdlReadToc + 0x20:
1016                         SetResultSize(1);
1017                         cdr.StatP |= STATUS_ROTATING;
1018                         cdr.Result[0] = cdr.StatP;
1019                         cdr.Stat = Complete;
1020                         break;
1021
1022                 case CdlReadN:
1023                 case CdlReadS:
1024                         if (!cdr.Reading) return;
1025
1026                         // Fighting Force 2 - update subq time immediately
1027                         // - fixes new game
1028                         cdr.CurTrack = 1;
1029                         ReadTrack(cdr.SetSector, 1);
1030
1031
1032                         // Crusaders of Might and Magic - update getlocl now
1033                         // - fixes cutscene speech
1034                         {
1035                                 u8 *buf = CDR_getBuffer();
1036                                 if (buf != NULL)
1037                                         memcpy(cdr.Transfer, buf, 8);
1038                         }
1039
1040                         /*
1041                         Duke Nukem: Land of the Babes - seek then delay read for one frame
1042                         - fixes cutscenes
1043                         C-12 - Final Resistance - doesn't like seek
1044                         */
1045
1046                         if (cdr.Seeked != SEEK_DONE) {
1047                                 cdr.StatP |= STATUS_SEEK;
1048                                 cdr.StatP &= ~STATUS_READ;
1049
1050                                 // Crusaders of Might and Magic - use short time
1051                                 // - fix cutscene speech (startup)
1052
1053                                 // ??? - use more accurate seek time later
1054                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1055                         } else {
1056                                 cdr.StatP |= STATUS_READ;
1057                                 cdr.StatP &= ~STATUS_SEEK;
1058
1059                                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1);
1060                         }
1061
1062                         SetResultSize(1);
1063                         cdr.StatP |= STATUS_ROTATING;
1064                         cdr.Result[0] = cdr.StatP;
1065                         cdr.Stat = Acknowledge;
1066                         break;
1067
1068                 default:
1069                         cdr.Stat = Complete;
1070                         break;
1071         }
1072
1073         Check_Shell( Irq );
1074
1075         cdr.ParamC = 0;
1076
1077         setIrq();
1078
1079 #ifdef CDR_LOG_CMD_IRQ
1080         SysPrintf("cdrInterrupt() Log: CDR Interrupt IRQ %d %02x: ",
1081                 cdr.Stat != NoIntr && cdr.Reg2 != 0x18, Irq);
1082         for (i = 0; i < cdr.ResultC; i++)
1083                 SysPrintf("%02x ", cdr.Result[i]);
1084         SysPrintf("\n");
1085 #endif
1086 }
1087
1088 void cdrReadInterrupt() {
1089         u8 *buf;
1090
1091         if (!cdr.Reading)
1092                 return;
1093
1094         if (cdr.Irq || cdr.Stat) {
1095                 CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
1096                 CDREAD_INT(0x1000);
1097                 return;
1098         }
1099
1100         cdr.OCUP = 1;
1101         SetResultSize(1);
1102         cdr.StatP |= STATUS_READ|STATUS_ROTATING;
1103         cdr.StatP &= ~STATUS_SEEK;
1104         cdr.Result[0] = cdr.StatP;
1105         cdr.Seeked = SEEK_DONE;
1106
1107         ReadTrack(cdr.SetSector, 0);
1108
1109         buf = CDR_getBuffer();
1110         if (buf == NULL)
1111                 cdr.RErr = -1;
1112
1113         if (cdr.RErr == -1) {
1114                 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1115                 memset(cdr.Transfer, 0, DATA_SIZE);
1116                 cdr.Stat = DiskError;
1117                 cdr.Result[0] |= STATUS_ERROR;
1118                 CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
1119                 return;
1120         }
1121
1122         memcpy(cdr.Transfer, buf, DATA_SIZE);
1123         CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1124
1125
1126         CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1127
1128         if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA
1129                 // Firemen 2: Multi-XA files - briefings, cutscenes
1130                 if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) {
1131                         cdr.File = cdr.Transfer[4 + 0];
1132                         cdr.Channel = cdr.Transfer[4 + 1];
1133                 }
1134
1135                 if ((cdr.Transfer[4 + 2] & 0x4) &&
1136                          (cdr.Transfer[4 + 1] == cdr.Channel) &&
1137                         (cdr.Transfer[4 + 0] == cdr.File)) {
1138                         int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
1139
1140                         if (!ret) {
1141                                 // only handle attenuator basic channel switch for now
1142                                 if (cdr.Xa.stereo) {
1143                                         int i;
1144                                         if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1145                                             && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1146                                         {
1147                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1148                                                         cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2];
1149                                         }
1150                                         else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
1151                                             && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
1152                                         {
1153                                                 for (i = 0; i < cdr.Xa.nsamples; i++)
1154                                                         cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1];
1155                                         }
1156                                 }
1157
1158                                 SPU_playADPCMchannel(&cdr.Xa);
1159                                 cdr.FirstSector = 0;
1160
1161 #if 0
1162                                 // Crash Team Racing: music, speech
1163                                 // - done using cdda decoded buffer (spu irq)
1164                                 // - don't do here
1165
1166                                 // signal ADPCM data ready
1167                                 setIrq();
1168 #endif
1169                         }
1170                         else cdr.FirstSector = -1;
1171                 }
1172         }
1173
1174         cdr.SetSector[2]++;
1175         if (cdr.SetSector[2] == 75) {
1176                 cdr.SetSector[2] = 0;
1177                 cdr.SetSector[1]++;
1178                 if (cdr.SetSector[1] == 60) {
1179                         cdr.SetSector[1] = 0;
1180                         cdr.SetSector[0]++;
1181                 }
1182         }
1183
1184         cdr.Readed = 0;
1185
1186         CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
1187
1188         /*
1189         Croc 2: $40 - only FORM1 (*)
1190         Judge Dredd: $C8 - only FORM1 (*)
1191         Sim Theme Park - no adpcm at all (zero)
1192         */
1193
1194         if (!(cdr.Mode & MODE_STRSND) || !(cdr.Transfer[4+2] & 0x4)) {
1195                 cdr.Stat = DataReady;
1196                 setIrq();
1197         }
1198
1199         // update for CdlGetlocP
1200         ReadTrack(cdr.SetSector, 0);
1201
1202         Check_Shell(0);
1203 }
1204
1205 /*
1206 cdrRead0:
1207         bit 0,1 - mode
1208         bit 2 - unknown
1209         bit 3 - unknown
1210         bit 4 - unknown
1211         bit 5 - 1 result ready
1212         bit 6 - 1 dma ready
1213         bit 7 - 1 command being processed
1214 */
1215
1216 unsigned char cdrRead0(void) {
1217         if (cdr.ResultReady)
1218                 cdr.Ctrl |= 0x20;
1219         else
1220                 cdr.Ctrl &= ~0x20;
1221
1222         if (cdr.OCUP)
1223                 cdr.Ctrl |= 0x40;
1224 //  else
1225 //              cdr.Ctrl &= ~0x40;
1226
1227         // What means the 0x10 and the 0x08 bits? I only saw it used by the bios
1228         cdr.Ctrl |= 0x18;
1229
1230         CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl);
1231
1232         return psxHu8(0x1800) = cdr.Ctrl;
1233 }
1234
1235 void cdrWrite0(unsigned char rt) {
1236         CDR_LOG_IO("cdr w0: %02x\n", rt);
1237
1238         cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1239 }
1240
1241 unsigned char cdrRead1(void) {
1242         if ((cdr.ResultP & 0xf) < cdr.ResultC)
1243                 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1244         else
1245                 psxHu8(0x1801) = 0;
1246         cdr.ResultP++;
1247         if (cdr.ResultP == cdr.ResultC)
1248                 cdr.ResultReady = 0;
1249
1250         CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801));
1251
1252         return psxHu8(0x1801);
1253 }
1254
1255 void cdrWrite1(unsigned char rt) {
1256         u8 set_loc[3];
1257         int i;
1258
1259         CDR_LOG_IO("cdr w1: %02x\n", rt);
1260
1261         switch (cdr.Ctrl & 3) {
1262         case 0:
1263                 break;
1264         case 3:
1265                 cdr.AttenuatorRight[1] = rt;
1266                 return;
1267         default:
1268                 return;
1269         }
1270
1271         cdr.Cmd = rt;
1272         cdr.OCUP = 0;
1273
1274 #ifdef CDR_LOG_CMD_IRQ
1275         SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]);
1276         if (cdr.ParamC) {
1277                 SysPrintf(" Param[%d] = {", cdr.ParamC);
1278                 for (i = 0; i < cdr.ParamC; i++)
1279                         SysPrintf(" %x,", cdr.Param[i]);
1280                 SysPrintf("}\n");
1281         } else {
1282                 SysPrintf("\n");
1283         }
1284 #endif
1285
1286         cdr.ResultReady = 0;
1287         cdr.Ctrl |= 0x80;
1288         // cdr.Stat = NoIntr; 
1289         AddIrqQueue(cdr.Cmd, 0x800);
1290
1291         switch (cdr.Cmd) {
1292         case CdlSync:
1293         case CdlNop:
1294         case CdlForward:
1295         case CdlBackward:
1296         case CdlReadT:
1297         case CdlTest:
1298         case CdlID:
1299         case CdlReadToc:
1300         case CdlGetmode:
1301         case CdlGetlocL:
1302         case CdlGetlocP:
1303         case CdlGetTD:
1304                 break;
1305
1306         case CdlSetloc:
1307                 StopReading();
1308                 for (i = 0; i < 3; i++)
1309                         set_loc[i] = btoi(cdr.Param[i]);
1310
1311                 // FIXME: clean up this SetSector/SetSectorPlay mess,
1312                 // there should be single var tracking current sector pos
1313                 if (cdr.Play)
1314                         i = msf2sec(cdr.SetSectorPlay);
1315                 else
1316                         i = msf2sec(cdr.SetSector);
1317                 i = abs(i - msf2sec(set_loc));
1318                 if (i > 16)
1319                         cdr.Seeked = SEEK_PENDING;
1320
1321                 memcpy(cdr.SetSector, set_loc, 3);
1322                 cdr.SetSector[3] = 0;
1323                 break;
1324
1325         case CdlPlay:
1326                 // Vib Ribbon: try same track again
1327                 StopCdda();
1328
1329 #if 0
1330                 if (!cdr.SetSector[0] & !cdr.SetSector[1] & !cdr.SetSector[2]) {
1331                         if (CDR_getTN(cdr.ResultTN) != -1) {
1332                                 if (cdr.CurTrack > cdr.ResultTN[1])
1333                                         cdr.CurTrack = cdr.ResultTN[1];
1334                                 if (CDR_getTD((unsigned char)(cdr.CurTrack), cdr.ResultTD) != -1) {
1335                                         int tmp = cdr.ResultTD[2];
1336                                         cdr.ResultTD[2] = cdr.ResultTD[0];
1337                                         cdr.ResultTD[0] = tmp;
1338                                         if (!Config.Cdda) CDR_play(cdr.ResultTD);
1339                                 }
1340                         }
1341                 }
1342 #endif
1343                 // Vib Ribbon - decoded buffer IRQ for CDDA reading
1344                 // - fixes ribbon timing + music CD mode
1345                 //TODO?
1346                 //CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
1347
1348                 cdr.Play = TRUE;
1349
1350                 cdr.StatP |= STATUS_SEEK;
1351                 cdr.StatP &= ~STATUS_ROTATING;
1352                 break;
1353
1354         case CdlReadN:
1355                 StopReading();
1356                 cdr.Reading = 1;
1357                 cdr.FirstSector = 1;
1358                 cdr.Readed = 0xff;
1359                 break;
1360
1361         case CdlStandby:
1362                 StopCdda();
1363                 StopReading();
1364                 break;
1365
1366         case CdlStop:
1367                 // GameShark CD Player: Reset CDDA to track start
1368                 if (cdr.Play) {
1369                         // grab time for current track
1370                         CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD);
1371
1372                         cdr.SetSectorPlay[0] = cdr.ResultTD[2];
1373                         cdr.SetSectorPlay[1] = cdr.ResultTD[1];
1374                         cdr.SetSectorPlay[2] = cdr.ResultTD[0];
1375                 }
1376
1377                 StopCdda();
1378                 StopReading();
1379                 break;
1380
1381         case CdlPause:
1382                 /*
1383                    GameShark CD Player: save time for resume
1384
1385                    Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors
1386                 */
1387
1388                 StopCdda();
1389                 StopReading();
1390                 break;
1391
1392         case CdlReset:
1393         case CdlInit:
1394                 cdr.Seeked = SEEK_DONE;
1395                 StopCdda();
1396                 StopReading();
1397                 break;
1398
1399         case CdlMute:
1400                 cdr.Muted = TRUE;
1401                         // Duke Nukem - Time to Kill
1402                         // - do not directly set cd-xa volume
1403                         //SPU_writeRegister( H_CDLeft, 0x0000 );
1404                         //SPU_writeRegister( H_CDRight, 0x0000 );
1405                 break;
1406
1407         case CdlDemute:
1408                 cdr.Muted = FALSE;
1409
1410                         // Duke Nukem - Time to Kill
1411                         // - do not directly set cd-xa volume
1412                         //SPU_writeRegister( H_CDLeft, 0x7f00 );
1413                         //SPU_writeRegister( H_CDRight, 0x7f00 );
1414                 break;
1415
1416         case CdlSetfilter:
1417                 cdr.File = cdr.Param[0];
1418                 cdr.Channel = cdr.Param[1];
1419                 break;
1420
1421         case CdlSetmode:
1422                 CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
1423
1424                 cdr.Mode = cdr.Param[0];
1425
1426                 // Squaresoft on PlayStation 1998 Collector's CD Vol. 1
1427                 // - fixes choppy movie sound
1428                 if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
1429                         StopCdda();
1430                 break;
1431
1432         case CdlGetTN:
1433                 //AddIrqQueue(cdr.Cmd, 0x800);
1434
1435                 // GameShark CDX CD Player: very long time
1436                 AddIrqQueue(cdr.Cmd, 0x100000);
1437                 break;
1438
1439         case CdlSeekL:
1440         case CdlSeekP:
1441                 // Tomb Raider 2 - reset cdda
1442                 StopCdda();
1443                 StopReading();
1444                 break;
1445
1446         case CdlReadS:
1447                 StopReading();
1448                 cdr.Reading = 2;
1449                 cdr.FirstSector = 1;
1450                 cdr.Readed = 0xff;
1451                 break;
1452
1453         default:
1454                 cdr.ParamC = 0;
1455                 CDR_LOG_I("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd);
1456                 return;
1457         }
1458 }
1459
1460 unsigned char cdrRead2(void) {
1461         unsigned char ret;
1462
1463         if (cdr.Readed == 0) {
1464                 ret = 0;
1465         } else {
1466                 ret = *pTransfer++;
1467         }
1468
1469         CDR_LOG_IO("cdr r2: %02x\n", ret);
1470         return ret;
1471 }
1472
1473 void cdrWrite2(unsigned char rt) {
1474         CDR_LOG_IO("cdr w2: %02x\n", rt);
1475
1476         switch (cdr.Ctrl & 3) {
1477         case 0:
1478                 if (cdr.ParamC < 8) // FIXME: size and wrapping
1479                         cdr.Param[cdr.ParamC++] = rt;
1480                 return;
1481         case 1:
1482                 cdr.Reg2 = rt;
1483                 setIrq();
1484                 return;
1485         case 2:
1486                 cdr.AttenuatorLeft[0] = rt;
1487                 return;
1488         case 3:
1489                 cdr.AttenuatorRight[0] = rt;
1490                 return;
1491         }
1492 }
1493
1494 unsigned char cdrRead3(void) {
1495         if (cdr.Ctrl & 0x1)
1496                 psxHu8(0x1803) = cdr.Stat | 0xE0;
1497         else
1498                 psxHu8(0x1803) = cdr.Reg2 | 0xE0;
1499
1500         CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803));
1501         return psxHu8(0x1803);
1502 }
1503
1504 void cdrWrite3(unsigned char rt) {
1505         CDR_LOG_IO("cdr w3: %02x\n", rt);
1506
1507         switch (cdr.Ctrl & 3) {
1508         case 0:
1509                 break; // transfer
1510         case 1:
1511                 cdr.Stat &= ~rt;
1512
1513                 if (rt & 0x40)
1514                         cdr.ParamC = 0;
1515                 return;
1516         case 2:
1517                 cdr.AttenuatorLeft[1] = rt;
1518                 return;
1519         case 3:
1520                 if (rt == 0x20)
1521                 CDR_LOG( "CD-XA Volume: %X %X | %X %X\n",
1522                         cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1],
1523                         cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
1524                 return;
1525         }
1526
1527         if ((rt & 0x80) && cdr.Readed == 0) {
1528                 cdr.Readed = 1;
1529                 pTransfer = cdr.Transfer;
1530
1531                 switch (cdr.Mode & 0x30) {
1532                         case MODE_SIZE_2328:
1533                         case 0x00:
1534                                 pTransfer += 12;
1535                                 break;
1536
1537                         case MODE_SIZE_2340:
1538                                 pTransfer += 0;
1539                                 break;
1540
1541                         default:
1542                                 break;
1543                 }
1544         }
1545 }
1546
1547 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1548         u32 cdsize;
1549         int size;
1550         u8 *ptr;
1551
1552         CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr);
1553
1554         switch (chcr) {
1555                 case 0x11000000:
1556                 case 0x11400100:
1557                         if (cdr.Readed == 0) {
1558                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n");
1559                                 break;
1560                         }
1561
1562                         cdsize = (bcr & 0xffff) * 4;
1563
1564                         // Ape Escape: bcr = 0001 / 0000
1565                         // - fix boot
1566                         if( cdsize == 0 )
1567                         {
1568                                 switch (cdr.Mode & 0x30) {
1569                                         case 0x00: cdsize = 2048; break;
1570                                         case MODE_SIZE_2328: cdsize = 2328; break;
1571                                         case MODE_SIZE_2340: cdsize = 2340; break;
1572                                 }
1573                         }
1574
1575
1576                         ptr = (u8 *)PSXM(madr);
1577                         if (ptr == NULL) {
1578                                 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1579                                 break;
1580                         }
1581
1582                         /*
1583                         GS CDX: Enhancement CD crash
1584                         - Setloc 0:0:0
1585                         - CdlPlay
1586                         - Spams DMA3 and gets buffer overrun
1587                         */
1588                         size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer);
1589                         if (size > cdsize)
1590                                 size = cdsize;
1591                         if (size > 0)
1592                         {
1593                                 memcpy(ptr, pTransfer, size);
1594                         }
1595
1596                         psxCpu->Clear(madr, cdsize / 4);
1597                         pTransfer += cdsize;
1598
1599
1600                         // burst vs normal
1601                         if( chcr == 0x11400100 ) {
1602                                 CDRDMA_INT( (cdsize/4) / 4 );
1603                         }
1604                         else if( chcr == 0x11000000 ) {
1605                                 CDRDMA_INT( (cdsize/4) * 1 );
1606                         }
1607                         return;
1608
1609                 default:
1610                         CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr);
1611                         break;
1612         }
1613
1614         HW_DMA3_CHCR &= SWAP32(~0x01000000);
1615         DMA_INTERRUPT(3);
1616 }
1617
1618 void cdrDmaInterrupt()
1619 {
1620         if (HW_DMA3_CHCR & SWAP32(0x01000000))
1621         {
1622                 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1623                 DMA_INTERRUPT(3);
1624         }
1625 }
1626
1627 static void getCdInfo(void)
1628 {
1629         unsigned int i, j, sector;
1630         struct SubQ *subq;
1631         u8 tmpp[3];
1632         u8 tmp;
1633
1634         CDR_getTN(cdr.ResultTN);
1635         CDR_getTD(0, cdr.SetSectorEnd);
1636         tmp = cdr.SetSectorEnd[0];
1637         cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2];
1638         cdr.SetSectorEnd[2] = tmp;
1639
1640         subq_broken = 0;
1641         subq = (struct SubQ *)CDR_getBufferSub();
1642         if (subq != NULL && cdr.ResultTN[1] >= 2) {
1643                 CDR_getTD(cdr.ResultTN[1], tmpp);
1644                 sector = fsm2sec(tmpp) - 33;
1645                 for (i = 0; i < 5; i++, sector += 2) {
1646                         sec2msf(sector, tmpp);
1647                         for (j = 0; j < 3; j++)
1648                                 tmpp[j] = itob(tmpp[j]);
1649                         CDR_readTrack(tmpp);
1650                         subq = (struct SubQ *)CDR_getBufferSub();
1651                         if (subq->IndexNumber == 0)
1652                                 break;
1653                 }
1654                 if (i == 5) {
1655                         SysPrintf("cdrom: subchannel data looks broken, not using it\n");
1656                         subq_broken = 1;
1657                 }
1658         }
1659 }
1660
1661 void cdrReset() {
1662         memset(&cdr, 0, sizeof(cdr));
1663         cdr.CurTrack = 1;
1664         cdr.File = 1;
1665         cdr.Channel = 1;
1666         pTransfer = cdr.Transfer;
1667
1668         // BIOS player - default values
1669         cdr.AttenuatorLeft[0] = 0x80;
1670         cdr.AttenuatorLeft[1] = 0x00;
1671         cdr.AttenuatorRight[0] = 0x00;
1672         cdr.AttenuatorRight[1] = 0x80;
1673
1674         getCdInfo();
1675 }
1676
1677 int cdrFreeze(void *f, int Mode) {
1678         u32 tmp;
1679         u8 tmpp[3];
1680
1681         if (Mode == 0 && !Config.Cdda)
1682                 CDR_stop();
1683         
1684         cdr.freeze_ver = 0x63647201;
1685         gzfreeze(&cdr, sizeof(cdr));
1686         
1687         if (Mode == 1) {
1688                 cdr.ParamP = cdr.ParamC;
1689                 tmp = pTransfer - cdr.Transfer;
1690         }
1691
1692         gzfreeze(&tmp, sizeof(tmp));
1693
1694         if (Mode == 0) {
1695                 getCdInfo();
1696
1697                 pTransfer = cdr.Transfer + tmp;
1698
1699                 // read right sub data
1700                 memcpy(tmpp, cdr.Prev, 3);
1701                 cdr.Prev[0]++;
1702                 ReadTrack(tmpp, 1);
1703
1704                 if (cdr.Play) {
1705                         Find_CurTrack(cdr.SetSectorPlay);
1706                         if (!Config.Cdda)
1707                                 CDR_play(cdr.SetSectorPlay);
1708                 }
1709
1710                 if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
1711                         // old versions did not latch Reg2, have to fixup..
1712                         if (cdr.Reg2 == 0) {
1713                                 SysPrintf("cdrom: fixing up old savestate\n");
1714                                 cdr.Reg2 = 7;
1715                         }
1716                 }
1717         }
1718
1719         return 0;
1720 }
1721
1722 void LidInterrupt() {
1723         cdr.LidCheck = 0x20; // start checker
1724
1725         getCdInfo();
1726        
1727         StopCdda();
1728         CDRLID_INT( cdReadTime * 3 );
1729
1730         // generate interrupt if none active - open or close
1731         if (cdr.Irq == 0 || cdr.Irq == 0xff) {
1732                 cdr.Ctrl |= 0x80;
1733                 cdr.Stat = NoIntr;
1734                 AddIrqQueue(CdlNop, 0x800);
1735         }
1736 }