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