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