cdrom: resume cdda on state load
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
index fff3160..d9a3bb3 100644 (file)
@@ -120,7 +120,6 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
 #define cdReadTime (PSXCLK / 75)
 
 static struct CdrStat stat;
-static struct SubQ *subq;
 
 static unsigned int msf2sec(char *msf) {
        return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
@@ -156,6 +155,13 @@ static void sec2msf(unsigned int s, char *msf) {
        new_dyna_set_event(PSXINT_CDRLID, eCycle); \
 }
 
+#define CDRPLAY_INT(eCycle) { \
+       psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
+       psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
+       psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
+       new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \
+}
+
 #define StartReading(type, eCycle) { \
        cdr.Reading = type; \
        cdr.FirstSector = 1; \
@@ -263,11 +269,12 @@ static void Check_Shell( int Irq )
                        i = stat.Status;
                        if (CDR_getStatus(&stat) != -1)
                        {
-                               if (stat.Type == 0xff)
-                                       cdr.Stat = DiskError;
+                               // BIOS hangs + BIOS error messages
+                               //if (stat.Type == 0xff)
+                                       //cdr.Stat = DiskError;
 
                                // case now open
-                               else if (stat.Status & STATUS_SHELLOPEN)
+                               if (stat.Status & STATUS_SHELLOPEN)
                                {
                                        // Vib Ribbon: pre-CD swap
                                        StopCdda();
@@ -446,7 +453,7 @@ void Set_Track()
 
 
 static u8 fake_subq_local[3], fake_subq_real[3], fake_subq_index, fake_subq_change;
-void Create_Fake_Subq()
+static void Create_Fake_Subq()
 {
        u8 temp_cur[3], temp_next[3], temp_start[3], pregap;
        int diff;
@@ -529,9 +536,127 @@ void Create_Fake_Subq()
 }
 
 
+static void cdrPlayInterrupt_Autopause()
+{
+       struct SubQ *subq = (struct SubQ *)CDR_getBufferSub();
+       if (subq != NULL ) {
+#ifdef CDR_LOG
+               CDR_LOG( "CDDA SUB - %X:%X:%X\n",
+                       subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
+#endif
+
+               /*
+               CDDA Autopause
+
+               Silhouette Mirage ($3)
+               Tomb Raider 1 ($7)
+               */
+
+               if( cdr.CurTrack >= btoi( subq->TrackNumber ) )
+                       return;
+       } else {
+               Create_Fake_Subq();
+#ifdef CDR_LOG___0
+               CDR_LOG( "CDDA FAKE SUB - %d:%d:%d\n",
+                       fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
+#endif
+
+               if( !fake_subq_change )
+                       return;
+
+               fake_subq_change = 0;
+       }
+
+       if (cdr.Mode & MODE_AUTOPAUSE) {
+#ifdef CDR_LOG
+               CDR_LOG( "CDDA STOP\n" );
+#endif
+
+               // Magic the Gathering
+               // - looping territory cdda
+
+               // ...?
+               //cdr.ResultReady = 1;
+               //cdr.Stat = DataReady;
+               cdr.Stat = DataEnd;
+               psxHu32ref(0x1070) |= SWAP32((u32)0x4);
+
+               StopCdda();
+       }
+       if (cdr.Mode & MODE_REPORT) {
+               // rearmed note: PCSX-Reloaded does this for every sector,
+               // but we try to get away with only track change here.
+               memset( cdr.Result, 0, 8 );
+               cdr.Result[0] |= 0x10;
+
+               if (subq != NULL) {
+#ifdef CDR_LOG
+                       CDR_LOG( "REPPLAY SUB - %X:%X:%X\n",
+                               subq->AbsoluteAddress[0], subq->AbsoluteAddress[1], subq->AbsoluteAddress[2] );
+#endif
+                       cdr.CurTrack = btoi( subq->TrackNumber );
+
+                       // BIOS CD Player: data already BCD format
+                       cdr.Result[1] = subq->TrackNumber;
+                       cdr.Result[2] = subq->IndexNumber;
+
+                       cdr.Result[3] = subq->AbsoluteAddress[0];
+                       cdr.Result[4] = subq->AbsoluteAddress[1];
+                       cdr.Result[5] = subq->AbsoluteAddress[2];
+               } else {
+#ifdef CDR_LOG___0
+                       CDR_LOG( "REPPLAY FAKE - %d:%d:%d\n",
+                               fake_subq_real[0], fake_subq_real[1], fake_subq_real[2] );
+#endif
+
+                       // track # / index #
+                       cdr.Result[1] = itob(cdr.CurTrack);
+                       cdr.Result[2] = itob(fake_subq_index);
+                       // absolute
+                       cdr.Result[3] = itob( fake_subq_real[0] );
+                       cdr.Result[4] = itob( fake_subq_real[1] );
+                       cdr.Result[5] = itob( fake_subq_real[2] );
+               }
+
+               // Rayman: Logo freeze (resultready + dataready)
+               cdr.ResultReady = 1;
+               cdr.Stat = DataReady;
+
+               SetResultSize(8);
+               psxHu32ref(0x1070) |= SWAP32((u32)0x4);
+       }
+}
+
+void cdrPlayInterrupt()
+{
+       if( !cdr.Play ) return;
+
+#ifdef CDR_LOG
+       CDR_LOG( "CDDA - %d:%d:%d\n",
+               cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
+#endif
+       CDRPLAY_INT( cdReadTime );
+
+       if (!cdr.Irq && !cdr.Stat && (cdr.Mode & MODE_CDDA) && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
+               cdrPlayInterrupt_Autopause();
+
+       cdr.SetSectorPlay[2]++;
+       if (cdr.SetSectorPlay[2] == 75) {
+               cdr.SetSectorPlay[2] = 0;
+               cdr.SetSectorPlay[1]++;
+               if (cdr.SetSectorPlay[1] == 60) {
+                       cdr.SetSectorPlay[1] = 0;
+                       cdr.SetSectorPlay[0]++;
+               }
+       }
+
+       //Check_Shell(0);
+}
+
 void cdrInterrupt() {
        int i;
        unsigned char Irq = cdr.Irq;
+       struct SubQ *subq;
 
        // Reschedule IRQ
        if (cdr.Stat) {
@@ -584,11 +709,12 @@ void cdrInterrupt() {
                        Wild 9: skip PREGAP + starting accurate SubQ
                        - plays tracks without retry play
                        */
+                       /* unneeded with correct cdriso?
                        Set_Track();
+                       */
                        Find_CurTrack();
                        ReadTrack( cdr.SetSectorPlay );
 
-
                        // GameShark CD Player: Calls 2x + Play 2x
                        if( cdr.FastBackward || cdr.FastForward ) {
                                if( cdr.FastForward ) cdr.FastForward--;
@@ -679,8 +805,7 @@ void cdrInterrupt() {
                        // BIOS player - set flag again
                        cdr.Play = TRUE;
 
-                       // TODO?
-                       // CDRPLAY_INT( cdReadTime );
+                       CDRPLAY_INT( cdReadTime );
                        break;
 
                case CdlForward:
@@ -737,7 +862,17 @@ void cdrInterrupt() {
                        cdr.Result[0] = cdr.StatP;
                        cdr.Stat = Acknowledge;
 
-                       AddIrqQueue(CdlPause + 0x20, 0x800);
+                       /*
+                       Gundam Battle Assault 2: much slower (*)
+                       - Fixes boot, gameplay
+
+                       Hokuto no Ken 2: slower
+                       - Fixes intro + subtitles
+
+                       InuYasha - Feudal Fairy Tale: slower
+                       - Fixes battles
+                       */
+                       AddIrqQueue(CdlPause + 0x20, cdReadTime * 3);
                        cdr.Ctrl |= 0x80;
                        break;
 
@@ -834,7 +969,8 @@ void cdrInterrupt() {
                                }
                                }
                        } else {
-                               if( cdr.Play == FALSE ) Create_Fake_Subq();
+                               if( cdr.Play == FALSE || !(cdr.Mode & MODE_CDDA) || !(cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)) )
+                                       Create_Fake_Subq();
 
 
                                // track # / index #
@@ -853,10 +989,9 @@ void cdrInterrupt() {
                        }
 
                        // redump.org - wipe time
-                       /*if( !cdr.Play && CheckSBI(cdr.Result+5) ) {
+                       if( !cdr.Play && CheckSBI(cdr.Result+5) ) {
                                memset( cdr.Result+2, 0, 6 );
                        }
-                       */
 
                        cdr.Stat = Acknowledge;
                        break;
@@ -1082,13 +1217,15 @@ void cdrInterrupt() {
                        // - fixes cutscene speech
                        {
                                u8 *buf = CDR_getBuffer();
-                               memcpy(cdr.Transfer, buf, 8);
+                               if (buf != NULL)
+                                       memcpy(cdr.Transfer, buf, 8);
                        }
                        
                        
                        /*
                        Duke Nukem: Land of the Babes - seek then delay read for one frame
                        - fixes cutscenes
+                       C-12 - Final Resistance - doesn't like seek
                        */
 
                        if (!cdr.Seeked) {
@@ -1193,14 +1330,34 @@ void cdrReadInterrupt() {
                        int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
 
                        if (!ret) {
+                               // only handle attenuator basic channel switch for now
+                               if (cdr.Xa.stereo) {
+                                       int i;
+                                       if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
+                                           && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
+                                       {
+                                               for (i = 0; i < cdr.Xa.nsamples; i++)
+                                                       cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2];
+                                       }
+                                       else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1])
+                                           && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1]))
+                                       {
+                                               for (i = 0; i < cdr.Xa.nsamples; i++)
+                                                       cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1];
+                                       }
+                               }
+
                                SPU_playADPCMchannel(&cdr.Xa);
                                cdr.FirstSector = 0;
 
-
+#if 0
                                // Crash Team Racing: music, speech
+                               // - done using cdda decoded buffer (spu irq)
+                               // - don't do here
 
                                // signal ADPCM data ready
                                psxHu32ref(0x1070) |= SWAP32((u32)0x200);
+#endif
                        }
                        else cdr.FirstSector = -1;
                }
@@ -1317,6 +1474,7 @@ unsigned char cdrRead1(void) {
 }
 
 void cdrWrite1(unsigned char rt) {
+       char set_loc[3];
        int i;
 
 #ifdef CDR_LOG
@@ -1326,7 +1484,7 @@ void cdrWrite1(unsigned char rt) {
 
        // Tekken: CDXA fade-out
        if( (cdr.Ctrl & 3) == 3 ) {
-               //cdr.AttenuatorRight[0] = rt;
+               cdr.AttenuatorRight[0] = rt;
        }
 
 
@@ -1365,9 +1523,12 @@ void cdrWrite1(unsigned char rt) {
 
        case CdlSetloc:
                StopReading();
-               cdr.Seeked = FALSE;
                for (i = 0; i < 3; i++)
-                       cdr.SetSector[i] = btoi(cdr.Param[i]);
+                       set_loc[i] = btoi(cdr.Param[i]);
+               i = abs(msf2sec(cdr.SetSector) - msf2sec(set_loc));
+               if (i > 16)
+                       cdr.Seeked = FALSE;
+               memcpy(cdr.SetSector, set_loc, 3);
                cdr.SetSector[3] = 0;
 
                /*
@@ -1477,31 +1638,15 @@ void cdrWrite1(unsigned char rt) {
                /*
                   GameShark CD Player: save time for resume
 
-                  Twisted Metal - World Tour: don't save times for DATA reads
-                  - Only get 1 chance to do this right
-                  */
-               if( cdr.Play && CDR_getStatus(&stat) != -1 ) {
-                       cdr.SetSectorPlay[0] = stat.Time[0];
-                       cdr.SetSectorPlay[1] = stat.Time[1];
-                       cdr.SetSectorPlay[2] = stat.Time[2];
-               }
+                  Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors
+               */
 
                StopCdda();
                StopReading();
                cdr.Ctrl |= 0x80;
                cdr.Stat = NoIntr;
 
-               /*
-                  Gundam Battle Assault 2: much slower (*)
-                  - Fixes boot, gameplay
-
-                  Hokuto no Ken 2: slower
-                  - Fixes intro + subtitles
-
-                  InuYasha - Feudal Fairy Tale: slower
-                  - Fixes battles
-                  */
-               AddIrqQueue(cdr.Cmd, cdReadTime * 3);
+               AddIrqQueue(cdr.Cmd, 0x800);
                break;
 
        case CdlReset:
@@ -1583,7 +1728,11 @@ void cdrWrite1(unsigned char rt) {
                //AddIrqQueue(cdr.Cmd, 0x28);
 
                // Tomb Raider 2 - cdda
-               AddIrqQueue(cdr.Cmd, 0x40);
+               //AddIrqQueue(cdr.Cmd, 0x40);
+
+               // rearmed: the above works in pcsxr-svn, but breaks here
+               // (TOCA world touring cars), perhaps some other code is not merged yet
+               AddIrqQueue(cdr.Cmd, 0x1000);
                break;
 
        case CdlGetTN:
@@ -1690,10 +1839,10 @@ void cdrWrite2(unsigned char rt) {
 
        // Tekken: CDXA fade-out
        if( (cdr.Ctrl & 3) == 2 ) {
-               //cdr.AttenuatorLeft[0] = rt;
+               cdr.AttenuatorLeft[0] = rt;
        }
        else if( (cdr.Ctrl & 3) == 3 ) {
-               //cdr.AttenuatorRight[1] = rt;
+               cdr.AttenuatorRight[1] = rt;
        }
 
 
@@ -1735,7 +1884,7 @@ void cdrWrite3(unsigned char rt) {
 #ifdef CDR_LOG
        CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt);
 #endif
-/*
+
        // Tekken: CDXA fade-out
        if( (cdr.Ctrl & 3) == 2 ) {
                cdr.AttenuatorLeft[1] = rt;
@@ -1747,7 +1896,7 @@ void cdrWrite3(unsigned char rt) {
                        cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] );
 #endif
        }
-*/
+
 
        // GameShark CDX CD Player: Irq timing mania
        if( rt == 0 &&
@@ -1771,8 +1920,18 @@ void cdrWrite3(unsigned char rt) {
                        return;
                }
 
+               // XA streaming - incorrect timing because of this reschedule
+               // - Final Fantasy Tactics
+               // - various other games
+
                if (cdr.Reading && !cdr.ResultReady) {
-                       CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
+                       int left = psxRegs.intCycle[PSXINT_CDREAD].sCycle + psxRegs.intCycle[PSXINT_CDREAD].cycle - psxRegs.cycle;
+                       int time = (cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime;
+                       if (Config.CdrReschedule != 2)
+                       if (left < time / 2 || Config.CdrReschedule) { // rearmed guesswork hack
+                               //printf("-- resched %d -> %d\n", left, time);
+                               CDREAD_INT(time);
+                       }
                }
 
                return;
@@ -1800,6 +1959,7 @@ void cdrWrite3(unsigned char rt) {
 
 void psxDma3(u32 madr, u32 bcr, u32 chcr) {
        u32 cdsize;
+       int size;
        u8 *ptr;
 
 #ifdef CDR_LOG
@@ -1844,15 +2004,12 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) {
                        - CdlPlay
                        - Spams DMA3 and gets buffer overrun
                        */
-
-                       if( (cdr.pTransfer-cdr.Transfer) + cdsize > 2352 )
-                       {
-                               // avoid crash - probably should wrap here
-                               //memcpy(ptr, cdr.pTransfer, cdsize);
-                       }
-                       else
+                       size = CD_FRAMESIZE_RAW - (cdr.pTransfer - cdr.Transfer);
+                       if (size > cdsize)
+                               size = cdsize;
+                       if (size > 0)
                        {
-                               memcpy(ptr, cdr.pTransfer, cdsize);
+                               memcpy(ptr, cdr.pTransfer, size);
                        }
 
                        psxCpu->Clear(madr, cdsize / 4);
@@ -1881,8 +2038,11 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) {
 
 void cdrDmaInterrupt()
 {
-       HW_DMA3_CHCR &= SWAP32(~0x01000000);
-       DMA_INTERRUPT(3);
+       if (HW_DMA3_CHCR & SWAP32(0x01000000))
+       {
+               HW_DMA3_CHCR &= SWAP32(~0x01000000);
+               DMA_INTERRUPT(3);
+       }
 }
 
 void cdrReset() {
@@ -1890,6 +2050,12 @@ void cdrReset() {
        cdr.CurTrack = 1;
        cdr.File = 1;
        cdr.Channel = 1;
+
+       // BIOS player - default values
+       cdr.AttenuatorLeft[0] = 0x80;
+       cdr.AttenuatorLeft[1] = 0x00;
+       cdr.AttenuatorRight[0] = 0x80;
+       cdr.AttenuatorRight[1] = 0x00;
 }
 
 int cdrFreeze(gzFile f, int Mode) {
@@ -1907,9 +2073,13 @@ int cdrFreeze(gzFile f, int Mode) {
 
        gzfreeze(&tmp, sizeof(tmp));
 
-       if (Mode == 0)
+       if (Mode == 0) {
                cdr.pTransfer = cdr.Transfer + tmp;
 
+               if (cdr.Play && !Config.Cdda)
+                       CDR_play(cdr.SetSectorPlay);
+       }
+
        return 0;
 }