From 9f8b032dc5074e29f2e93ce06651f6da3bb70a86 Mon Sep 17 00:00:00 2001 From: notaz Date: Tue, 25 Jan 2011 16:51:27 +0200 Subject: [PATCH] cdrom: merge most of cdrom.c from latest pcsxr (r62214) most changes pulled are shalma's work --- libpcsxcore/cdrom.c | 1546 ++++++++++++++++++++++-------- libpcsxcore/cdrom.h | 1 + libpcsxcore/new_dynarec/emu_if.c | 4 +- libpcsxcore/psxdma.h | 7 + libpcsxcore/r3000a.c | 12 + libpcsxcore/r3000a.h | 3 + 6 files changed, 1191 insertions(+), 382 deletions(-) diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c index 0e033db7..fff31606 100644 --- a/libpcsxcore/cdrom.c +++ b/libpcsxcore/cdrom.c @@ -23,6 +23,7 @@ #include "cdrom.h" #include "ppf.h" +#include "psxdma.h" cdrStruct cdr; @@ -83,6 +84,36 @@ unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 }; unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F }; unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 }; +// cdr.Stat: +#define NoIntr 0 +#define DataReady 1 +#define Complete 2 +#define Acknowledge 3 +#define DataEnd 4 +#define DiskError 5 + +/* Modes flags */ +#define MODE_SPEED (1<<7) // 0x80 +#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_SF (1<<3) // 0x08 channel on/off +#define MODE_REPORT (1<<2) // 0x04 +#define MODE_AUTOPAUSE (1<<1) // 0x02 +#define MODE_CDDA (1<<0) // 0x01 + +/* Status flags */ +#define STATUS_PLAY (1<<7) // 0x80 +#define STATUS_SEEK (1<<6) // 0x40 +#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_ROTATING (1<<1) // 0x02 +#define STATUS_ERROR (1<<0) // 0x01 + + + // 1x = 75 sectors per second // PSXCLK = 1 sec in the ps // so (PSXCLK / 75) = cdr read time (linuzappz) @@ -91,6 +122,19 @@ unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 }; static struct CdrStat stat; static struct SubQ *subq; +static unsigned int msf2sec(char *msf) { + return ((msf[0] * 60 + msf[1]) * 75) + msf[2]; +} + +static void sec2msf(unsigned int s, char *msf) { + msf[0] = s / 75 / 60; + s = s - msf[0] * 75 * 60; + msf[1] = s / 75; + s = s - msf[1] * 75; + msf[2] = s; +} + + #define CDR_INT(eCycle) { \ psxRegs.interrupt |= (1 << PSXINT_CDR); \ psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \ @@ -105,6 +149,13 @@ static struct SubQ *subq; new_dyna_set_event(PSXINT_CDREAD, eCycle); \ } +#define CDRLID_INT(eCycle) { \ + psxRegs.interrupt |= (1 << PSXINT_CDRLID); \ + psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \ + new_dyna_set_event(PSXINT_CDRLID, eCycle); \ +} + #define StartReading(type, eCycle) { \ cdr.Reading = type; \ cdr.FirstSector = 1; \ @@ -117,14 +168,17 @@ static struct SubQ *subq; cdr.Reading = 0; \ psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \ } \ - cdr.StatP &= ~0x20;\ + cdr.StatP &= ~STATUS_READ;\ } #define StopCdda() { \ if (cdr.Play) { \ if (!Config.Cdda) CDR_stop(); \ - cdr.StatP &= ~0x80; \ + cdr.StatP &= ~STATUS_PLAY; \ cdr.Play = FALSE; \ + cdr.FastForward = 0; \ + cdr.FastBackward = 0; \ + /*SPU_registerCallback( SPUirq );*/ \ } \ } @@ -134,10 +188,213 @@ static struct SubQ *subq; cdr.ResultReady = 1; \ } -static void ReadTrack() { - cdr.Prev[0] = itob(cdr.SetSector[0]); - cdr.Prev[1] = itob(cdr.SetSector[1]); - cdr.Prev[2] = itob(cdr.SetSector[2]); + +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) + cdr.StatP &= ~STATUS_SEEK; + + cdr.LidCheck = 0; + } +} + + +static void Check_Shell( int Irq ) +{ + // check case open/close + if (cdr.LidCheck > 0) + { +#ifdef CDR_LOG + CDR_LOG( "LidCheck\n" ); +#endif + + // $20 = check lid state + if( cdr.LidCheck == 0x20 ) + { + u32 i; + + i = stat.Status; + if (CDR_getStatus(&stat) != -1) + { + if (stat.Type == 0xff) + cdr.Stat = DiskError; + + // case now open + else 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; + + psxHu32ref(0x1070) |= SWAP32((u32)0x4); + + + // begin close-seek-ready cycle + CDRLID_INT( cdReadTime * 3 ); + cdr.LidCheck = 0x30; + + + // GameShark Lite: Wants -exactly- $42, then $02 + // GS CDX 3.3: Wants $11/$80, $13/$80, $01/$00 + } + + // case still closed - wait for recheck + else + { + CDRLID_INT( cdReadTime * 3 ); + cdr.LidCheck = 0x10; + } + } + } + + + // 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; + + + // GS CDX: special return value + if( cdr.StatP & STATUS_SHELLOPEN ) + { + cdr.Result[1] = 0x80; + } + + + if( cdr.Stat == NoIntr ) + cdr.Stat = Acknowledge; + + psxHu32ref(0x1070) |= SWAP32((u32)0x4); + } + } +} + + +void Find_CurTrack() { + cdr.CurTrack = 0; + + if (CDR_getTN(cdr.ResultTN) != -1) { + int lcv; + + for( lcv = 1; lcv <= cdr.ResultTN[1]; lcv++ ) { + if (CDR_getTD((u8)(lcv), cdr.ResultTD) != -1) { + u32 sect1, sect2; + +#ifdef CDR_LOG___0 + CDR_LOG( "curtrack %d %d %d | %d %d %d | %d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], + cdr.ResultTD[2], cdr.ResultTD[1], cdr.ResultTD[0], + cdr.CurTrack ); +#endif + + // find next track boundary - only need m:s accuracy + sect1 = cdr.SetSectorPlay[0] * 60 * 75 + cdr.SetSectorPlay[1] * 75; + sect2 = cdr.ResultTD[2] * 60 * 75 + cdr.ResultTD[1] * 75; + + // Twisted Metal 4 - psx cdda pregap (2-sec) + // - fix in-game music + sect2 -= 75 * 2; + + if( sect1 >= sect2 ) { + cdr.CurTrack++; + continue; + } + } + + break; + } + } +} + +static void ReadTrack( u8 *time ) { + cdr.Prev[0] = itob( time[0] ); + cdr.Prev[1] = itob( time[1] ); + cdr.Prev[2] = itob( time[2] ); #ifdef CDR_LOG CDR_LOG("ReadTrack() Log: KEY *** %x:%x:%x\n", cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); @@ -145,29 +402,140 @@ static void ReadTrack() { cdr.RErr = CDR_readTrack(cdr.Prev); } -// cdr.Stat: -#define NoIntr 0 -#define DataReady 1 -#define Complete 2 -#define Acknowledge 3 -#define DataEnd 4 -#define DiskError 5 void AddIrqQueue(unsigned char irq, unsigned long ecycle) { cdr.Irq = irq; - if (cdr.Stat) { - cdr.eCycle = ecycle; + cdr.eCycle = ecycle; + + // Doom: Force rescheduling + // - Fixes boot + CDR_INT(ecycle); +} + + +void Set_Track() +{ + if (CDR_getTN(cdr.ResultTN) != -1) { + int lcv; + + for( lcv = 1; lcv < cdr.ResultTN[1]; lcv++ ) { + if (CDR_getTD((u8)(lcv), cdr.ResultTD) != -1) { +#ifdef CDR_LOG___0 + CDR_LOG( "settrack %d %d %d | %d %d %d | %d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], + cdr.ResultTD[2], cdr.ResultTD[1], cdr.ResultTD[0], + cdr.CurTrack ); +#endif + + // check if time matches track start (only need min, sec accuracy) + // - m:s:f vs f:s:m + if( cdr.SetSectorPlay[0] == cdr.ResultTD[2] && + cdr.SetSectorPlay[1] == cdr.ResultTD[1] ) { + // skip pregap frames + if( cdr.SetSectorPlay[2] < cdr.ResultTD[0] ) + cdr.SetSectorPlay[2] = cdr.ResultTD[0]; + + break; + } + else if( cdr.SetSectorPlay[0] < cdr.ResultTD[2] ) + break; + } + } + } +} + + +static u8 fake_subq_local[3], fake_subq_real[3], fake_subq_index, fake_subq_change; +void Create_Fake_Subq() +{ + u8 temp_cur[3], temp_next[3], temp_start[3], pregap; + int diff; + + if (CDR_getTN(cdr.ResultTN) == -1) return; + if( cdr.CurTrack+1 <= cdr.ResultTN[1] ) { + pregap = 150; + if( CDR_getTD(cdr.CurTrack+1, cdr.ResultTD) == -1 ) return; + } else { + // last track - cd size + pregap = 0; + if( CDR_getTD(0, cdr.ResultTD) == -1 ) return; + } + + if( cdr.Play == TRUE ) { + temp_cur[0] = cdr.SetSectorPlay[0]; + temp_cur[1] = cdr.SetSectorPlay[1]; + temp_cur[2] = cdr.SetSectorPlay[2]; + } else { + temp_cur[0] = btoi( cdr.Prev[0] ); + temp_cur[1] = btoi( cdr.Prev[1] ); + temp_cur[2] = btoi( cdr.Prev[2] ); + } + + fake_subq_real[0] = temp_cur[0]; + fake_subq_real[1] = temp_cur[1]; + fake_subq_real[2] = temp_cur[2]; + + temp_next[0] = cdr.ResultTD[2]; + temp_next[1] = cdr.ResultTD[1]; + temp_next[2] = cdr.ResultTD[0]; + + + // flag- next track + if( msf2sec(temp_cur) >= msf2sec( temp_next )-pregap ) { + fake_subq_change = 1; + + cdr.CurTrack++; + + // end cd + if( pregap == 0 ) StopCdda(); + } + + ////////////////////////////////////////////////// + ////////////////////////////////////////////////// + + // repair + if( cdr.CurTrack <= cdr.ResultTN[1] ) { + if( CDR_getTD(cdr.CurTrack, cdr.ResultTD) == -1 ) return; + } else { + // last track - cd size + if( CDR_getTD(0, cdr.ResultTD) == -1 ) return; + } + + temp_start[0] = cdr.ResultTD[2]; + temp_start[1] = cdr.ResultTD[1]; + temp_start[2] = cdr.ResultTD[0]; + + +#ifdef CDR_LOG + CDR_LOG( "CDDA FAKE SUB - %d:%d:%d / %d:%d:%d / %d:%d:%d\n", + temp_cur[0], temp_cur[1], temp_cur[2], + temp_start[0], temp_start[1], temp_start[2], + temp_next[0], temp_next[1], temp_next[2]); +#endif + + + + // local time - pregap / real + diff = msf2sec(temp_cur) - msf2sec( temp_start ); + if( diff < 0 ) { + fake_subq_index = 0; + + sec2msf( -diff, fake_subq_local ); } else { - CDR_INT(ecycle); + fake_subq_index = 1; + + sec2msf( diff, fake_subq_local ); } } + void cdrInterrupt() { int i; unsigned char Irq = cdr.Irq; + // Reschedule IRQ if (cdr.Stat) { - CDR_INT(0x1000); + CDR_INT( 0x100 ); return; } @@ -177,7 +545,7 @@ void cdrInterrupt() { switch (Irq) { case CdlSync: SetResultSize(1); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; break; @@ -186,60 +554,169 @@ void cdrInterrupt() { SetResultSize(1); cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; - i = stat.Status; - if (CDR_getStatus(&stat) != -1) { - if (stat.Type == 0xff) cdr.Stat = DiskError; - if (stat.Status & 0x10) { - cdr.Stat = DiskError; - cdr.Result[0] |= 0x11; - cdr.Result[0] &= ~0x02; - } - else if (i & 0x10) { - cdr.StatP |= 0x2; - cdr.Result[0] |= 0x2; - CheckCdrom(); - } - } + + if (cdr.LidCheck == 0) cdr.LidCheck = 0x20; break; case CdlSetloc: cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; break; case CdlPlay: + fake_subq_change = 0; + + if( cdr.Seeked == FALSE ) { + memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 ); + cdr.Seeked = TRUE; + } + + /* + Rayman: detect track changes + - fixes logo freeze + + Twisted Metal 2: skip PREGAP + starting accurate SubQ + - plays tracks without retry play + + Wild 9: skip PREGAP + starting accurate SubQ + - plays tracks without retry play + */ + Set_Track(); + Find_CurTrack(); + ReadTrack( cdr.SetSectorPlay ); + + + // GameShark CD Player: Calls 2x + Play 2x + if( cdr.FastBackward || cdr.FastForward ) { + if( cdr.FastForward ) cdr.FastForward--; + if( cdr.FastBackward ) cdr.FastBackward--; + + if( cdr.FastBackward == 0 && cdr.FastForward == 0 ) { + if( cdr.Play && CDR_getStatus(&stat) != -1 ) { + cdr.SetSectorPlay[0] = stat.Time[0]; + cdr.SetSectorPlay[1] = stat.Time[1]; + cdr.SetSectorPlay[2] = stat.Time[2]; + } + } + } + + + if (!Config.Cdda) { + // BIOS CD Player + // - Pause player, hit Track 01/02/../xx (Setloc issued!!) + + // GameShark CD Player: Resume play + if( cdr.ParamC == 0 ) { +#ifdef CDR_LOG___0 + CDR_LOG( "PLAY Resume @ %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); +#endif + + //CDR_play( cdr.SetSectorPlay ); + } + else + { + // BIOS CD Player: Resume play + if( cdr.Param[0] == 0 ) { +#ifdef CDR_LOG___0 + CDR_LOG( "PLAY Resume T0 @ %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); +#endif + + //CDR_play( cdr.SetSectorPlay ); + } + else { +#ifdef CDR_LOG___0 + CDR_LOG( "PLAY Resume Td @ %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); +#endif + + // BIOS CD Player: Allow track replaying + StopCdda(); + + + cdr.CurTrack = btoi( cdr.Param[0] ); + + if (CDR_getTN(cdr.ResultTN) != -1) { + // check last track + if (cdr.CurTrack > cdr.ResultTN[1]) + cdr.CurTrack = cdr.ResultTN[1]; + + if (CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD) != -1) { + cdr.SetSectorPlay[0] = cdr.ResultTD[2]; + cdr.SetSectorPlay[1] = cdr.ResultTD[1]; + cdr.SetSectorPlay[2] = cdr.ResultTD[0]; + + // reset data + Set_Track(); + Find_CurTrack(); + ReadTrack( cdr.SetSectorPlay ); + + //CDR_play(cdr.SetSectorPlay); + } + } + } + } + } + + + // Vib Ribbon: gameplay checks flag + cdr.StatP &= ~STATUS_SEEK; + + cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; - cdr.StatP |= 0x80; -// if ((cdr.Mode & 0x5) == 0x5) AddIrqQueue(REPPLAY, cdReadTime); + + cdr.StatP |= STATUS_PLAY; + + + // BIOS player - set flag again + cdr.Play = TRUE; + + // TODO? + // CDRPLAY_INT( cdReadTime ); break; - case CdlForward: + case CdlForward: cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; + + + // GameShark CD Player: Calls 2x + Play 2x + if( cdr.FastForward == 0 ) cdr.FastForward = 2; + else cdr.FastForward++; + + cdr.FastBackward = 0; break; - case CdlBackward: + case CdlBackward: cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; + + + // GameShark CD Player: Calls 2x + Play 2x + if( cdr.FastBackward == 0 ) cdr.FastBackward = 2; + else cdr.FastBackward++; + + cdr.FastForward = 0; break; - case CdlStandby: + case CdlStandby: cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Complete; break; @@ -247,112 +724,100 @@ void cdrInterrupt() { case CdlStop: cdr.CmdProcess = 0; SetResultSize(1); - cdr.StatP &= ~0x2; + cdr.StatP &= ~STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Complete; // cdr.Stat = Acknowledge; - // check case open/close -shalma - i = stat.Status; - if (CDR_getStatus(&stat) != -1) { - if (stat.Type == 0xff) cdr.Stat = DiskError; - if (stat.Status & 0x10) { - cdr.Stat = DiskError; - cdr.Result[0] |= 0x11; - cdr.Result[0] &= ~0x02; - } - else if (i & 0x10) { - cdr.StatP |= 0x2; - cdr.Result[0] |= 0x2; - CheckCdrom(); - } - } + if (cdr.LidCheck == 0) cdr.LidCheck = 0x20; break; case CdlPause: SetResultSize(1); cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlPause + 0x20, 0x1000); + cdr.Stat = Acknowledge; + + AddIrqQueue(CdlPause + 0x20, 0x800); cdr.Ctrl |= 0x80; break; case CdlPause + 0x20: SetResultSize(1); - cdr.StatP &= ~0x20; - cdr.StatP |= 0x2; + cdr.StatP &= ~STATUS_READ; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.Stat = Complete; break; - case CdlInit: + case CdlInit: SetResultSize(1); - cdr.StatP = 0x2; + cdr.StatP = STATUS_ROTATING; cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + cdr.Stat = Acknowledge; // if (!cdr.Init) { - AddIrqQueue(CdlInit + 0x20, 0x1000); + AddIrqQueue(CdlInit + 0x20, 0x800); // } break; case CdlInit + 0x20: SetResultSize(1); cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.Stat = Complete; cdr.Init = 1; break; - case CdlMute: + case CdlMute: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; break; - case CdlDemute: + case CdlDemute: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; break; - case CdlSetfilter: + case CdlSetfilter: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - break; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + break; case CdlSetmode: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - break; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + break; - case CdlGetmode: + case CdlGetmode: SetResultSize(6); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Result[1] = cdr.Mode; - cdr.Result[2] = cdr.File; - cdr.Result[3] = cdr.Channel; - cdr.Result[4] = 0; - cdr.Result[5] = 0; - cdr.Stat = Acknowledge; - break; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Result[1] = cdr.Mode; + cdr.Result[2] = cdr.File; + cdr.Result[3] = cdr.Channel; + cdr.Result[4] = 0; + cdr.Result[5] = 0; + cdr.Stat = Acknowledge; + break; - case CdlGetlocL: + case CdlGetlocL: SetResultSize(8); -// for (i = 0; i < 8; i++) -// cdr.Result[i] = itob(cdr.Transfer[i]); - for (i = 0; i < 8; i++) + for (i = 0; i < 8; i++) cdr.Result[i] = cdr.Transfer[i]; - cdr.Stat = Acknowledge; - break; + cdr.Stat = Acknowledge; + break; case CdlGetlocP: - SetResultSize(8); + // GameShark CDX CD Player: uses 17 bytes output (wraps around) + SetResultSize(17); + memset( cdr.Result, 0, 16 ); + subq = (struct SubQ *)CDR_getBufferSub(); if (subq != NULL) { @@ -361,56 +826,69 @@ void cdrInterrupt() { memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3); memcpy(cdr.Result + 5, subq->AbsoluteAddress, 3); - // subQ integrity check + + // subQ integrity check - data only (skip audio) + if( subq->TrackNumber == 1 && stat.Type == 0x01 ) { if (calcCrc((u8 *)subq + 12, 10) != (((u16)subq->CRC[0] << 8) | subq->CRC[1])) { memset(cdr.Result + 2, 0, 3 + 3); // CRC wrong, wipe out time data } + } } else { - cdr.Result[0] = 1; - cdr.Result[1] = 1; + if( cdr.Play == FALSE ) Create_Fake_Subq(); - cdr.Result[2] = btoi(cdr.Prev[0]); - cdr.Result[3] = btoi(cdr.Prev[1]) - 2; - cdr.Result[4] = cdr.Prev[2]; - // m:s adjustment - if ((s8)cdr.Result[3] < 0) { - cdr.Result[3] += 60; - cdr.Result[2] -= 1; - } + // track # / index # + cdr.Result[0] = itob(cdr.CurTrack); + cdr.Result[1] = itob(fake_subq_index); - cdr.Result[2] = itob(cdr.Result[2]); - cdr.Result[3] = itob(cdr.Result[3]); + // local + cdr.Result[2] = itob( fake_subq_local[0] ); + cdr.Result[3] = itob( fake_subq_local[1] ); + cdr.Result[4] = itob( fake_subq_local[2] ); - memcpy(cdr.Result + 5, cdr.Prev, 3); + // absolute + cdr.Result[5] = itob( fake_subq_real[0] ); + cdr.Result[6] = itob( fake_subq_real[1] ); + cdr.Result[7] = itob( fake_subq_real[2] ); } + // redump.org - wipe time + /*if( !cdr.Play && CheckSBI(cdr.Result+5) ) { + memset( cdr.Result+2, 0, 6 ); + } + */ + cdr.Stat = Acknowledge; break; - case CdlGetTN: + case CdlGetTN: + // 5-Star Racing: don't stop CDDA + // + // Vib Ribbon: CD swap + StopReading(); + cdr.CmdProcess = 0; SetResultSize(3); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - if (CDR_getTN(cdr.ResultTN) == -1) { + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + if (CDR_getTN(cdr.ResultTN) == -1) { cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; - } else { - cdr.Stat = Acknowledge; - cdr.Result[1] = itob(cdr.ResultTN[0]); - cdr.Result[2] = itob(cdr.ResultTN[1]); - } - break; + cdr.Result[0] |= STATUS_ERROR; + } else { + cdr.Stat = Acknowledge; + cdr.Result[1] = itob(cdr.ResultTN[0]); + cdr.Result[2] = itob(cdr.ResultTN[1]); + } + break; - case CdlGetTD: + case CdlGetTD: cdr.CmdProcess = 0; cdr.Track = btoi(cdr.Param[0]); SetResultSize(4); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) { cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; + cdr.Result[0] |= STATUS_ERROR; } else { cdr.Stat = Acknowledge; cdr.Result[0] = cdr.StatP; @@ -420,45 +898,71 @@ void cdrInterrupt() { } break; - case CdlSeekL: + case CdlSeekL: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - cdr.Seeked = TRUE; - AddIrqQueue(CdlSeekL + 0x20, 0x1000); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.StatP |= STATUS_SEEK; + cdr.Stat = Acknowledge; + + /* + Crusaders of Might and Magic = 0.5x-4x + - fix cutscene speech start + + Eggs of Steel = 2x-? + - fix new game + + Medievil = ?-4x + - fix cutscene speech + + Rockman X5 = 0.5-4x + - fix capcom logo + */ + AddIrqQueue(CdlSeekL + 0x20, cdReadTime * 4); break; - case CdlSeekL + 0x20: + case CdlSeekL + 0x20: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.StatP &= ~STATUS_SEEK; + cdr.Result[0] = cdr.StatP; + cdr.Seeked = TRUE; + cdr.Stat = Complete; + + + // Mega Man Legends 2: must update read cursor for getlocp + ReadTrack( cdr.SetSector ); break; - case CdlSeekP: + case CdlSeekP: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlSeekP + 0x20, 0x1000); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.StatP |= STATUS_SEEK; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlSeekP + 0x20, cdReadTime * 1); break; - case CdlSeekP + 0x20: + case CdlSeekP + 0x20: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.StatP &= ~STATUS_SEEK; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; + cdr.Seeked = TRUE; + + // GameShark Music Player + memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 ); + + // Tomb Raider 2: must update read cursor for getlocp + Find_CurTrack(); + ReadTrack( cdr.SetSectorPlay ); break; case CdlTest: - cdr.Stat = Acknowledge; - switch (cdr.Param[0]) { - case 0x20: // System Controller ROM Version + cdr.Stat = Acknowledge; + switch (cdr.Param[0]) { + case 0x20: // System Controller ROM Version SetResultSize(4); memcpy(cdr.Result, Test20, 4); break; @@ -470,34 +974,45 @@ void cdrInterrupt() { SetResultSize(8); memcpy(cdr.Result, Test23, 4); break; - } + } break; - case CdlID: + case CdlID: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlID + 0x20, 0x1000); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlID + 0x20, 0x800); break; case CdlID + 0x20: SetResultSize(8); - if (CDR_getStatus(&stat) == -1) { - cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player - cdr.Result[1] = 0x00; // 0x80 leads to the menu in the bios, else loads CD - } - else { - if (stat.Type == 2) { - cdr.Result[0] = 0x08; - cdr.Result[1] = 0x10; - } - else { - cdr.Result[0] = 0x00; - cdr.Result[1] = 0x00; - } - } - cdr.Result[1] |= 0x80; + + if (CDR_getStatus(&stat) == -1) { + cdr.Result[0] = 0x00; // 0x08 and cdr.Result[1]|0x10 : audio cd, enters cd player + cdr.Result[1] = 0x80; // 0x80 leads to the menu in the bios, else loads CD + } + else { + if (stat.Type == 2) { + // Music CD + cdr.Result[0] = 0x08; + cdr.Result[1] = 0x10; + + cdr.Result[1] |= 0x80; + } + else { + // Data CD + if (CdromId[0] == '\0') { + cdr.Result[0] = 0x00; + cdr.Result[1] = 0x80; + } + else { + cdr.Result[0] = 0x08; + cdr.Result[1] = 0x00; + } + } + } + cdr.Result[2] = 0x00; cdr.Result[3] = 0x00; strncpy((char *)&cdr.Result[4], "PCSX", 4); @@ -506,24 +1021,39 @@ void cdrInterrupt() { case CdlReset: SetResultSize(1); - cdr.StatP = 0x2; + cdr.StatP = STATUS_ROTATING; cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + cdr.Stat = Acknowledge; break; - case CdlReadToc: + case CdlReadT: + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlReadT + 0x20, 0x800); + break; + + case CdlReadT + 0x20: + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; + break; + + case CdlReadToc: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlReadToc + 0x20, 0x1000); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlReadToc + 0x20, 0x800); break; - case CdlReadToc + 0x20: + case CdlReadToc + 0x20: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; break; case AUTOPAUSE: @@ -532,54 +1062,59 @@ void cdrInterrupt() { StopCdda(); StopReading(); cdr.OCUP = 0; - cdr.StatP&=~0x20; + cdr.StatP&=~0x20; cdr.StatP|= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = DataEnd; + cdr.Result[0] = cdr.StatP; + cdr.Stat = DataEnd; */ AddIrqQueue(CdlPause, 0x800); break; case READ_ACK: if (!cdr.Reading) return; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; + + // Fighting Force 2 - update subq time immediately + // - fixes new game + ReadTrack( cdr.SetSector ); + + + // Crusaders of Might and Magic - update getlocl now + // - fixes cutscene speech + { + u8 *buf = CDR_getBuffer(); + memcpy(cdr.Transfer, buf, 8); + } + + + /* + Duke Nukem: Land of the Babes - seek then delay read for one frame + - fixes cutscenes + */ + if (!cdr.Seeked) { cdr.Seeked = TRUE; - cdr.StatP |= 0x40; - } - cdr.StatP |= 0x20; - cdr.Stat = Acknowledge; -// CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); - CDREAD_INT(0x80000); - break; + cdr.StatP |= STATUS_SEEK; + cdr.StatP &= ~STATUS_READ; + + // Crusaders of Might and Magic - use short time + // - fix cutscene speech (startup) + + // ??? - use more accurate seek time later + CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1); + } else { + cdr.StatP |= STATUS_READ; + cdr.StatP &= ~STATUS_SEEK; + + CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime * 1); + } - case REPPLAY_ACK: - cdr.Stat = Acknowledge; - cdr.Result[0] = cdr.StatP; SetResultSize(1); - AddIrqQueue(REPPLAY, cdReadTime); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; break; - case REPPLAY: - if ((cdr.Mode & 5) != 5) break; -/* if (CDR_getStatus(&stat) == -1) { - cdr.Result[0] = 0; - cdr.Result[1] = 0; - cdr.Result[2] = 0; - cdr.Result[3] = 0; - cdr.Result[4] = 0; - cdr.Result[5] = 0; - cdr.Result[6] = 0; - cdr.Result[7] = 0; - } else memcpy(cdr.Result, &stat.Track, 8); - cdr.Stat = 1; - SetResultSize(8); - AddIrqQueue(REPPLAY_ACK, cdReadTime); -*/ break; - case 0xff: return; @@ -588,6 +1123,8 @@ void cdrInterrupt() { break; } + Check_Shell( Irq ); + if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) { psxHu32ref(0x1070) |= SWAP32((u32)0x4); } @@ -603,8 +1140,8 @@ void cdrReadInterrupt() { if (!cdr.Reading) return; - if (cdr.Stat) { - CDREAD_INT(0x1000); + if (cdr.Irq || cdr.Stat) { + CDREAD_INT(0x100); return; } @@ -612,13 +1149,13 @@ void cdrReadInterrupt() { CDR_LOG("cdrReadInterrupt() Log: KEY END"); #endif - cdr.OCUP = 1; + cdr.OCUP = 1; SetResultSize(1); - cdr.StatP |= 0x22; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; + cdr.StatP |= STATUS_READ|STATUS_ROTATING; + cdr.StatP &= ~STATUS_SEEK; + cdr.Result[0] = cdr.StatP; - ReadTrack(); + ReadTrack( cdr.SetSector ); buf = CDR_getBuffer(); if (buf == NULL) @@ -630,7 +1167,7 @@ void cdrReadInterrupt() { #endif memset(cdr.Transfer, 0, DATA_SIZE); cdr.Stat = DiskError; - cdr.Result[0] |= 0x01; + cdr.Result[0] |= STATUS_ERROR; CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); return; } @@ -638,49 +1175,78 @@ void cdrReadInterrupt() { memcpy(cdr.Transfer, buf, DATA_SIZE); CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); - cdr.Stat = DataReady; #ifdef CDR_LOG fprintf(emuLog, "cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]); #endif - if ((!cdr.Muted) && (cdr.Mode & 0x40) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA + if ((!cdr.Muted) && (cdr.Mode & MODE_STRSND) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA + // Firemen 2: Multi-XA files - briefings, cutscenes + if( cdr.FirstSector == 1 && (cdr.Mode & MODE_SF)==0 ) { + cdr.File = cdr.Transfer[4 + 0]; + cdr.Channel = cdr.Transfer[4 + 1]; + } + if ((cdr.Transfer[4 + 2] & 0x4) && - ((cdr.Mode & 0x8) ? (cdr.Transfer[4 + 1] == cdr.Channel) : 1) && + (cdr.Transfer[4 + 1] == cdr.Channel) && (cdr.Transfer[4 + 0] == cdr.File)) { int ret = xa_decode_sector(&cdr.Xa, cdr.Transfer+4, cdr.FirstSector); if (!ret) { SPU_playADPCMchannel(&cdr.Xa); cdr.FirstSector = 0; + + + // Crash Team Racing: music, speech + + // signal ADPCM data ready + psxHu32ref(0x1070) |= SWAP32((u32)0x200); } else cdr.FirstSector = -1; } } cdr.SetSector[2]++; - if (cdr.SetSector[2] == 75) { - cdr.SetSector[2] = 0; - cdr.SetSector[1]++; - if (cdr.SetSector[1] == 60) { - cdr.SetSector[1] = 0; - cdr.SetSector[0]++; - } - } - - cdr.Readed = 0; - - if ((cdr.Transfer[4 + 2] & 0x80) && (cdr.Mode & 0x2)) { // EOF + if (cdr.SetSector[2] == 75) { + cdr.SetSector[2] = 0; + cdr.SetSector[1]++; + if (cdr.SetSector[1] == 60) { + cdr.SetSector[1] = 0; + cdr.SetSector[0]++; + } + } + + cdr.Readed = 0; + + // G-Police: Don't autopause ADPCM even if mode set (music) + if ((cdr.Transfer[4 + 2] & 0x80) && (cdr.Mode & MODE_AUTOPAUSE) && + (cdr.Transfer[4 + 2] & 0x4) != 0x4 ) { // EOF #ifdef CDR_LOG CDR_LOG("cdrReadInterrupt() Log: Autopausing read\n"); #endif -// AddIrqQueue(AUTOPAUSE, 0x1000); - AddIrqQueue(CdlPause, 0x1000); +// AddIrqQueue(AUTOPAUSE, 0x2000); + AddIrqQueue(CdlPause, 0x2000); } else { - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime); + } + + /* + Croc 2: $40 - only FORM1 (*) + Judge Dredd: $C8 - only FORM1 (*) + Sim Theme Park - no adpcm at all (zero) + */ + + if( (cdr.Mode & MODE_STRSND) == 0 || (cdr.Transfer[4+2] & 0x4) != 0x4 ) { + cdr.Stat = DataReady; + } else { + // Breath of Fire 3 - fix inn sleeping + // Rockman X5 - no music restart problem + cdr.Stat = NoIntr; } psxHu32ref(0x1070) |= SWAP32((u32)0x4); + + Check_Shell(0); } /* @@ -727,7 +1293,7 @@ void cdrWrite0(unsigned char rt) { #endif cdr.Ctrl = rt | (cdr.Ctrl & ~0x3); - if (rt == 0) { + if (rt == 0) { cdr.ParamP = 0; cdr.ParamC = 0; cdr.ResultReady = 0; @@ -736,7 +1302,9 @@ void cdrWrite0(unsigned char rt) { unsigned char cdrRead1(void) { if (cdr.ResultReady) { // && cdr.Ctrl & 0x1) { - psxHu8(0x1801) = cdr.Result[cdr.ResultP++]; + // GameShark CDX CD Player: uses 17 bytes output (wraps around) + psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf]; + cdr.ResultP++; if (cdr.ResultP == cdr.ResultC) cdr.ResultReady = 0; } else { @@ -754,8 +1322,16 @@ void cdrWrite1(unsigned char rt) { #ifdef CDR_LOG CDR_LOG("cdrWrite1() Log: CD1 write: %x (%s)\n", rt, CmdName[rt]); #endif + + + // Tekken: CDXA fade-out + if( (cdr.Ctrl & 3) == 3 ) { + //cdr.AttenuatorRight[0] = rt; + } + + // psxHu8(0x1801) = rt; - cdr.Cmd = rt; + cdr.Cmd = rt; cdr.OCUP = 0; #ifdef CDRCMD_DEBUG @@ -772,219 +1348,321 @@ void cdrWrite1(unsigned char rt) { if (cdr.Ctrl & 0x1) return; - switch (cdr.Cmd) { + switch (cdr.Cmd) { case CdlSync: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlNop: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + + // Twisted Metal 3 - fix music + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlSetloc: - StopReading(); - cdr.Seeked = FALSE; - for (i = 0; i < 3; i++) - cdr.SetSector[i] = btoi(cdr.Param[i]); + StopReading(); + cdr.Seeked = FALSE; + for (i = 0; i < 3; i++) + cdr.SetSector[i] = btoi(cdr.Param[i]); cdr.SetSector[3] = 0; -/* if ((cdr.SetSector[0] | cdr.SetSector[1] | cdr.SetSector[2]) == 0) { - *(u32 *)cdr.SetSector = *(u32 *)cdr.SetSectorSeek; - }*/ - cdr.Ctrl |= 0x80; + + /* + if ((cdr.SetSector[0] | cdr.SetSector[1] | cdr.SetSector[2]) == 0) { + *(u32 *)cdr.SetSector = *(u32 *)cdr.SetSectorSeek; + }*/ + + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlPlay: - 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); - } - } - } else if (!Config.Cdda) { - CDR_play(cdr.SetSector); + // Vib Ribbon: try same track again + StopCdda(); + + 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); + } } - cdr.Play = TRUE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + } else if (!Config.Cdda) { + CDR_play(cdr.SetSector); + } + + // Vib Ribbon - decoded buffer IRQ for CDDA reading + // - fixes ribbon timing + music CD mode + //TODO? + //CDRDBUF_INT( PSXCLK / 44100 * 0x100 ); + + + cdr.Play = TRUE; + + cdr.StatP |= STATUS_SEEK; + cdr.StatP &= ~STATUS_ROTATING; + + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlForward: - if (cdr.CurTrack < 0xaa) - cdr.CurTrack++; - cdr.Ctrl |= 0x80; + //if (cdr.CurTrack < 0xaa) + // cdr.CurTrack++; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlBackward: - if (cdr.CurTrack > 1) - cdr.CurTrack--; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + //if (cdr.CurTrack > 1) + //cdr.CurTrack--; + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlReadN: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl|= 0x80; - cdr.Stat = NoIntr; - StartReading(1, 0x1000); - break; + cdr.Irq = 0; + StopReading(); + cdr.Ctrl|= 0x80; + cdr.Stat = NoIntr; + StartReading(1, 0x800); + break; case CdlStandby: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + StopCdda(); + StopReading(); + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlStop: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + // GameShark CD Player: Reset CDDA to track start + if( cdr.Play && CDR_getStatus(&stat) != -1 ) { + cdr.SetSectorPlay[0] = stat.Time[0]; + cdr.SetSectorPlay[1] = stat.Time[1]; + cdr.SetSectorPlay[2] = stat.Time[2]; + + Find_CurTrack(); + + + // grab time for current track + CDR_getTD((u8)(cdr.CurTrack), cdr.ResultTD); + + cdr.SetSectorPlay[0] = cdr.ResultTD[2]; + cdr.SetSectorPlay[1] = cdr.ResultTD[1]; + cdr.SetSectorPlay[2] = cdr.ResultTD[0]; + } + + StopCdda(); + StopReading(); + + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlPause: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x80000); - break; + /* + GameShark CD Player: save time for resume + + Twisted Metal - World Tour: don't save times for DATA reads + - Only get 1 chance to do this right + */ + if( cdr.Play && CDR_getStatus(&stat) != -1 ) { + cdr.SetSectorPlay[0] = stat.Time[0]; + cdr.SetSectorPlay[1] = stat.Time[1]; + cdr.SetSectorPlay[2] = stat.Time[2]; + } - case CdlReset: + StopCdda(); + StopReading(); + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + + /* + 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(cdr.Cmd, cdReadTime * 3); + break; + + case CdlReset: case CdlInit: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + StopCdda(); + StopReading(); + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlMute: cdr.Muted = TRUE; - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); + + // Duke Nukem - Time to Kill + // - do not directly set cd-xa volume + //SPU_writeRegister( H_CDLeft, 0x0000 ); + //SPU_writeRegister( H_CDRight, 0x0000 ); break; case CdlDemute: cdr.Muted = FALSE; - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); + + // Duke Nukem - Time to Kill + // - do not directly set cd-xa volume + //SPU_writeRegister( H_CDLeft, 0x7f00 ); + //SPU_writeRegister( H_CDRight, 0x7f00 ); break; case CdlSetfilter: cdr.File = cdr.Param[0]; cdr.Channel = cdr.Param[1]; - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlSetmode: #ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); + CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); #endif cdr.Mode = cdr.Param[0]; - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); + + // Squaresoft on PlayStation 1998 Collector's CD Vol. 1 + // - fixes choppy movie sound + if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 ) + StopCdda(); break; case CdlGetmode: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlGetlocL: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + + // Crusaders of Might and Magic - cutscene speech + AddIrqQueue(cdr.Cmd, 0x800); + break; case CdlGetlocP: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + + // GameShark CDX / Lite Player: pretty narrow time window + // - doesn't always work due to time inprecision + //AddIrqQueue(cdr.Cmd, 0x28); + + // Tomb Raider 2 - cdda + AddIrqQueue(cdr.Cmd, 0x40); break; case CdlGetTN: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + //AddIrqQueue(cdr.Cmd, 0x800); + + // GameShark CDX CD Player: very long time + AddIrqQueue(cdr.Cmd, 0x100000); + break; case CdlGetTD: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlSeekL: // ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + + StopCdda(); + StopReading(); + + break; case CdlSeekP: // ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + + // Tomb Raider 2 - reset cdda + StopCdda(); + StopReading(); + + AddIrqQueue(cdr.Cmd, 0x800); break; + // Destruction Derby: read TOC? GetTD after this + case CdlReadT: + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + break; + case CdlTest: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlID: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; case CdlReadS: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - StartReading(2, 0x1000); - break; + cdr.Irq = 0; + StopReading(); + cdr.Ctrl |= 0x80; + cdr.Stat = NoIntr; + StartReading(2, 0x800); + break; case CdlReadToc: - cdr.Ctrl |= 0x80; + cdr.Ctrl |= 0x80; cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + AddIrqQueue(cdr.Cmd, 0x800); break; default: #ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd); + CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd); #endif - return; - } + return; + } if (cdr.Stat != NoIntr) { psxHu32ref(0x1070) |= SWAP32((u32)0x4); } @@ -1009,10 +1687,20 @@ void cdrWrite2(unsigned char rt) { #ifdef CDR_LOG CDR_LOG("cdrWrite2() Log: CD2 write: %x\n", rt); #endif - if (cdr.Ctrl & 0x1) { + + // Tekken: CDXA fade-out + if( (cdr.Ctrl & 3) == 2 ) { + //cdr.AttenuatorLeft[0] = rt; + } + else if( (cdr.Ctrl & 3) == 3 ) { + //cdr.AttenuatorRight[1] = rt; + } + + + if (cdr.Ctrl & 0x1) { switch (rt) { case 0x07: - cdr.ParamP = 0; + cdr.ParamP = 0; cdr.ParamC = 0; cdr.ResultReady = 1; //0; cdr.Ctrl &= ~3; //cdr.Ctrl = 0; @@ -1022,7 +1710,7 @@ void cdrWrite2(unsigned char rt) { cdr.Reg2 = rt; break; } - } else if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) { + } else if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) { cdr.Param[cdr.ParamP++] = rt; cdr.ParamC++; } @@ -1047,29 +1735,63 @@ void cdrWrite3(unsigned char rt) { #ifdef CDR_LOG CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt); #endif - if (rt == 0x07 && cdr.Ctrl & 0x1) { +/* + // Tekken: CDXA fade-out + if( (cdr.Ctrl & 3) == 2 ) { + cdr.AttenuatorLeft[1] = rt; + } + else if( (cdr.Ctrl & 3) == 3 && rt == 0x20 ) { +#ifdef CDR_LOG + CDR_LOG( "CD-XA Volume: %X %X | %X %X\n", + cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1], + cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] ); +#endif + } +*/ + + // GameShark CDX CD Player: Irq timing mania + if( rt == 0 && + cdr.Irq != 0 && cdr.Irq != 0xff && + cdr.ResultReady == 0 ) { + + // GS CDX: ~0x28 cycle timing - way too precise + if( cdr.Irq == CdlGetlocP ) { + cdrInterrupt(); + + psxRegs.interrupt &= ~(1 << PSXINT_CDR); + } + } + + + if (rt == 0x07 && cdr.Ctrl & 0x1) { cdr.Stat = 0; if (cdr.Irq == 0xff) { cdr.Irq = 0; return; } - if (cdr.Irq) - CDR_INT(cdr.eCycle); - if (cdr.Reading && !cdr.ResultReady) - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + + if (cdr.Reading && !cdr.ResultReady) { + CDREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime); + } return; } + if (rt == 0x80 && !(cdr.Ctrl & 0x1) && cdr.Readed == 0) { cdr.Readed = 1; cdr.pTransfer = cdr.Transfer; switch (cdr.Mode & 0x30) { - case 0x10: + case MODE_SIZE_2328: case 0x00: cdr.pTransfer += 12; break; + + case MODE_SIZE_2340: + cdr.pTransfer += 0; + break; + default: break; } @@ -1096,6 +1818,18 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { cdsize = (bcr & 0xffff) * 4; + // Ape Escape: bcr = 0001 / 0000 + // - fix boot + if( cdsize == 0 ) + { + switch (cdr.Mode & 0x30) { + case 0x00: cdsize = 2048; break; + case MODE_SIZE_2328: cdsize = 2328; break; + case MODE_SIZE_2340: cdsize = 2340; break; + } + } + + ptr = (u8 *)PSXM(madr); if (ptr == NULL) { #ifdef CPU_LOG @@ -1103,10 +1837,37 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { #endif break; } - memcpy(ptr, cdr.pTransfer, cdsize); + + /* + GS CDX: Enhancement CD crash + - Setloc 0:0:0 + - CdlPlay + - Spams DMA3 and gets buffer overrun + */ + + if( (cdr.pTransfer-cdr.Transfer) + cdsize > 2352 ) + { + // avoid crash - probably should wrap here + //memcpy(ptr, cdr.pTransfer, cdsize); + } + else + { + memcpy(ptr, cdr.pTransfer, cdsize); + } + psxCpu->Clear(madr, cdsize / 4); cdr.pTransfer += cdsize; - break; + + + // burst vs normal + if( chcr == 0x11400100 ) { + CDRDMA_INT( (cdsize/4) / 4 ); + } + else if( chcr == 0x11000000 ) { + CDRDMA_INT( (cdsize/4) * 1 ); + } + return; + default: #ifdef CDR_LOG CDR_LOG("psxDma3() Log: Unknown cddma %x\n", chcr); @@ -1118,19 +1879,29 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { DMA_INTERRUPT(3); } +void cdrDmaInterrupt() +{ + HW_DMA3_CHCR &= SWAP32(~0x01000000); + DMA_INTERRUPT(3); +} + void cdrReset() { memset(&cdr, 0, sizeof(cdr)); cdr.CurTrack = 1; cdr.File = 1; cdr.Channel = 1; - memset(&stat, 0, sizeof(stat)); } int cdrFreeze(gzFile f, int Mode) { uintptr_t tmp; - gzfreeze(&cdr, sizeof(cdr)); + if( Mode == 0 ) { + StopCdda(); + } + + gzfreeze(&cdr, sizeof(cdr)); + if (Mode == 1) tmp = cdr.pTransfer - cdr.Transfer; @@ -1141,3 +1912,16 @@ int cdrFreeze(gzFile f, int Mode) { return 0; } + +void LidInterrupt() { + cdr.LidCheck = 0x20; // start checker + + 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); + } +} diff --git a/libpcsxcore/cdrom.h b/libpcsxcore/cdrom.h index abadc4d2..90523c30 100644 --- a/libpcsxcore/cdrom.h +++ b/libpcsxcore/cdrom.h @@ -107,6 +107,7 @@ void cdrReadInterrupt(); void cdrRepplayInterrupt(); void cdrLidSeekInterrupt(); void cdrPlayInterrupt(); +void cdrDmaInterrupt(); unsigned char cdrRead0(void); unsigned char cdrRead1(void); unsigned char cdrRead2(void); diff --git a/libpcsxcore/new_dynarec/emu_if.c b/libpcsxcore/new_dynarec/emu_if.c index a37f7a27..09428748 100644 --- a/libpcsxcore/new_dynarec/emu_if.c +++ b/libpcsxcore/new_dynarec/emu_if.c @@ -61,6 +61,8 @@ static irq_func * const irq_funcs[] = { [PSXINT_SPUDMA] = spuInterrupt, [PSXINT_MDECINDMA] = mdec0Interrupt, [PSXINT_GPUOTCDMA] = gpuotcInterrupt, + [PSXINT_CDRDMA] = cdrDmaInterrupt, + [PSXINT_CDRLID] = cdrLidSeekInterrupt, }; /* local dupe of psxBranchTest, using event_cycles */ @@ -133,7 +135,7 @@ void new_dyna_save(void) void new_dyna_restore(void) { int i; - for (i = 0; i < PSXINT_NEWDRC_CHECK; i++) + for (i = 0; i < PSXINT_COUNT; i++) event_cycles[i] = psxRegs.intCycle[i].sCycle + psxRegs.intCycle[i].cycle; } diff --git a/libpcsxcore/psxdma.h b/libpcsxcore/psxdma.h index e21353f8..28495fa8 100644 --- a/libpcsxcore/psxdma.h +++ b/libpcsxcore/psxdma.h @@ -64,6 +64,13 @@ extern "C" { new_dyna_set_event(PSXINT_GPUOTCDMA, eCycle); \ } +#define CDRDMA_INT(eCycle) { \ + psxRegs.interrupt |= (1 << PSXINT_CDRDMA); \ + psxRegs.intCycle[PSXINT_CDRDMA].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDRDMA].sCycle = psxRegs.cycle; \ + new_dyna_set_event(PSXINT_CDRDMA, eCycle); \ +} + void psxDma2(u32 madr, u32 bcr, u32 chcr); void psxDma3(u32 madr, u32 bcr, u32 chcr); void psxDma4(u32 madr, u32 bcr, u32 chcr); diff --git a/libpcsxcore/r3000a.c b/libpcsxcore/r3000a.c index f92edb72..ba33548d 100644 --- a/libpcsxcore/r3000a.c +++ b/libpcsxcore/r3000a.c @@ -163,6 +163,18 @@ void psxBranchTest() { gpuotcInterrupt(); } } + if (psxRegs.interrupt & (1 << PSXINT_CDRDMA)) { // cdrom + if ((psxRegs.cycle - psxRegs.intCycle[PSXINT_CDRDMA].sCycle) >= psxRegs.intCycle[PSXINT_CDRDMA].cycle) { + psxRegs.interrupt &= ~(1 << PSXINT_CDRDMA); + cdrDmaInterrupt(); + } + } + if (psxRegs.interrupt & (1 << PSXINT_CDRLID)) { // cdr lid states + if ((psxRegs.cycle - psxRegs.intCycle[PSXINT_CDRLID].sCycle) >= psxRegs.intCycle[PSXINT_CDRLID].cycle) { + psxRegs.interrupt &= ~(1 << PSXINT_CDRLID); + cdrLidSeekInterrupt(); + } + } } if (psxHu32(0x1070) & psxHu32(0x1074)) { diff --git a/libpcsxcore/r3000a.h b/libpcsxcore/r3000a.h index c12191bc..dfce1731 100644 --- a/libpcsxcore/r3000a.h +++ b/libpcsxcore/r3000a.h @@ -155,7 +155,10 @@ enum { PSXINT_GPUBUSY, PSXINT_MDECINDMA, PSXINT_GPUOTCDMA, + PSXINT_CDRDMA, PSXINT_NEWDRC_CHECK, + DUMMY2, + PSXINT_CDRLID, PSXINT_COUNT }; -- 2.39.2