Remove CdlGetLocP hack
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
index 93d2fa4..76fd545 100644 (file)
 
 cdrStruct cdr;
 static unsigned char *pTransfer;
+static s16 read_buf[CD_FRAMESIZE_RAW/2];
 
 /* CD-ROM magic numbers */
-#define CdlSync        0
+#define CdlSync        0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
 #define CdlNop         1
 #define CdlSetloc      2
 #define CdlPlay        3
@@ -63,7 +64,7 @@ static unsigned char *pTransfer;
 #define CdlDemute      12
 #define CdlSetfilter   13
 #define CdlSetmode     14
-#define CdlGetmode     15
+#define CdlGetparam    15
 #define CdlGetlocL     16
 #define CdlGetlocP     17
 #define CdlReadT       18
@@ -83,8 +84,8 @@ static unsigned char *pTransfer;
 char *CmdName[0x100]= {
     "CdlSync",     "CdlNop",       "CdlSetloc",  "CdlPlay",
     "CdlForward",  "CdlBackward",  "CdlReadN",   "CdlStandby",
-    "CdlStop",     "CdlPause",     "CdlReset",    "CdlMute",
-    "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetmode",
+    "CdlStop",     "CdlPause",     "CdlReset",   "CdlMute",
+    "CdlDemute",   "CdlSetfilter", "CdlSetmode", "CdlGetparam",
     "CdlGetlocL",  "CdlGetlocP",   "CdlReadT",   "CdlGetTN",
     "CdlGetTD",    "CdlSeekL",     "CdlSeekP",   "CdlSetclock",
     "CdlGetclock", "CdlTest",      "CdlID",      "CdlReadS",
@@ -382,7 +383,7 @@ static void ReadTrack(const u8 *time) {
 
        CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
 
-       cdr.RErr = CDR_readTrack(tmp);
+       cdr.NoErr = CDR_readTrack(tmp);
        memcpy(cdr.Prev, tmp, 3);
 
        if (CheckSBI(time))
@@ -431,6 +432,10 @@ static void AddIrqQueue(unsigned short irq, unsigned long ecycle) {
 
 static void cdrPlayInterrupt_Autopause()
 {
+       u32 abs_lev_max = 0;
+       boolean abs_lev_chselect;
+       u32 i;
+
        if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
                CDR_LOG( "CDDA STOP\n" );
 
@@ -446,10 +451,20 @@ static void cdrPlayInterrupt_Autopause()
                StopCdda();
        }
        else if (((cdr.Mode & MODE_REPORT) || cdr.FastForward || cdr.FastBackward)) {
-
+               CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], (u8 *)read_buf);
                cdr.Result[0] = cdr.StatP;
                cdr.Result[1] = cdr.subq.Track;
                cdr.Result[2] = cdr.subq.Index;
+               
+               abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
+               
+               /* 8 is a hack. For accuracy, it should be 588. */
+               for (i = 0; i < 8; i++)
+               {
+                       abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
+               }
+               abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
+               abs_lev_max |= abs_lev_chselect << 15;
 
                if (cdr.subq.Absolute[2] & 0x10) {
                        cdr.Result[3] = cdr.subq.Relative[0];
@@ -462,8 +477,8 @@ static void cdrPlayInterrupt_Autopause()
                        cdr.Result[5] = cdr.subq.Absolute[2];
                }
 
-               cdr.Result[6] = 0;
-               cdr.Result[7] = 0;
+               cdr.Result[6] = abs_lev_max >> 0;
+               cdr.Result[7] = abs_lev_max >> 8;
 
                // Rayman: Logo freeze (resultready + dataready)
                cdr.ResultReady = 1;
@@ -490,7 +505,6 @@ void cdrPlayInterrupt()
                cdr.Seeked = SEEK_DONE;
                if (cdr.Irq == 0) {
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        setIrq();
                }
 
@@ -518,6 +532,12 @@ void cdrPlayInterrupt()
                cdrPlayInterrupt_Autopause();
 
        if (!cdr.Play) return;
+       
+       if (CDR_readCDDA && !cdr.Muted && cdr.Mode & MODE_REPORT) {
+               cdrAttenuate(read_buf, CD_FRAMESIZE_RAW / 4, 1);
+               if (SPU_playCDDAchannel)
+                       SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW);
+       }
 
        cdr.SetSectorPlay[2]++;
        if (cdr.SetSectorPlay[2] == 75) {
@@ -576,10 +596,6 @@ void cdrInterrupt() {
        cdr.Irq = 0;
 
        switch (Irq) {
-               case CdlSync:
-                       // TOOD: sometimes/always return error?
-                       break;
-
                case CdlNop:
                        if (cdr.DriveState != DRIVESTATE_LID_OPEN)
                                cdr.StatP &= ~STATUS_SHELLOPEN;
@@ -592,8 +608,10 @@ void cdrInterrupt() {
                do_CdlPlay:
                case CdlPlay:
                        StopCdda();
-                       /* It would set it to SEEK_DONE*/
-                       cdr.Seeked = SEEK_PENDING;
+                       if (cdr.Seeked == SEEK_PENDING) {
+                               // XXX: wrong, should seek instead..
+                               cdr.Seeked = SEEK_DONE;
+                       }
 
                        cdr.FastBackward = 0;
                        cdr.FastForward = 0;
@@ -642,7 +660,7 @@ void cdrInterrupt() {
                        cdr.TrackChanged = FALSE;
 
                        if (!Config.Cdda)
-                               CDR_play(cdr.SetSectorPlay);
+                               CDR_play();
 
                        // Vib Ribbon: gameplay checks flag
                        cdr.StatP &= ~STATUS_SEEK;
@@ -660,7 +678,6 @@ void cdrInterrupt() {
                case CdlForward:
                        // TODO: error 80 if stopped
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        // GameShark CD Player: Calls 2x + Play 2x
                        cdr.FastForward = 1;
                        cdr.FastBackward = 0;
@@ -685,7 +702,6 @@ void cdrInterrupt() {
 
                case CdlStandby + 0x100:
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlStop:
@@ -713,21 +729,34 @@ void cdrInterrupt() {
                        cdr.StatP &= ~STATUS_ROTATING;
                        cdr.Result[0] = cdr.StatP;
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlPause:
                        /*
                        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 + 0x100, cdReadTime * 3);
+                       /* Gameblabla - Tightening the timings (as taken from Duckstation). 
+                        * The timings from Duckstation are based upon hardware tests.
+                        * Mednafen's timing don't work for Gundam Battle Assault 2 in PAL/50hz mode,
+                        * seems to be timing sensitive as it can depend on the CPU's clock speed.
+                        * 
+                        * We will need to get around this for Bedlam/Rise 2 later...
+                        * */
+                       if (cdr.DriveState != DRIVESTATE_STANDBY)
+                       {
+                               delay = 7000;
+                       }
+                       else
+                       {
+                               delay = (((cdr.Mode & MODE_SPEED) ? 2 : 1) * (1000000));
+                               CDRMISC_INT((cdr.Mode & MODE_SPEED) ? cdReadTime / 2 : cdReadTime);
+                       }
+                       AddIrqQueue(CdlPause + 0x100, delay);
                        cdr.Ctrl |= 0x80;
                        break;
 
@@ -735,7 +764,6 @@ void cdrInterrupt() {
                        cdr.StatP &= ~STATUS_READ;
                        cdr.Result[0] = cdr.StatP;
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlReset:
@@ -748,7 +776,6 @@ void cdrInterrupt() {
 
                case CdlReset + 0x100:
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlMute:
@@ -768,13 +795,12 @@ void cdrInterrupt() {
                        no_busy_error = 1;
                        break;
 
-               case CdlGetmode:
-                       SetResultSize(6);
+               case CdlGetparam:
+                       SetResultSize(5);
                        cdr.Result[1] = cdr.Mode;
-                       cdr.Result[2] = cdr.File;
-                       cdr.Result[3] = cdr.Channel;
-                       cdr.Result[4] = 0;
-                       cdr.Result[5] = 0;
+                       cdr.Result[2] = 0;
+                       cdr.Result[3] = cdr.File;
+                       cdr.Result[4] = cdr.Channel;
                        no_busy_error = 1;
                        break;
 
@@ -786,9 +812,6 @@ void cdrInterrupt() {
                case CdlGetlocP:
                        SetResultSize(8);
                        memcpy(&cdr.Result, &cdr.subq, 8);
-
-                       if (!cdr.Play && !cdr.Reading)
-                               cdr.Result[1] = 0; // HACK?
                        break;
 
                case CdlReadT: // SetSession?
@@ -799,7 +822,6 @@ void cdrInterrupt() {
 
                case CdlReadT + 0x100:
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlGetTN:
@@ -825,7 +847,9 @@ void cdrInterrupt() {
                                cdr.Result[0] = cdr.StatP;
                                cdr.Result[1] = itob(cdr.ResultTD[2]);
                                cdr.Result[2] = itob(cdr.ResultTD[1]);
-                               cdr.Result[3] = itob(cdr.ResultTD[0]);
+                               /* According to Nocash's documentation, the function doesn't care about ff.
+                                * This can be seen also in Mednafen's implementation. */
+                               //cdr.Result[3] = itob(cdr.ResultTD[0]);
                        }
                        break;
 
@@ -897,7 +921,6 @@ void cdrInterrupt() {
                        /* This adds the string "PCSX" in Playstation bios boot screen */
                        memcpy((char *)&cdr.Result[4], "PCSX", 4);
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        break;
 
                case CdlInit:
@@ -910,8 +933,7 @@ void cdrInterrupt() {
                        break;
 
                case CdlGetQ:
-                       // TODO?
-                       CDR_LOG_I("got CdlGetQ\n");
+                       no_busy_error = 1;
                        break;
 
                case CdlReadToc:
@@ -922,7 +944,6 @@ void cdrInterrupt() {
 
                case CdlReadToc + 0x100:
                        cdr.Stat = Complete;
-                       cdr.RErr = 1;
                        no_busy_error = 1;
                        break;
 
@@ -1003,6 +1024,7 @@ void cdrInterrupt() {
                        start_rotating = 1;
                        break;
 
+               case CdlSync:
                default:
                        CDR_LOG_I("Invalid command: %02x\n", Irq);
                        error = ERROR_INVALIDCMD;
@@ -1121,9 +1143,9 @@ void cdrReadInterrupt() {
 
        buf = CDR_getBuffer();
        if (buf == NULL)
-               cdr.RErr = 0;
+               cdr.NoErr = 0;
 
-       if (cdr.RErr == 0) {
+       if (cdr.NoErr == 0) {
                CDR_LOG_I("cdrReadInterrupt() Log: err\n");
                memset(cdr.Transfer, 0, DATA_SIZE);
                cdr.Stat = DiskError;
@@ -1156,6 +1178,19 @@ 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);
+                               /*
+                                * Gameblabla -
+                                * This is a hack for Megaman X4, Castlevania etc...
+                                * that regressed from the new m_locationChanged and CDROM timings changes.
+                                * It is mostly noticeable in Castevania however and the stuttering can be very jarring.
+                                * 
+                                * According to PCSX redux authors, we shouldn't cause a location change if
+                                * the sector difference is too small. 
+                                * I attempted to go with that approach but came empty handed.
+                                * So for now, let's just set cdr.m_locationChanged to false when playing back any ADPCM samples.
+                                * This does not regress Crash Team Racing's intro at least.
+                               */
+                               cdr.m_locationChanged = FALSE;
                                SPU_playADPCMchannel(&cdr.Xa);
                                cdr.FirstSector = 0;
                        }
@@ -1286,17 +1321,29 @@ void cdrWrite1(unsigned char rt) {
 
        switch (cdr.Cmd) {
        case CdlSetloc:
-               for (i = 0; i < 3; i++)
-                       set_loc[i] = btoi(cdr.Param[i]);
+               CDR_LOG("CDROM setloc command (%02X, %02X, %02X)\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
 
-               i = msf2sec(cdr.SetSectorPlay);
-               i = abs(i - msf2sec(set_loc));
-               if (i > 16)
-                       cdr.Seeked = SEEK_PENDING;
+               // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
+               if (((cdr.Param[0] & 0x0F) > 0x09) || (cdr.Param[0] > 0x99) || ((cdr.Param[1] & 0x0F) > 0x09) || (cdr.Param[1] >= 0x60) || ((cdr.Param[2] & 0x0F) > 0x09) || (cdr.Param[2] >= 0x75))
+               {
+                       CDR_LOG("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
+               }
+               else
+               {
+                       for (i = 0; i < 3; i++)
+                       {
+                               set_loc[i] = btoi(cdr.Param[i]);
+                       }
 
-               memcpy(cdr.SetSector, set_loc, 3);
-               cdr.SetSector[3] = 0;
-               cdr.SetlocPending = 1;
+                       i = msf2sec(cdr.SetSectorPlay);
+                       i = abs(i - msf2sec(set_loc));
+                       if (i > 16)
+                               cdr.Seeked = SEEK_PENDING;
+
+                       memcpy(cdr.SetSector, set_loc, 3);
+                       cdr.SetSector[3] = 0;
+                       cdr.SetlocPending = 1;
+               }
                break;
 
        case CdlReadN:
@@ -1566,7 +1613,7 @@ int cdrFreeze(void *f, int Mode) {
 
                        Find_CurTrack(cdr.SetSectorPlay);
                        if (!Config.Cdda)
-                               CDR_play(cdr.SetSectorPlay);
+                               CDR_play();
                }
 
                if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) {