cdrom: rewrite lid code
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
index 17fbf4c..95e1f05 100644 (file)
@@ -108,6 +108,7 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
 #define MODE_STRSND      (1<<6) // 0x40 ADPCM on/off
 #define MODE_SIZE_2340   (1<<5) // 0x20
 #define MODE_SIZE_2328   (1<<4) // 0x10
+#define MODE_SIZE_2048   (0<<4) // 0x00
 #define MODE_SF          (1<<3) // 0x08 channel on/off
 #define MODE_REPORT      (1<<2) // 0x04
 #define MODE_AUTOPAUSE   (1<<1) // 0x02
@@ -124,12 +125,18 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
 #define STATUS_ERROR     (1<<0) // 0x01
 
 
-
 // 1x = 75 sectors per second
 // PSXCLK = 1 sec in the ps
 // so (PSXCLK / 75) = cdr read time (linuzappz)
 #define cdReadTime (PSXCLK / 75)
 
+enum drive_state {
+       DRIVESTATE_STANDBY = 0,
+       DRIVESTATE_LID_OPEN,
+       DRIVESTATE_RESCAN_CD,
+       DRIVESTATE_PREPARE_CD,
+};
+
 // for cdr.Seeked
 enum seeked_state {
        SEEK_PENDING = 0,
@@ -218,165 +225,76 @@ static void setIrq(void)
                psxHu32ref(0x1070) |= SWAP32((u32)0x4);
 }
 
+// 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()
 {
-       // turn back on checking
-       if( cdr.LidCheck == 0x10 )
-       {
-               cdr.LidCheck = 0;
-       }
-
-       // official lid close
-       else if( cdr.LidCheck == 0x30 )
-       {
-               // GS CDX 3.3: $13
-               cdr.StatP |= STATUS_ROTATING;
-
-
-               // GS CDX 3.3 - ~50 getlocp tries
-               CDRLID_INT( cdReadTime * 3 );
-               cdr.LidCheck = 0x40;
-       }
-
-       // turn off ready
-       else if( cdr.LidCheck == 0x40 )
-       {
-               // GS CDX 3.3: $01
-               cdr.StatP &= ~STATUS_SHELLOPEN;
-               cdr.StatP &= ~STATUS_ROTATING;
-
-
-               // GS CDX 3.3 - ~50 getlocp tries
-               CDRLID_INT( cdReadTime * 3 );
-               cdr.LidCheck = 0x50;
-       }
-
-       // now seek
-       else if( cdr.LidCheck == 0x50 )
-       {
-               // GameShark Lite: Start seeking ($42)
-               cdr.StatP |= STATUS_SEEK;
-               cdr.StatP |= STATUS_ROTATING;
-               cdr.StatP &= ~STATUS_ERROR;
-
-
-               CDRLID_INT( cdReadTime * 3 );
-               cdr.LidCheck = 0x60;
-       }
-
-       // done = cd ready
-       else if( cdr.LidCheck == 0x60 )
-       {
-               // GameShark Lite: Seek detection done ($02)
+       switch (cdr.DriveState) {
+       default:
+       case DRIVESTATE_STANDBY:
                cdr.StatP &= ~STATUS_SEEK;
 
-               cdr.LidCheck = 0;
-       }
-}
-
-static void Check_Shell( int Irq )
-{
-       // check case open/close
-       if (cdr.LidCheck > 0)
-       {
-               CDR_LOG( "LidCheck\n" );
+               if (CDR_getStatus(&stat) == -1)
+                       return;
 
-               // $20 = check lid state
-               if( cdr.LidCheck == 0x20 )
+               if (stat.Status & STATUS_SHELLOPEN)
                {
-                       u32 i;
-
-                       i = stat.Status;
-                       if (CDR_getStatus(&stat) != -1)
-                       {
-                               // BIOS hangs + BIOS error messages
-                               //if (stat.Type == 0xff)
-                                       //cdr.Stat = DiskError;
-
-                               // case now open
-                               if (stat.Status & STATUS_SHELLOPEN)
-                               {
-                                       // Vib Ribbon: pre-CD swap
-                                       StopCdda();
-
-
-                                       // GameShark Lite: Death if DiskError happens
-                                       //
-                                       // Vib Ribbon: Needs DiskError for CD swap
-
-                                       if (Irq != CdlNop)
-                                       {
-                                               cdr.Stat = DiskError;
-
-                                               cdr.StatP |= STATUS_ERROR;
-                                               cdr.Result[0] |= STATUS_ERROR;
-                                       }
-
-                                       // GameShark Lite: Wants -exactly- $10
-                                       cdr.StatP |= STATUS_SHELLOPEN;
-                                       cdr.StatP &= ~STATUS_ROTATING;
-
-
-                                       CDRLID_INT( cdReadTime * 3 );
-                                       cdr.LidCheck = 0x10;
-
-
-                                       // GS CDX 3.3 = $11
-                               }
-
-                               // case just closed
-                               else if ( i & STATUS_SHELLOPEN )
-                               {
-                                       cdr.StatP |= STATUS_ROTATING;
-
-                                       CheckCdrom();
-
-
-                                       if( cdr.Stat == NoIntr )
-                                               cdr.Stat = Acknowledge;
-
-                                       setIrq();
+                       StopCdda();
+                       cdr.DriveState = DRIVESTATE_LID_OPEN;
+                       CDRLID_INT(0x800);
+               }
+               break;
 
-                                       // begin close-seek-ready cycle
-                                       CDRLID_INT( cdReadTime * 3 );
-                                       cdr.LidCheck = 0x30;
+       case DRIVESTATE_LID_OPEN:
+               if (CDR_getStatus(&stat) == -1)
+                       stat.Status &= ~STATUS_SHELLOPEN;
 
+               // 02, 12, 10
+               if (!(cdr.StatP & STATUS_SHELLOPEN)) {
+                       StopReading();
+                       cdr.StatP |= STATUS_SHELLOPEN;
 
-                                       // GameShark Lite: Wants -exactly- $42, then $02
-                                       // GS CDX 3.3: Wants $11/$80, $13/$80, $01/$00
-                               }
+                       // could generate error irq here, but real hardware
+                       // only sometimes does that
+                       // (not done when lots of commands are sent?)
 
-                               // case still closed - wait for recheck
-                               else
-                               {
-                                       CDRLID_INT( cdReadTime * 3 );
-                                       cdr.LidCheck = 0x10;
-                               }
-                       }
+                       CDRLID_INT(cdReadTime * 30);
+                       break;
                }
+               else if (cdr.StatP & STATUS_ROTATING) {
+                       cdr.StatP &= ~STATUS_ROTATING;
+               }
+               else if (!(stat.Status & STATUS_SHELLOPEN)) {
+                       // closed now
+                       CheckCdrom();
 
+                       // cdr.StatP STATUS_SHELLOPEN is "sticky"
+                       // and is only cleared by CdlNop
 
-               // GS CDX: clear all values but #1,#2
-               if( (cdr.LidCheck >= 0x30) || (cdr.StatP & STATUS_SHELLOPEN) )
-               {
-                       SetResultSize(16);
-                       memset( cdr.Result, 0, 16 );
-
-                       cdr.Result[0] = cdr.StatP;
+                       cdr.DriveState = DRIVESTATE_RESCAN_CD;
+                       CDRLID_INT(cdReadTime * 105);
+                       break;
+               }
 
+               // recheck for close
+               CDRLID_INT(cdReadTime * 3);
+               break;
 
-                       // GS CDX: special return value
-                       if( cdr.StatP & STATUS_SHELLOPEN )
-                       {
-                               cdr.Result[1] = 0x80;
-                       }
+       case DRIVESTATE_RESCAN_CD:
+               cdr.StatP |= STATUS_ROTATING;
+               cdr.DriveState = DRIVESTATE_PREPARE_CD;
 
+               // this is very long on real hardware, over 6 seconds
+               // make it a bit faster here...
+               CDRLID_INT(cdReadTime * 150);
+               break;
 
-                       if( cdr.Stat == NoIntr )
-                               cdr.Stat = Acknowledge;
+       case DRIVESTATE_PREPARE_CD:
+               cdr.StatP |= STATUS_SEEK;
 
-                       setIrq();
-               }
+               cdr.DriveState = DRIVESTATE_STANDBY;
+               CDRLID_INT(cdReadTime * 26);
+               break;
        }
 }
 
@@ -617,7 +535,7 @@ void cdrInterrupt() {
                        SetResultSize(1);
                        cdr.StatP |= STATUS_ROTATING;
                        cdr.Result[0] = cdr.StatP;
-                       cdr.Stat = Acknowledge; 
+                       cdr.Stat = Acknowledge;
                        break;
 
                case CdlNop:
@@ -625,7 +543,8 @@ void cdrInterrupt() {
                        cdr.Result[0] = cdr.StatP;
                        cdr.Stat = Acknowledge;
 
-                       if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
+                       if (cdr.DriveState != DRIVESTATE_LID_OPEN)
+                               cdr.StatP &= ~STATUS_SHELLOPEN;
                        break;
 
                case CdlSetloc:
@@ -746,8 +665,6 @@ void cdrInterrupt() {
                        cdr.Result[0] = cdr.StatP;
                        cdr.Stat = Complete;
 //                     cdr.Stat = Acknowledge;
-
-                       if (cdr.LidCheck == 0) cdr.LidCheck = 0x20;
                        break;
 
                case CdlPause:
@@ -852,11 +769,6 @@ void cdrInterrupt() {
                        break;
 
                case CdlGetTN:
-                       // 5-Star Racing: don't stop CDDA
-                       //
-                       // Vib Ribbon: CD swap
-                       StopReading();
-
                        cdr.CmdProcess = 0;
                        SetResultSize(3);
                        cdr.StatP |= STATUS_ROTATING;
@@ -946,9 +858,9 @@ void cdrInterrupt() {
                        cdr.Result[2] = 0;
                        cdr.Result[3] = 0;
 
-                       // 0x10 - audio | 0x80 - unlicensed
-                       if (CDR_getStatus(&stat) == -1) {
-                               cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD
+                       // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
+                       if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
+                               cdr.Result[1] = 0xc0;
                        }
                        else {
                                if (stat.Type == 2)
@@ -1050,7 +962,18 @@ void cdrInterrupt() {
                        break;
        }
 
-       Check_Shell( Irq );
+       if (Irq != CdlNop) {
+               switch (cdr.DriveState) {
+               case DRIVESTATE_LID_OPEN:
+               case DRIVESTATE_RESCAN_CD:
+               case DRIVESTATE_PREPARE_CD:
+                       SetResultSize(2);
+                       cdr.Result[0] = cdr.StatP | STATUS_ERROR;
+                       cdr.Result[1] = 0x80;
+                       cdr.Stat = DiskError;
+                       break;
+               }
+       }
 
        cdr.ParamC = 0;
 
@@ -1160,24 +1083,14 @@ void cdrReadInterrupt() {
                        cdr.Channel = cdr.Transfer[4 + 1];
                }
 
-               if ((cdr.Transfer[4 + 2] & 0x4) &&
+               if((cdr.Transfer[4 + 2] & 0x4) &&
                         (cdr.Transfer[4 + 1] == cdr.Channel) &&
-                       (cdr.Transfer[4 + 0] == cdr.File)) {
+                        (cdr.Transfer[4 + 0] == cdr.File)) {
                        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);
                                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
-                               setIrq();
-#endif
                        }
                        else cdr.FirstSector = -1;
                }
@@ -1210,8 +1123,6 @@ void cdrReadInterrupt() {
 
        // update for CdlGetlocP
        ReadTrack(cdr.SetSector);
-
-       Check_Shell(0);
 }
 
 /*
@@ -1338,23 +1249,8 @@ void cdrWrite1(unsigned char rt) {
                // Vib Ribbon: try same track again
                StopCdda();
 
-#if 0
-               if (!cdr.SetSector[0] & !cdr.SetSector[1] & !cdr.SetSector[2]) {
-                       if (CDR_getTN(cdr.ResultTN) != -1) {
-                               if (cdr.CurTrack > cdr.ResultTN[1])
-                                       cdr.CurTrack = cdr.ResultTN[1];
-                               if (CDR_getTD((unsigned char)(cdr.CurTrack), cdr.ResultTD) != -1) {
-                                       int tmp = cdr.ResultTD[2];
-                                       cdr.ResultTD[2] = cdr.ResultTD[0];
-                                       cdr.ResultTD[0] = tmp;
-                                       if (!Config.Cdda) CDR_play(cdr.ResultTD);
-                               }
-                       }
-               }
-#endif
                // Vib Ribbon - decoded buffer IRQ for CDDA reading
                // - fixes ribbon timing + music CD mode
-               //TODO?
                //CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
 
                cdr.Play = TRUE;
@@ -1579,10 +1475,11 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) {
                        // - fix boot
                        if( cdsize == 0 )
                        {
-                               switch (cdr.Mode & 0x30) {
-                                       case 0x00: cdsize = 2048; break;
-                                       case MODE_SIZE_2328: cdsize = 2328; break;
+                               switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
                                        case MODE_SIZE_2340: cdsize = 2340; break;
+                                       case MODE_SIZE_2328: cdsize = 2328; break;
+                                       default:
+                                       case MODE_SIZE_2048: cdsize = 2048; break;
                                }
                        }
 
@@ -1654,6 +1551,9 @@ void cdrReset() {
        cdr.CurTrack = 1;
        cdr.File = 1;
        cdr.Channel = 1;
+       cdr.Reg2 = 0x1f;
+       cdr.Stat = NoIntr;
+       cdr.DriveState = DRIVESTATE_STANDBY;
        pTransfer = cdr.Transfer;
 
        // BIOS player - default values
@@ -1711,17 +1611,7 @@ int cdrFreeze(void *f, int Mode) {
 }
 
 void LidInterrupt() {
-       cdr.LidCheck = 0x20; // start checker
-
        getCdInfo();
-       
        StopCdda();
-       CDRLID_INT( cdReadTime * 3 );
-
-       // generate interrupt if none active - open or close
-       if (cdr.Irq == 0 || cdr.Irq == 0xff) {
-               cdr.Ctrl |= 0x80;
-               cdr.Stat = NoIntr;
-               AddIrqQueue(CdlNop, 0x800);
-       }
+       cdrLidSeekInterrupt();
 }