X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=pcsx_rearmed.git;a=blobdiff_plain;f=libpcsxcore%2Fcdrom.c;h=78db2914d46ba7dabe5c410ff02b1aa0f5972780;hp=92e4b7f2f87b1ee54b14861a87c7f2502297b136;hb=c69642c80a8f3333354a589b5dabdb17f0f23e9b;hpb=ef79bbde537d6b9c745a7d86cb9df1d04c35590d diff --git a/libpcsxcore/cdrom.c b/libpcsxcore/cdrom.c index 92e4b7f2..78db2914 100644 --- a/libpcsxcore/cdrom.c +++ b/libpcsxcore/cdrom.c @@ -23,8 +23,28 @@ #include "cdrom.h" #include "ppf.h" +#include "psxdma.h" + +/* logging */ +#if 0 +#define CDR_LOG SysPrintf +#else +#define CDR_LOG(...) +#endif +#if 0 +#define CDR_LOG_I SysPrintf +#else +#define CDR_LOG_I(...) +#endif +#if 0 +#define CDR_LOG_IO SysPrintf +#else +#define CDR_LOG_IO(...) +#endif +//#define CDR_LOG_CMD_IRQ cdrStruct cdr; +static unsigned char *pTransfer; /* CD-ROM magic numbers */ #define CdlSync 0 @@ -58,14 +78,6 @@ cdrStruct cdr; #define CdlReset 28 #define CdlReadToc 30 -#define AUTOPAUSE 249 -#define READ_ACK 250 -#define READ 251 -#define REPPLAY_ACK 252 -#define REPPLAY 253 -#define ASYNC 254 -/* don't set 255, it's reserved */ - char *CmdName[0x100]= { "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay", "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby", @@ -83,97 +95,526 @@ 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) #define cdReadTime (PSXCLK / 75) +// for cdr.Seeked +enum seeked_state { + SEEK_PENDING = 0, + SEEK_DONE = 1, +}; + static struct CdrStat stat; -static struct SubQ *subq; +static unsigned int msf2sec(const u8 *msf) { + return ((msf[0] * 60 + msf[1]) * 75) + msf[2]; +} + +// for that weird psemu API.. +static unsigned int fsm2sec(const u8 *msf) { + return ((msf[2] * 60 + msf[1]) * 75) + msf[0]; +} + +static void sec2msf(unsigned int s, u8 *msf) { + msf[0] = s / 75 / 60; + s = s - msf[0] * 75 * 60; + msf[1] = s / 75; + s = s - msf[1] * 75; + msf[2] = s; +} + +// cdrInterrupt #define CDR_INT(eCycle) { \ - psxRegs.interrupt |= 0x4; \ - psxRegs.intCycle[2 + 1] = eCycle; \ - psxRegs.intCycle[2] = psxRegs.cycle; } + psxRegs.interrupt |= (1 << PSXINT_CDR); \ + psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \ + new_dyna_set_event(PSXINT_CDR, eCycle); \ +} +// cdrReadInterrupt #define CDREAD_INT(eCycle) { \ - psxRegs.interrupt |= 0x40000; \ - psxRegs.intCycle[2 + 16 + 1] = eCycle; \ - psxRegs.intCycle[2 + 16] = psxRegs.cycle; } - -#define StartReading(type, eCycle) { \ - cdr.Reading = type; \ - cdr.FirstSector = 1; \ - cdr.Readed = 0xff; \ - AddIrqQueue(READ_ACK, eCycle); \ + psxRegs.interrupt |= (1 << PSXINT_CDREAD); \ + psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \ + new_dyna_set_event(PSXINT_CDREAD, eCycle); \ +} + +// cdrLidSeekInterrupt +#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); \ +} + +// cdrPlayInterrupt +#define CDRMISC_INT(eCycle) { \ + psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \ + psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \ + psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \ + new_dyna_set_event(PSXINT_CDRPLAY, eCycle); \ } #define StopReading() { \ if (cdr.Reading) { \ cdr.Reading = 0; \ - psxRegs.interrupt &= ~0x40000; \ + 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 );*/ \ } \ } #define SetResultSize(size) { \ - cdr.ResultP = 0; \ + cdr.ResultP = 0; \ cdr.ResultC = size; \ 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]); +static void setIrq(void) +{ + if (cdr.Stat & cdr.Reg2) + psxHu32ref(0x1070) |= SWAP32((u32)0x4); +} -#ifdef CDR_LOG - CDR_LOG("ReadTrack() Log: KEY *** %x:%x:%x\n", cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); -#endif - cdr.RErr = CDR_readTrack(cdr.Prev); +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; + } } -// cdr.Stat: -#define NoIntr 0 -#define DataReady 1 -#define Complete 2 -#define Acknowledge 3 -#define DataEnd 4 -#define DiskError 5 +static void Check_Shell( int Irq ) +{ + // check case open/close + if (cdr.LidCheck > 0) + { + CDR_LOG( "LidCheck\n" ); + + // $20 = check lid state + if( cdr.LidCheck == 0x20 ) + { + 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(); + + // 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; + + setIrq(); + } + } +} + +static void Find_CurTrack(const u8 *time) +{ + int current, sect; + + current = msf2sec(time); + + for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) { + CDR_getTD(cdr.CurTrack + 1, cdr.ResultTD); + sect = fsm2sec(cdr.ResultTD); + if (sect - current >= 150) + break; + } +} + +static void ReadTrack(const u8 *time) { + unsigned char tmp[3]; + struct SubQ *subq; + u16 crc; + + tmp[0] = itob(time[0]); + tmp[1] = itob(time[1]); + tmp[2] = itob(time[2]); + + if (memcmp(cdr.Prev, tmp, 3) == 0) + return; + + CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]); + + cdr.RErr = CDR_readTrack(tmp); + memcpy(cdr.Prev, tmp, 3); + + cdr.TrackChanged = FALSE; + + if (CheckSBI(time)) + return; + + subq = (struct SubQ *)CDR_getBufferSub(); + if (subq != NULL) { + crc = calcCrc((u8 *)subq + 12, 10); + if (crc == (((u16)subq->CRC[0] << 8) | subq->CRC[1])) { + cdr.subq.Track = subq->TrackNumber; + cdr.subq.Index = subq->IndexNumber; + memcpy(cdr.subq.Relative, subq->TrackRelativeAddress, 3); + memcpy(cdr.subq.Absolute, subq->AbsoluteAddress, 3); + + // .. + 1 is probably wrong, but deals with corrupted + // subq + good checksum + // (how does real thing handle that?) + if (cdr.CurTrack + 1 == btoi(subq->TrackNumber)) { + cdr.CurTrack++; + cdr.TrackChanged = TRUE; + } + } + } + else { + unsigned char start[3], next[3]; + unsigned int this_s, start_s, next_s, pregap; + int relative_s; + + CDR_getTD(cdr.CurTrack, start); + if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) { + pregap = 150; + CDR_getTD(cdr.CurTrack + 1, next); + } + else { + // last track - cd size + pregap = 0; + next[0] = cdr.SetSectorEnd[2]; + next[1] = cdr.SetSectorEnd[1]; + next[2] = cdr.SetSectorEnd[0]; + } + + this_s = msf2sec(time); + start_s = fsm2sec(start); + next_s = fsm2sec(next); + + if (next_s - this_s < pregap) { + cdr.TrackChanged = TRUE; + cdr.CurTrack++; + start_s = next_s; + } + + cdr.subq.Index = 1; + + relative_s = this_s - start_s; + if (relative_s < 0) { + cdr.subq.Index = 0; + relative_s = -relative_s; + } + sec2msf(relative_s, cdr.subq.Relative); + + cdr.subq.Track = itob(cdr.CurTrack); + cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]); + cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]); + cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]); + cdr.subq.Absolute[0] = tmp[0]; + cdr.subq.Absolute[1] = tmp[1]; + cdr.subq.Absolute[2] = tmp[2]; + } + + CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n", + cdr.subq.Track, cdr.subq.Index, + cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2], + cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]); +} + +static void AddIrqQueue(unsigned char irq, unsigned long ecycle) { + if (cdr.Irq != 0) + CDR_LOG_I("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq); -void AddIrqQueue(unsigned char irq, unsigned long ecycle) { cdr.Irq = irq; - if (cdr.Stat) { - cdr.eCycle = ecycle; - } else { - CDR_INT(ecycle); + cdr.eCycle = ecycle; + + CDR_INT(ecycle); +} + +static void cdrPlayInterrupt_Autopause() +{ + if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) { + CDR_LOG( "CDDA STOP\n" ); + + // Magic the Gathering + // - looping territory cdda + + // ...? + //cdr.ResultReady = 1; + //cdr.Stat = DataReady; + cdr.Stat = DataEnd; + setIrq(); + + StopCdda(); + } + else if (cdr.Mode & MODE_REPORT) { + + cdr.Result[0] = cdr.StatP; + cdr.Result[1] = cdr.subq.Track; + cdr.Result[2] = cdr.subq.Index; + + if (cdr.subq.Absolute[2] & 0x10) { + cdr.Result[3] = cdr.subq.Relative[0]; + cdr.Result[4] = cdr.subq.Relative[1] | 0x80; + cdr.Result[5] = cdr.subq.Relative[2]; + } + else { + cdr.Result[3] = cdr.subq.Absolute[0]; + cdr.Result[4] = cdr.subq.Absolute[1]; + cdr.Result[5] = cdr.subq.Absolute[2]; + } + + cdr.Result[6] = 0; + cdr.Result[7] = 0; + + // Rayman: Logo freeze (resultready + dataready) + cdr.ResultReady = 1; + cdr.Stat = DataReady; + + SetResultSize(8); + setIrq(); } } +// also handles seek +void cdrPlayInterrupt() +{ + if (cdr.Seeked == SEEK_PENDING) { + if (cdr.Stat) { + CDR_LOG_I("cdrom: seek stat hack\n"); + CDRMISC_INT(0x1000); + return; + } + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; + cdr.StatP &= ~STATUS_SEEK; + cdr.Result[0] = cdr.StatP; + cdr.Seeked = SEEK_DONE; + if (cdr.Irq == 0) { + cdr.Stat = Complete; + setIrq(); + } + + memcpy(cdr.SetSectorPlay, cdr.SetSector, 4); + Find_CurTrack(cdr.SetSectorPlay); + ReadTrack(cdr.SetSectorPlay); + cdr.TrackChanged = FALSE; + } + + if (!cdr.Play) return; + + CDR_LOG( "CDDA - %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] ); + + if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) { + StopCdda(); + cdr.TrackChanged = TRUE; + } + + if (!cdr.Irq && !cdr.Stat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT))) + cdrPlayInterrupt_Autopause(); + + if (!cdr.Play) return; + + cdr.SetSectorPlay[2]++; + if (cdr.SetSectorPlay[2] == 75) { + cdr.SetSectorPlay[2] = 0; + cdr.SetSectorPlay[1]++; + if (cdr.SetSectorPlay[1] == 60) { + cdr.SetSectorPlay[1] = 0; + cdr.SetSectorPlay[0]++; + } + } + + CDRMISC_INT(cdReadTime); + + // update for CdlGetlocP/autopause + ReadTrack(cdr.SetSectorPlay); +} + void cdrInterrupt() { int i; unsigned char Irq = cdr.Irq; + // Reschedule IRQ if (cdr.Stat) { + CDR_LOG_I("cdrom: stat hack: %02x %x\n", cdr.Irq, cdr.Stat); CDR_INT(0x1000); return; } - cdr.Irq = 0xff; + cdr.Irq = 0; cdr.Ctrl &= ~0x80; switch (Irq) { case CdlSync: SetResultSize(1); - cdr.StatP |= 0x2; + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; cdr.Stat = Acknowledge; break; @@ -182,60 +623,117 @@ 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: + if (cdr.Seeked == SEEK_PENDING) { + // XXX: wrong, should seek instead.. + memcpy( cdr.SetSectorPlay, cdr.SetSector, 4 ); + cdr.Seeked = SEEK_DONE; + } + + // BIOS CD Player + // - Pause player, hit Track 01/02/../xx (Setloc issued!!) + + if (cdr.ParamC == 0 || cdr.Param[0] == 0) { + CDR_LOG("PLAY Resume @ %d:%d:%d\n", + cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]); + } + else + { + int track = btoi( cdr.Param[0] ); + + if (track <= cdr.ResultTN[1]) + cdr.CurTrack = track; + + CDR_LOG("PLAY track %d\n", cdr.CurTrack); + + 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]; + } + } + + /* + 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 + */ + Find_CurTrack(cdr.SetSectorPlay); + ReadTrack(cdr.SetSectorPlay); + cdr.TrackChanged = FALSE; + + if (!Config.Cdda) + 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; + + CDRMISC_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; @@ -243,170 +741,143 @@ 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; + + /* + 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 + 0x20, cdReadTime * 3); 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); - subq = (struct SubQ *)CDR_getBufferSub(); - - if (subq != NULL) { - cdr.Result[0] = subq->TrackNumber; - cdr.Result[1] = subq->IndexNumber; - memcpy(cdr.Result + 2, subq->TrackRelativeAddress, 3); - memcpy(cdr.Result + 5, subq->AbsoluteAddress, 3); - - // subQ integrity check - 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; - - 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; - } + memcpy(&cdr.Result, &cdr.subq, 8); - cdr.Result[2] = itob(cdr.Result[2]); - cdr.Result[3] = itob(cdr.Result[3]); - - memcpy(cdr.Result + 5, cdr.Prev, 3); - } + if (!cdr.Play && !cdr.Reading) + cdr.Result[1] = 0; // HACK? 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; @@ -416,45 +887,35 @@ void cdrInterrupt() { } break; - case CdlSeekL: + case CdlSeekL: + case CdlSeekP: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - cdr.Seeked = TRUE; - AddIrqQueue(CdlSeekL + 0x20, 0x1000); - break; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.StatP |= STATUS_SEEK; + cdr.Stat = Acknowledge; - case CdlSeekL + 0x20: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; - break; + /* + Crusaders of Might and Magic = 0.5x-4x + - fix cutscene speech start - case CdlSeekP: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.StatP |= 0x40; - cdr.Stat = Acknowledge; - AddIrqQueue(CdlSeekP + 0x20, 0x1000); - break; + Eggs of Steel = 2x-? + - fix new game - case CdlSeekP + 0x20: - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.StatP &= ~0x40; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + Medievil = ?-4x + - fix cutscene speech + + Rockman X5 = 0.5-4x + - fix capcom logo + */ + CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4); + cdr.Seeked = SEEK_PENDING; 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; @@ -466,34 +927,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); @@ -502,94 +974,104 @@ void cdrInterrupt() { case CdlReset: SetResultSize(1); - cdr.StatP = 0x2; + cdr.StatP = STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + break; + + case CdlReadT: + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; cdr.Result[0] = cdr.StatP; - cdr.Stat = Acknowledge; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlReadT + 0x20, 0x800); break; - case CdlReadToc: + case CdlReadT + 0x20: 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 = Complete; break; - case CdlReadToc + 0x20: + case CdlReadToc: SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = Complete; + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + AddIrqQueue(CdlReadToc + 0x20, 0x800); break; - case AUTOPAUSE: - cdr.OCUP = 0; -/* SetResultSize(1); - StopCdda(); - StopReading(); - cdr.OCUP = 0; - cdr.StatP&=~0x20; - cdr.StatP|= 0x2; - cdr.Result[0] = cdr.StatP; - cdr.Stat = DataEnd; -*/ AddIrqQueue(CdlPause, 0x800); + case CdlReadToc + 0x20: + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Complete; break; - case READ_ACK: + case CdlReadN: + case CdlReadS: if (!cdr.Reading) return; - SetResultSize(1); - cdr.StatP |= 0x2; - cdr.Result[0] = cdr.StatP; - if (!cdr.Seeked) { - cdr.Seeked = TRUE; - cdr.StatP |= 0x40; + // Fighting Force 2 - update subq time immediately + // - fixes new game + cdr.CurTrack = 1; + ReadTrack(cdr.SetSector); + + + // Crusaders of Might and Magic - update getlocl now + // - fixes cutscene speech + { + u8 *buf = CDR_getBuffer(); + if (buf != NULL) + memcpy(cdr.Transfer, buf, 8); } - cdr.StatP |= 0x20; - cdr.Stat = Acknowledge; -// CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); - CDREAD_INT(0x80000); - break; + /* + Duke Nukem: Land of the Babes - seek then delay read for one frame + - fixes cutscenes + C-12 - Final Resistance - doesn't like seek + */ - case REPPLAY_ACK: - cdr.Stat = Acknowledge; - cdr.Result[0] = cdr.StatP; - SetResultSize(1); - AddIrqQueue(REPPLAY, cdReadTime); - break; + if (cdr.Seeked != SEEK_DONE) { + cdr.StatP |= STATUS_SEEK; + cdr.StatP &= ~STATUS_READ; - 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; + // Crusaders of Might and Magic - use short time + // - fix cutscene speech (startup) - case 0xff: - return; + // ??? - 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); + } + + SetResultSize(1); + cdr.StatP |= STATUS_ROTATING; + cdr.Result[0] = cdr.StatP; + cdr.Stat = Acknowledge; + break; default: cdr.Stat = Complete; break; } - if (cdr.Stat != NoIntr && cdr.Reg2 != 0x18) { - psxHu32ref(0x1070) |= SWAP32((u32)0x4); - } + Check_Shell( Irq ); + + cdr.ParamC = 0; + + setIrq(); -#ifdef CDR_LOG - CDR_LOG("cdrInterrupt() Log: CDR Interrupt IRQ %x\n", Irq); +#ifdef CDR_LOG_CMD_IRQ + SysPrintf("cdrInterrupt() Log: CDR Interrupt IRQ %d %02x: ", + cdr.Stat != NoIntr && cdr.Reg2 != 0x18, Irq); + for (i = 0; i < cdr.ResultC; i++) + SysPrintf("%02x ", cdr.Result[i]); + SysPrintf("\n"); #endif } @@ -599,34 +1081,30 @@ void cdrReadInterrupt() { if (!cdr.Reading) return; - if (cdr.Stat) { + if (cdr.Irq || cdr.Stat) { + CDR_LOG_I("cdrom: read stat hack %02x %x\n", cdr.Irq, cdr.Stat); CDREAD_INT(0x1000); return; } -#ifdef CDR_LOG - 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; + cdr.Seeked = SEEK_DONE; - ReadTrack(); + ReadTrack( cdr.SetSector ); buf = CDR_getBuffer(); if (buf == NULL) cdr.RErr = -1; if (cdr.RErr == -1) { -#ifdef CDR_LOG - fprintf(emuLog, "cdrReadInterrupt() Log: err\n"); -#endif + CDR_LOG_I("cdrReadInterrupt() Log: err\n"); 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; } @@ -634,55 +1112,89 @@ 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 + CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]); + + 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.Muted) && (cdr.Mode & 0x40) && (!Config.Xa) && (cdr.FirstSector != -1)) { // CD-XA 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) { + // only handle attenuator basic channel switch for now + if (cdr.Xa.stereo) { + int i; + if ((cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1]) + && !(cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1])) + { + for (i = 0; i < cdr.Xa.nsamples; i++) + cdr.Xa.pcm[i*2 + 1] = cdr.Xa.pcm[i*2]; + } + else if (!(cdr.AttenuatorLeft[0] | cdr.AttenuatorLeft[1]) + && (cdr.AttenuatorRight[0] | cdr.AttenuatorRight[1])) + { + for (i = 0; i < cdr.Xa.nsamples; i++) + cdr.Xa.pcm[i*2] = cdr.Xa.pcm[i*2 + 1]; + } + } + 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; } } 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 -#ifdef CDR_LOG - CDR_LOG("cdrReadInterrupt() Log: Autopausing read\n"); -#endif -// AddIrqQueue(AUTOPAUSE, 0x1000); - AddIrqQueue(CdlPause, 0x1000); + if (cdr.SetSector[2] == 75) { + cdr.SetSector[2] = 0; + cdr.SetSector[1]++; + if (cdr.SetSector[1] == 60) { + cdr.SetSector[1] = 0; + cdr.SetSector[0]++; + } } - else { - CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime); + + cdr.Readed = 0; + + 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) || !(cdr.Transfer[4+2] & 0x4)) { + cdr.Stat = DataReady; + setIrq(); } - psxHu32ref(0x1070) |= SWAP32((u32)0x4); + + // update for CdlGetlocP + ReadTrack(cdr.SetSector); + + Check_Shell(0); } /* cdrRead0: - bit 0 - 0 REG1 command send / 1 REG1 data read - bit 1 - 0 data transfer finish / 1 data transfer ready/in progress + bit 0,1 - mode bit 2 - unknown bit 3 - unknown bit 4 - unknown @@ -705,56 +1217,51 @@ unsigned char cdrRead0(void) { // What means the 0x10 and the 0x08 bits? I only saw it used by the bios cdr.Ctrl |= 0x18; -#ifdef CDR_LOG - CDR_LOG("cdrRead0() Log: CD0 Read: %x\n", cdr.Ctrl); -#endif + CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl); return psxHu8(0x1800) = cdr.Ctrl; } -/* -cdrWrite0: - 0 - to send a command / 1 - to get the result -*/ - void cdrWrite0(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite0() Log: CD0 write: %x\n", rt); -#endif - cdr.Ctrl = rt | (cdr.Ctrl & ~0x3); + CDR_LOG_IO("cdr w0: %02x\n", rt); - if (rt == 0) { - cdr.ParamP = 0; - cdr.ParamC = 0; - cdr.ResultReady = 0; - } + cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3); } unsigned char cdrRead1(void) { - if (cdr.ResultReady) { // && cdr.Ctrl & 0x1) { - psxHu8(0x1801) = cdr.Result[cdr.ResultP++]; - if (cdr.ResultP == cdr.ResultC) - cdr.ResultReady = 0; - } else { + if ((cdr.ResultP & 0xf) < cdr.ResultC) + psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf]; + else psxHu8(0x1801) = 0; - } -#ifdef CDR_LOG - CDR_LOG("cdrRead1() Log: CD1 Read: %x\n", psxHu8(0x1801)); -#endif + cdr.ResultP++; + if (cdr.ResultP == cdr.ResultC) + cdr.ResultReady = 0; + + CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801)); + return psxHu8(0x1801); } void cdrWrite1(unsigned char rt) { + u8 set_loc[3]; int i; -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: CD1 write: %x (%s)\n", rt, CmdName[rt]); -#endif -// psxHu8(0x1801) = rt; - cdr.Cmd = rt; + CDR_LOG_IO("cdr w1: %02x\n", rt); + + switch (cdr.Ctrl & 3) { + case 0: + break; + case 3: + cdr.AttenuatorRight[1] = rt; + return; + default: + return; + } + + cdr.Cmd = rt; cdr.OCUP = 0; -#ifdef CDRCMD_DEBUG +#ifdef CDR_LOG_CMD_IRQ SysPrintf("cdrWrite1() Log: CD1 write: %x (%s)", rt, CmdName[rt]); if (cdr.ParamC) { SysPrintf(" Param[%d] = {", cdr.ParamC); @@ -766,223 +1273,177 @@ void cdrWrite1(unsigned char rt) { } #endif - if (cdr.Ctrl & 0x1) return; - - switch (cdr.Cmd) { - case CdlSync: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlNop: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlSetloc: - 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; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - 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); + cdr.ResultReady = 0; + cdr.Ctrl |= 0x80; + // cdr.Stat = NoIntr; + AddIrqQueue(cdr.Cmd, 0x800); + + switch (cdr.Cmd) { + case CdlSync: + case CdlNop: + case CdlForward: + case CdlBackward: + case CdlReadT: + case CdlTest: + case CdlID: + case CdlReadToc: + case CdlGetmode: + case CdlGetlocL: + case CdlGetlocP: + case CdlGetTD: + break; + + case CdlSetloc: + StopReading(); + for (i = 0; i < 3; i++) + set_loc[i] = btoi(cdr.Param[i]); + + // FIXME: clean up this SetSector/SetSectorPlay mess, + // there should be single var tracking current sector pos + if (cdr.Play) + i = msf2sec(cdr.SetSectorPlay); + else + i = msf2sec(cdr.SetSector); + i = abs(i - msf2sec(set_loc)); + if (i > 16) + cdr.Seeked = SEEK_PENDING; + + memcpy(cdr.SetSector, set_loc, 3); + cdr.SetSector[3] = 0; + break; + + case CdlPlay: + // 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); + } } - cdr.Play = TRUE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlForward: - if (cdr.CurTrack < 0xaa) - cdr.CurTrack++; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlBackward: - if (cdr.CurTrack > 1) - cdr.CurTrack--; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlReadN: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl|= 0x80; - cdr.Stat = NoIntr; - StartReading(1, 0x1000); - break; - - case CdlStandby: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlStop: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlPause: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x80000); - break; - - case CdlReset: - case CdlInit: - StopCdda(); - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlMute: - cdr.Muted = TRUE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + } +#endif + // 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; + break; + + case CdlReadN: + StopReading(); + cdr.Reading = 1; + cdr.FirstSector = 1; + cdr.Readed = 0xff; + break; + + case CdlStandby: + StopCdda(); + StopReading(); + break; + + case CdlStop: + // GameShark CD Player: Reset CDDA to track start + if (cdr.Play) { + // 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]; + } - case CdlDemute: - cdr.Muted = FALSE; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + StopCdda(); + StopReading(); + break; + + case CdlPause: + /* + GameShark CD Player: save time for resume + + Twisted Metal - World Tour: don't mix Setloc / CdlPlay cursors + */ + + StopCdda(); + StopReading(); + break; + + case CdlReset: + case CdlInit: + cdr.Seeked = SEEK_DONE; + StopCdda(); + StopReading(); + break; + + case CdlMute: + cdr.Muted = TRUE; + // 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; + + // 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.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); break; case CdlSetmode: -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); -#endif - cdr.Mode = cdr.Param[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlGetmode: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]); - case CdlGetlocL: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + cdr.Mode = cdr.Param[0]; - case CdlGetlocP: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); + // Squaresoft on PlayStation 1998 Collector's CD Vol. 1 + // - fixes choppy movie sound + if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 ) + StopCdda(); break; case CdlGetTN: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + //AddIrqQueue(cdr.Cmd, 0x800); - case CdlGetTD: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + // GameShark CDX CD Player: very long time + AddIrqQueue(cdr.Cmd, 0x100000); + break; case CdlSeekL: -// ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - case CdlSeekP: -// ((u32 *)cdr.SetSectorSeek)[0] = ((u32 *)cdr.SetSector)[0]; - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlTest: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; - - case CdlID: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + // Tomb Raider 2 - reset cdda + StopCdda(); + StopReading(); + break; case CdlReadS: - cdr.Irq = 0; - StopReading(); - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - StartReading(2, 0x1000); - break; - - case CdlReadToc: - cdr.Ctrl |= 0x80; - cdr.Stat = NoIntr; - AddIrqQueue(cdr.Cmd, 0x1000); - break; + StopReading(); + cdr.Reading = 2; + cdr.FirstSector = 1; + cdr.Readed = 0xff; + break; - default: -#ifdef CDR_LOG - CDR_LOG("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd); -#endif - return; - } - if (cdr.Stat != NoIntr) { - psxHu32ref(0x1070) |= SWAP32((u32)0x4); + default: + cdr.ParamC = 0; + CDR_LOG_I("cdrWrite1() Log: Unknown command: %x\n", cdr.Cmd); + return; } } @@ -992,80 +1453,81 @@ unsigned char cdrRead2(void) { if (cdr.Readed == 0) { ret = 0; } else { - ret = *cdr.pTransfer++; + ret = *pTransfer++; } -#ifdef CDR_LOG - CDR_LOG("cdrRead2() Log: CD2 Read: %x\n", ret); -#endif + CDR_LOG_IO("cdr r2: %02x\n", ret); return ret; } void cdrWrite2(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite2() Log: CD2 write: %x\n", rt); -#endif - if (cdr.Ctrl & 0x1) { - switch (rt) { - case 0x07: - cdr.ParamP = 0; - cdr.ParamC = 0; - cdr.ResultReady = 1; //0; - cdr.Ctrl &= ~3; //cdr.Ctrl = 0; - break; + CDR_LOG_IO("cdr w2: %02x\n", rt); - default: - cdr.Reg2 = rt; - break; - } - } else if (!(cdr.Ctrl & 0x1) && cdr.ParamP < 8) { - cdr.Param[cdr.ParamP++] = rt; - cdr.ParamC++; + switch (cdr.Ctrl & 3) { + case 0: + if (cdr.ParamC < 8) // FIXME: size and wrapping + cdr.Param[cdr.ParamC++] = rt; + return; + case 1: + cdr.Reg2 = rt; + setIrq(); + return; + case 2: + cdr.AttenuatorLeft[0] = rt; + return; + case 3: + cdr.AttenuatorRight[0] = rt; + return; } } unsigned char cdrRead3(void) { - if (cdr.Stat) { - if (cdr.Ctrl & 0x1) - psxHu8(0x1803) = cdr.Stat | 0xE0; - else - psxHu8(0x1803) = 0xff; - } else { - psxHu8(0x1803) = 0; - } -#ifdef CDR_LOG - CDR_LOG("cdrRead3() Log: CD3 Read: %x\n", psxHu8(0x1803)); -#endif + if (cdr.Ctrl & 0x1) + psxHu8(0x1803) = cdr.Stat | 0xE0; + else + psxHu8(0x1803) = cdr.Reg2 | 0xE0; + + CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803)); return psxHu8(0x1803); } void cdrWrite3(unsigned char rt) { -#ifdef CDR_LOG - CDR_LOG("cdrWrite3() Log: CD3 write: %x\n", rt); -#endif - if (rt == 0x07 && cdr.Ctrl & 0x1) { - cdr.Stat = 0; + CDR_LOG_IO("cdr w3: %02x\n", rt); - 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); + switch (cdr.Ctrl & 3) { + case 0: + break; // transfer + case 1: + cdr.Stat &= ~rt; + if (rt & 0x40) + cdr.ParamC = 0; + return; + case 2: + cdr.AttenuatorLeft[1] = rt; + return; + case 3: + if (rt == 0x20) + CDR_LOG( "CD-XA Volume: %X %X | %X %X\n", + cdr.AttenuatorLeft[0], cdr.AttenuatorLeft[1], + cdr.AttenuatorRight[0], cdr.AttenuatorRight[1] ); return; } - if (rt == 0x80 && !(cdr.Ctrl & 0x1) && cdr.Readed == 0) { + + if ((rt & 0x80) && cdr.Readed == 0) { cdr.Readed = 1; - cdr.pTransfer = cdr.Transfer; + pTransfer = cdr.Transfer; switch (cdr.Mode & 0x30) { - case 0x10: + case MODE_SIZE_2328: case 0x00: - cdr.pTransfer += 12; + pTransfer += 12; break; + + case MODE_SIZE_2340: + pTransfer += 0; + break; + default: break; } @@ -1074,39 +1536,68 @@ void cdrWrite3(unsigned char rt) { void psxDma3(u32 madr, u32 bcr, u32 chcr) { u32 cdsize; + int size; u8 *ptr; -#ifdef CDR_LOG CDR_LOG("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x\n", chcr, madr, bcr); -#endif switch (chcr) { case 0x11000000: case 0x11400100: if (cdr.Readed == 0) { -#ifdef CDR_LOG CDR_LOG("psxDma3() Log: *** DMA 3 *** NOT READY\n"); -#endif break; } 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 CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n"); -#endif break; } - memcpy(ptr, cdr.pTransfer, cdsize); + + /* + GS CDX: Enhancement CD crash + - Setloc 0:0:0 + - CdlPlay + - Spams DMA3 and gets buffer overrun + */ + size = CD_FRAMESIZE_RAW - (pTransfer - cdr.Transfer); + if (size > cdsize) + size = cdsize; + if (size > 0) + { + memcpy(ptr, pTransfer, size); + } + psxCpu->Clear(madr, cdsize / 4); - cdr.pTransfer += cdsize; - break; + pTransfer += cdsize; + + + // 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); -#endif break; } @@ -1114,25 +1605,99 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { DMA_INTERRUPT(3); } +void cdrDmaInterrupt() +{ + if (HW_DMA3_CHCR & SWAP32(0x01000000)) + { + HW_DMA3_CHCR &= SWAP32(~0x01000000); + DMA_INTERRUPT(3); + } +} + +static void getCdInfo(void) +{ + u8 tmp; + + CDR_getTN(cdr.ResultTN); + CDR_getTD(0, cdr.SetSectorEnd); + tmp = cdr.SetSectorEnd[0]; + cdr.SetSectorEnd[0] = cdr.SetSectorEnd[2]; + cdr.SetSectorEnd[2] = tmp; +} + void cdrReset() { memset(&cdr, 0, sizeof(cdr)); cdr.CurTrack = 1; cdr.File = 1; cdr.Channel = 1; + pTransfer = cdr.Transfer; + + // BIOS player - default values + cdr.AttenuatorLeft[0] = 0x80; + cdr.AttenuatorLeft[1] = 0x00; + cdr.AttenuatorRight[0] = 0x00; + cdr.AttenuatorRight[1] = 0x80; + + getCdInfo(); } -int cdrFreeze(gzFile f, int Mode) { - uintptr_t tmp; +int cdrFreeze(void *f, int Mode) { + u32 tmp; + u8 tmpp[3]; + if (Mode == 0 && !Config.Cdda) + CDR_stop(); + + cdr.freeze_ver = 0x63647201; gzfreeze(&cdr, sizeof(cdr)); - - if (Mode == 1) - tmp = cdr.pTransfer - cdr.Transfer; + + if (Mode == 1) { + cdr.ParamP = cdr.ParamC; + tmp = pTransfer - cdr.Transfer; + } gzfreeze(&tmp, sizeof(tmp)); - if (Mode == 0) - cdr.pTransfer = cdr.Transfer + tmp; + if (Mode == 0) { + getCdInfo(); + + pTransfer = cdr.Transfer + tmp; + + // read right sub data + memcpy(tmpp, cdr.Prev, 3); + cdr.Prev[0]++; + ReadTrack(tmpp); + + if (cdr.Play) { + Find_CurTrack(cdr.SetSectorPlay); + if (!Config.Cdda) + CDR_play(cdr.SetSectorPlay); + } + + if ((cdr.freeze_ver & 0xffffff00) != 0x63647200) { + // old versions did not latch Reg2, have to fixup.. + if (cdr.Reg2 == 0) { + SysPrintf("cdrom: fixing up old savestate\n"); + cdr.Reg2 = 7; + } + } + } return 0; } + +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); + } +}