cdrom: maybe more accurate lid behavior
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
index 047172c..cc506c9 100644 (file)
@@ -106,7 +106,7 @@ static struct {
        u16 CmdInProgress;
        u8 Irq1Pending;
        u8 unused5;
-       u32 LastReadCycles;
+       u32 LastReadSeekCycles;
 
        u8 unused7;
 
@@ -199,7 +199,7 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
 #define STATUS_READ      (1<<5) // 0x20
 #define STATUS_SHELLOPEN (1<<4) // 0x10
 #define STATUS_UNKNOWN3  (1<<3) // 0x08
-#define STATUS_UNKNOWN2  (1<<2) // 0x04
+#define STATUS_SEEKERROR (1<<2) // 0x04
 #define STATUS_ROTATING  (1<<1) // 0x02
 #define STATUS_ERROR     (1<<0) // 0x01
 
@@ -207,6 +207,7 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
 #define ERROR_NOTREADY   (1<<7) // 0x80
 #define ERROR_INVALIDCMD (1<<6) // 0x40
 #define ERROR_INVALIDARG (1<<5) // 0x20
+#define ERROR_SHELLOPEN  (1<<3) // 0x08
 
 // 1x = 75 sectors per second
 // PSXCLK = 1 sec in the ps
@@ -306,7 +307,7 @@ void cdrLidSeekInterrupt(void)
        default:
        case DRIVESTATE_STANDBY:
                StopCdda();
-               StopReading();
+               //StopReading();
                SetPlaySeekRead(cdr.StatP, 0);
 
                if (CDR_getStatus(&stat) == -1)
@@ -326,11 +327,28 @@ void cdrLidSeekInterrupt(void)
 
                // 02, 12, 10
                if (!(cdr.StatP & STATUS_SHELLOPEN)) {
+                       SetPlaySeekRead(cdr.StatP, 0);
                        cdr.StatP |= STATUS_SHELLOPEN;
 
-                       // could generate error irq here, but real hardware
-                       // only sometimes does that
-                       // (not done when lots of commands are sent?)
+                       // IIRC this sometimes doesn't happen on real hw
+                       // (when lots of commands are sent?)
+                       if (cdr.Reading) {
+                               StopReading();
+                               SetResultSize(2);
+                               cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
+                               cdr.Result[1] = ERROR_SHELLOPEN;
+                               cdr.Stat = DiskError;
+                               setIrq(0x1006);
+                       }
+                       if (cdr.CmdInProgress) {
+                               psxRegs.interrupt &= ~(1 << PSXINT_CDR);
+                               cdr.CmdInProgress = 0;
+                               SetResultSize(2);
+                               cdr.Result[0] = cdr.StatP | STATUS_ERROR;
+                               cdr.Result[1] = ERROR_NOTREADY;
+                               cdr.Stat = DiskError;
+                               setIrq(0x1007);
+                       }
 
                        set_event(PSXINT_CDRLID, cdReadTime * 30);
                        break;
@@ -448,11 +466,11 @@ static int ReadTrack(const u8 *time)
        tmp[1] = itob(time[1]);
        tmp[2] = itob(time[2]);
 
+       CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
+
        if (memcmp(cdr.Prev, tmp, 3) == 0)
                return 1;
 
-       CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
-
        read_ok = CDR_readTrack(tmp);
        if (read_ok)
                memcpy(cdr.Prev, tmp, 3);
@@ -462,12 +480,13 @@ static int ReadTrack(const u8 *time)
 static void UpdateSubq(const u8 *time)
 {
        const struct SubQ *subq;
+       int s = MSF2SECT(time[0], time[1], time[2]);
        u16 crc;
 
-       if (CheckSBI(time))
+       if (CheckSBI(s))
                return;
 
-       subq = (struct SubQ *)CDR_getBufferSub(MSF2SECT(time[0], time[1], time[2]));
+       subq = (struct SubQ *)CDR_getBufferSub(s);
        if (subq != NULL && cdr.CurTrack == 1) {
                crc = calcCrc((u8 *)subq + 12, 10);
                if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) {
@@ -555,19 +574,25 @@ static void cdrPlayInterrupt_Autopause()
                cdr.ReportDelay--;
 }
 
-// LastReadCycles
 static int cdrSeekTime(unsigned char *target)
 {
        int diff = msf2sec(cdr.SetSectorPlay) - msf2sec(target);
-       int pausePenalty, seekTime = abs(diff) * (cdReadTime / 2000);
+       int seekTime = abs(diff) * (cdReadTime / 2000);
+       int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
        seekTime = MAX_VALUE(seekTime, 20000);
 
        // need this stupidly long penalty or else Spyro2 intro desyncs
-       pausePenalty = (s32)(psxRegs.cycle - cdr.LastReadCycles) > cdReadTime * 8 ? cdReadTime * 25 : 0;
-       seekTime += pausePenalty;
+       // note: if misapplied this breaks MGS cutscenes among other things
+       if (cyclesSinceRS > cdReadTime * 50)
+               seekTime += cdReadTime * 25;
+       // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
+       // and then wants some slack time
+       else if (cyclesSinceRS < cdReadTime *3/2)
+               seekTime += cdReadTime;
 
        seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
-       CDR_LOG("seek: %.2f %.2f\n", (float)seekTime / PSXCLK, (float)seekTime / cdReadTime);
+       CDR_LOG("seek: %.2f %.2f (%.2f)\n", (float)seekTime / PSXCLK,
+               (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime);
        return seekTime;
 }
 
@@ -633,7 +658,7 @@ static void msfiSub(u8 *msfi, u32 count)
 
 void cdrPlayReadInterrupt(void)
 {
-       cdr.LastReadCycles = psxRegs.cycle;
+       cdr.LastReadSeekCycles = psxRegs.cycle;
 
        if (cdr.Reading) {
                cdrReadInterrupt();
@@ -658,7 +683,7 @@ void cdrPlayReadInterrupt(void)
        if (!cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
                cdrPlayInterrupt_Autopause();
 
-       if (!cdr.Muted && !Config.Cdda) {
+       if (!cdr.Muted && cdr.Play && !Config.Cdda) {
                cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
                cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
                SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, cdr.FirstSector);
@@ -749,7 +774,7 @@ void cdrInterrupt(void) {
                        break;
 
                case CdlSetloc:
-               case CdlSetloc + CMD_WHILE_NOT_READY:
+               // case CdlSetloc + CMD_WHILE_NOT_READY: // or is it?
                        CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
 
                        // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
@@ -1071,6 +1096,7 @@ void cdrInterrupt(void) {
                                memcpy(cdr.LocL, buf, 8);
                        UpdateSubq(cdr.SetSectorPlay);
                        cdr.TrackChanged = FALSE;
+                       cdr.LastReadSeekCycles = psxRegs.cycle;
                        break;
 
                case CdlTest:
@@ -1190,11 +1216,11 @@ void cdrInterrupt(void) {
                        // FALLTHROUGH
 
                set_error:
-                       CDR_LOG_I("cmd %02x error %02x\n", Cmd, error);
                        SetResultSize(2);
                        cdr.Result[0] = cdr.StatP | STATUS_ERROR;
                        cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
                        cdr.Stat = DiskError;
+                       CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
                        break;
        }