Lameguy64 / n00brom

Open-source Caetla equivalent for PS1 homebrew development
Mozilla Public License 2.0
38 stars 4 forks source link

SCPH-1000/SCPH-3000 Require SetSession Workaround #4

Open alex-free opened 2 years ago

alex-free commented 2 years ago

Me and another developer MottZilla have discovered that on VC0 and VC1(a) consoles SetSession is bugged. Martin Korth (of no cash PSX fame) has confirmed this to us over email.

Sending SetSession once is not enough. The CDROM BIOS Firmware does not clear the current TOC and update it completely unless you send it enough times. Me and another developer MottZilla have come up with a solution that while timely, actually works and enables these consoles to play CD audio in backup CD-Rs correctly for the first time ever.

Our sources are at https://github.com/alex-free/tonyhax (specifically relevant is https://github.com/alex-free/tonyhax/blob/master/loader/secondary.c)

Our implementation is at https://alex-free.github.io/ps1demoswap

I have a SCPH-1000 rev a and SCPH-1001 rev a which both have a bugged SetSession command, so I can do some testing for you as long as n00bROM can go onto a GameShark.

If you have any questions, please ask! I figured I would drop this info here for you to decide what to do with it.

Lameguy64 commented 2 years ago

I didn't expect anyone to be using n00bROM to notice flaws like this. I thought it was a flop given Unirom remained top dog, even though it is nought more but a bodged together Caetla. Guess I now have an excuse to push an update that implements self-decompression and a forced interlace video hack that isn't working yet, that's been sitting in my working copy for two years.

So I'll just have to bash the SetSession command a few times to make sure the mechacon would load the updated TOC? For a moment I thought this has something to do with my Ez-Swap mechanism, or is that affected as well given it uses SetSession to force a TOC update after the disc swap?

alex-free commented 2 years ago

I didn't expect anyone to be using n00bROM to notice flaws like this. I thought it was a flop given Unirom remained top dog, even though it is nought more but a bodged together Caetla. Guess I now have an excuse to push an update that implements self-decompression and a forced interlace video hack that isn't working yet, that's been sitting in my working copy for two years.

So I'll just have to bash the SetSession command a few times to make sure the mechacon would load the updated TOC? For a moment I thought this has something to do with my Ez-Swap mechanism, or is that affected as well given it uses SetSession to force a TOC update after the disc swap?

It's not that simple. Ez-Swap is affected as is anything using the SetSession command. Here is what needs to happen to use the SetSession command to update the TOC on the 3 oldest CDROM Controller BIOS versions (from https://github.com/alex-free/tonyhax/blob/master/loader/secondary.c):

Use the CDROM test commands to get the CDROM Controller BIOS version. If it is VC0 or VC1 A SetSession is bugged and doesn't work right:

` sscmd = 0x20; cd_command(CD_CMD_TEST,(unsigned char *)&sscmd,1); cd_wait_int(); cd_read_reply(cdcontrollerver);

if(cdcontrollerver[1]==0x09 && cdcontrollerver[2]==0x19 && cdcontrollerver[0]==0x94 && cdcontrollerver[3]==0xC0) 
{
    #if !defined STEALTH
        debug_write("CDROM Controller BIOS Version: September 19th 1994 VC0 A");
    #endif        
    bugged_setsession = 1;
    enable_unlock = 0;
} 
else if(cdcontrollerver[1]==0x11 && cdcontrollerver[2]==0x18 && cdcontrollerver[0]==0x94 && cdcontrollerver[3]==0xC0)
{
    #if !defined STEALTH
        debug_write("CDROM Controller BIOS Version: November 18th 1994 VC0 B");
    #endif        
    bugged_setsession = 1;
    enable_unlock = 0;
}
else if(cdcontrollerver[1]==0x05 && cdcontrollerver[2]==0x16 && cdcontrollerver[0]==0x95 && cdcontrollerver[3]==0xC1)
{
    #if !defined STEALTH
        debug_write("CDROM Controller BIOS Version: May 16th 1995 VC1 A");
    #endif        
    bugged_setsession = 1;
}

`

Set the motor speed to 2x speed.

sscmd = 0x80; cd_command(CD_CMD_SETMODE,(unsigned char *)&sscmd,1);cd_wait_int();

Send SetSession 64 times. This makes sure we have an updated TOC. Check all the track lengths for 00:00 values. It is possible that the TOC is only updated partially and some track data is missing. If we have any 00:00 tracks send SetSession 16 times, and then check the track lengths again. If none are 00:00 the TOC is updated and complete. If there are 00:00 tracks then keep sending 16 SetSessions and then checking until all 00:00 tracks are filled in.

`temp = 64;

    while(1)
    {
            while(1)
            {
                sscmd = 1; cd_command(CD_CMD_SET_SESSION,(unsigned char *)&sscmd,1); cd_wait_int(); cd_wait_int();
                temp--;

                if(temp==0)
                    break;
            }

        get_toc_data();

        if (is_toc_complete())
            break;
        temp = 16; // Now we can check more often, 16 is enough to trigger changes to the TOC now
    }

}

void get_toc_data() // TODO: Clean up? GetTN is a BCD value. This code currently is working though. { unsigned char scmd[4]; unsigned char buf[4]; int n; int a,b; int ti;

cd_command(CD_CMD_GETTN,0,0);
cd_wait_int();
cd_read_reply(buf);

a = buf[2]>>4;
b = buf[2]&0xF;

ti_tn = b + (a*10);
ti_tn = buf[2];     // It's Not BCD?

for(n=0;n<100;n++)
{
    ti_mm[n] = 0;ti_ss[n] = 0;
}
n = 1;
ti= 1;

while(1)
{
    scmd[0] = n;cd_command(CD_CMD_GETTD,(unsigned char *)&scmd,1);cd_wait_int(); // GetTD for Track
    cd_read_reply(buf);ti_mm[ti] = buf[1];ti_ss[ti] = buf[2];
    n++;ti++;

    if( (n&0xF)==0xA )
    {
        n = n - 0xA;
        n = n + 0x10;
    }

    if(n>ti_tn)
        break;
}           

}

bool is_toc_complete() { int i;

int track_count = (ti_tn&0xF) | ((ti_tn>>4)*10);    // Convert ti_tn from BCD to Decimal.

track_count++;
for(i=1;i<track_count;i++)  // Check Tracks 1 to ti_tn to see if any are 00:00.
{
    if( (ti_mm[i] + ti_ss[i]) == 0) 
        return false;
}

return true;

} `

alex-free commented 2 years ago

MottZilla figured out a better work-around that would be very easy to add to your ROM. Send SetSession 2 (change session to non-existent session 2). Wait for 2 interrupts. Send SetSession 1 (now the TOC will actually be read on Session 1 on the SCPH-1000, SCPH-3000, and SCPH-3500 consoles that contain the buggy SetSession command!). Wait for 2 interrupts as before.

Again this is only required by VC0 A, VC0 B, and VC1 CDROM Controller BIOS firmwares. VC1 B and newer have a working SetSession command.

It is up to you if you want to blindly do the above for all Japanese consoles, or if you want to detect the CDROM Controller BIOS firmware version first and do it only for the bugged implementations of SetSession. All in all the SetSession 2->SetSession 1 implementation takes ~30 seconds to re-read the TOC data for the swapped in backup CD-R or import PS1 game.

Once I get a serial cable for my PS1 (all the parts are coming soon, won't be long) I might just try to add this myself and do a pull request. Until then, unless you make a commit I can test, I can't really do anything. But it really is as simple as that.

I have this working in my Tonyhax International fork: https://alex-free.github.io/tonyhax-international . Again the code you may want to look at is in the secondary.c file at https://github.com/alex-free/tonyhax/blob/master/loader/secondary.c .