cdrom: try to eliminate playback timing drifting
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
index 5dfa658..184d07a 100644 (file)
@@ -241,12 +241,16 @@ static void sec2msf(unsigned int s, u8 *msf) {
        new_dyna_set_event(PSXINT_CDR, eCycle); \
 }
 
-// cdrReadInterrupt
-#define CDREAD_INT(eCycle) { \
+// cdrPlaySeekReadInterrupt
+#define CDRPLAYSEEKREAD_INT(eCycle, isFirst) { \
+       u32 e_ = eCycle; \
        psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
-       psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
-       psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
-       new_dyna_set_event(PSXINT_CDREAD, eCycle); \
+       if (isFirst) \
+               psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
+       else \
+               psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
+       psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
+       new_dyna_set_event_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
 }
 
 // cdrLidSeekInterrupt
@@ -257,14 +261,6 @@ static void sec2msf(unsigned int s, u8 *msf) {
        new_dyna_set_event(PSXINT_CDRLID, eCycle); \
 }
 
-// cdrPlayInterrupt
-#define CDRSEEKPLAY_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 StopReading() { \
        cdr.Reading = 0; \
        psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
@@ -307,7 +303,7 @@ static void setIrq(int log_cmd)
 
 // timing used in this function was taken from tests on real hardware
 // (yes it's slow, but you probably don't want to modify it)
-void cdrLidSeekInterrupt()
+void cdrLidSeekInterrupt(void)
 {
        switch (cdr.DriveState) {
        default:
@@ -584,18 +580,24 @@ static int cdrSeekTime(unsigned char *target)
        return seekTime;
 }
 
-// also handles seek
-void cdrPlayInterrupt()
+static void cdrReadInterrupt(void);
+
+void cdrPlaySeekReadInterrupt(void)
 {
-       if (cdr.StatP & STATUS_SEEK) {
+       if (cdr.Reading) {
+               cdrReadInterrupt();
+               return;
+       }
+
+       if (!cdr.Play && (cdr.StatP & STATUS_SEEK)) {
                if (cdr.Stat) {
                        CDR_LOG_I("cdrom: seek stat hack\n");
-                       CDRSEEKPLAY_INT(0x1000);
+                       CDRPLAYSEEKREAD_INT(0x1000, 1);
                        return;
                }
                SetResultSize(1);
                cdr.StatP |= STATUS_ROTATING;
-               SetPlaySeekRead(cdr.StatP, cdr.Play ? STATUS_PLAY : 0);
+               SetPlaySeekRead(cdr.StatP, 0);
                cdr.Result[0] = cdr.StatP;
                if (cdr.Irq == 0) {
                        cdr.Stat = Complete;
@@ -605,6 +607,7 @@ void cdrPlayInterrupt()
                Find_CurTrack(cdr.SetSectorPlay);
                ReadTrack(cdr.SetSectorPlay);
                cdr.TrackChanged = FALSE;
+               return;
        }
 
        if (!cdr.Play) return;
@@ -612,6 +615,7 @@ void cdrPlayInterrupt()
        CDR_LOG( "CDDA - %d:%d:%d\n",
                cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
 
+       SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
        if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
                StopCdda();
                SetPlaySeekRead(cdr.StatP, 0);
@@ -624,10 +628,10 @@ void cdrPlayInterrupt()
        if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
                cdrPlayInterrupt_Autopause();
 
-       if (CDR_readCDDA && !cdr.Muted && !Config.Cdda) {
+       if (!cdr.Muted && !Config.Cdda) {
                cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
-               if (SPU_playCDDAchannel)
-                       SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW);
+               SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
+               cdr.FirstSector = 0;
        }
 
        cdr.SetSectorPlay[2]++;
@@ -640,13 +644,13 @@ void cdrPlayInterrupt()
                }
        }
 
-       CDRSEEKPLAY_INT(cdReadTime);
+       CDRPLAYSEEKREAD_INT(cdReadTime, 0);
 
        // update for CdlGetlocP/autopause
        generate_subq(cdr.SetSectorPlay);
 }
 
-void cdrInterrupt() {
+void cdrInterrupt(void) {
        u16 Irq = cdr.Irq;
        int no_busy_error = 0;
        int start_rotating = 0;
@@ -756,6 +760,7 @@ void cdrInterrupt() {
                        Find_CurTrack(cdr.SetSectorPlay);
                        ReadTrack(cdr.SetSectorPlay);
                        cdr.TrackChanged = FALSE;
+                       cdr.FirstSector = 1;
 
                        if (!Config.Cdda)
                                CDR_play(cdr.SetSectorPlay);
@@ -765,7 +770,7 @@ void cdrInterrupt() {
                        // BIOS player - set flag again
                        cdr.Play = TRUE;
 
-                       CDRSEEKPLAY_INT(cdReadTime + seekTime);
+                       CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
                        start_rotating = 1;
                        break;
 
@@ -973,7 +978,7 @@ void cdrInterrupt() {
                        Rockman X5 = 0.5-4x
                        - fix capcom logo
                        */
-                       CDRSEEKPLAY_INT(cdReadTime + seekTime);
+                       CDRPLAYSEEKREAD_INT(cdReadTime + seekTime, 1);
                        start_rotating = 1;
                        break;
 
@@ -1018,7 +1023,8 @@ void cdrInterrupt() {
                        }
                        cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
 
-                       strncpy((char *)&cdr.Result[4], "PCSX", 4);
+                       /* This adds the string "PCSX" in Playstation bios boot screen */
+                       memcpy((char *)&cdr.Result[4], "PCSX", 4);
                        cdr.Stat = Complete;
                        break;
 
@@ -1070,40 +1076,7 @@ void cdrInterrupt() {
                        // - fixes new game
                        ReadTrack(cdr.SetSectorPlay);
 
-
-                       // Crusaders of Might and Magic - update getlocl now
-                       // - fixes cutscene speech
-                       {
-                               u8 *buf = CDR_getBuffer();
-                               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
-                       */
-                       
-                       /*      
-                               By nicolasnoble from PCSX Redux :
-                               "It LOOKS like this logic is wrong, therefore disabling it with `&& false` for now.
-                               For "PoPoLoCrois Monogatari II", the game logic will soft lock and will never issue GetLocP to detect
-                               the end of its XA streams, as it seems to assume ReadS will not return a status byte with the SEEK
-                               flag set. I think the reasonning is that since it's invalid to call GetLocP while seeking, the game
-                               tries to protect itself against errors by preventing from issuing a GetLocP while it knows the
-                               last status was "seek". But this makes the logic just softlock as it'll never get a notification
-                               about the fact the drive is done seeking and the read actually started.
-
-                               In other words, this state machine here is probably wrong in assuming the response to ReadS/ReadN is
-                               done right away. It's rather when it's done seeking, and the read has actually started. This probably
-                               requires a bit more work to make sure seek delays are processed properly.
-                               Checked with a few games, this seems to work fine."
-                               
-                               Gameblabla additional notes :
-                               This still needs the "+ seekTime" that PCSX Redux doesn't have for the Driver "retry" mission error.
-                       */
-                       CDREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime);
+                       CDRPLAYSEEKREAD_INT(((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2) + seekTime, 1);
 
                        SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
                        start_rotating = 1;
@@ -1193,15 +1166,13 @@ void cdrAttenuate(s16 *buf, int samples, int stereo)
        }
 }
 
-void cdrReadInterrupt() {
+static void cdrReadInterrupt(void)
+{
        u8 *buf;
 
-       if (!cdr.Reading)
-               return;
-
        if (cdr.Irq || cdr.Stat) {
                CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat);
-               CDREAD_INT(2048);
+               CDRPLAYSEEKREAD_INT(2048, 1);
                return;
        }
 
@@ -1221,7 +1192,7 @@ void cdrReadInterrupt() {
                memset(cdr.Transfer, 0, DATA_SIZE);
                cdr.Stat = DiskError;
                cdr.Result[0] |= STATUS_ERROR;
-               CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
+               setIrq(0x205);
                return;
        }
 
@@ -1249,7 +1220,7 @@ void cdrReadInterrupt() {
                        int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector);
                        if (!ret) {
                                cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
-                               SPU_playADPCMchannel(&cdr.Xa);
+                               SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, cdr.FirstSector);
                                cdr.FirstSector = 0;
                        }
                        else cdr.FirstSector = -1;
@@ -1268,7 +1239,7 @@ void cdrReadInterrupt() {
 
        cdr.Readed = 0;
 
-       CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime);
+       CDRPLAYSEEKREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
 
        /*
        Croc 2: $40 - only FORM1 (*)
@@ -1544,7 +1515,7 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) {
        DMA_INTERRUPT(3);
 }
 
-void cdrDmaInterrupt()
+void cdrDmaInterrupt(void)
 {
        if (HW_DMA3_CHCR & SWAP32(0x01000000))
        {
@@ -1620,6 +1591,8 @@ int cdrFreeze(void *f, int Mode) {
                        Find_CurTrack(cdr.SetSectorPlay);
                        if (!Config.Cdda)
                                CDR_play(cdr.SetSectorPlay);
+                       if (psxRegs.interrupt & (1 << PSXINT_CDRPLAY_OLD))
+                               CDRPLAYSEEKREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime, 1);
                }
 
                if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {
@@ -1640,7 +1613,7 @@ int cdrFreeze(void *f, int Mode) {
        return 0;
 }
 
-void LidInterrupt() {
+void LidInterrupt(void) {
        getCdInfo();
        cdrLidSeekInterrupt();
 }