1616 lines
35 KiB
C
1616 lines
35 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
***************************************************************************/
|
|
|
|
/*
|
|
* Handles all CD-ROM registers and functions.
|
|
*/
|
|
|
|
#include "cdrom.h"
|
|
#include "ppf.h"
|
|
#include "psxdma.h"
|
|
|
|
cdrStruct cdr;
|
|
|
|
/* CD-ROM magic numbers */
|
|
#define CdlSync 0
|
|
#define CdlNop 1
|
|
#define CdlSetloc 2
|
|
#define CdlPlay 3
|
|
#define CdlForward 4
|
|
#define CdlBackward 5
|
|
#define CdlReadN 6
|
|
#define CdlStandby 7
|
|
#define CdlStop 8
|
|
#define CdlPause 9
|
|
#define CdlInit 10 // 0xa
|
|
#define CdlMute 11 // 0xb
|
|
#define CdlDemute 12 // 0xc
|
|
#define CdlSetfilter 13 // 0xd
|
|
#define CdlSetmode 14 // 0xe
|
|
#define CdlGetmode 15 // 0xf
|
|
#define CdlGetlocL 16 // 0x10
|
|
#define CdlGetlocP 17 // 0x11
|
|
#define CdlReadT 18 // 0x12
|
|
#define CdlGetTN 19 // 0x13
|
|
#define CdlGetTD 20 // 0x14
|
|
#define CdlSeekL 21 // 0x15
|
|
#define CdlSeekP 22 // 0x16
|
|
#define CdlSetclock 23 // 0x17
|
|
#define CdlGetclock 24 // 0x18
|
|
#define CdlTest 25 // 0x19
|
|
#define CdlID 26 // 0x1a
|
|
#define CdlReadS 27 // 0x1b
|
|
#define CdlReset 28 // 0x1c
|
|
#define CdlGetQ 29 // 0x1d
|
|
#define CdlReadToc 30 // 0x1e
|
|
|
|
char *CmdName[0x100]= {
|
|
"CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
|
|
"CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
|
|
"CdlStop", "CdlPause", "CdlInit", "CdlMute",
|
|
"CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetmode",
|
|
"CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
|
|
"CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
|
|
"CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
|
|
"CdlReset", NULL, "CDlReadToc", NULL
|
|
};
|
|
|
|
unsigned char Test04[] = { 0 };
|
|
unsigned char Test05[] = { 0 };
|
|
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_SIZE_2048 (0<<4) // 0x00
|
|
#define MODE_SF (1<<3) // 0x08 channel on/off
|
|
#define MODE_REPORT (1<<2) // 0x04
|
|
#define MODE_AUTOPAUSE (1<<1) // 0x02
|
|
#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
|
|
|
|
/* Errors */
|
|
#define ERROR_NOTREADY (1<<7) // 0x80
|
|
#define ERROR_INVALIDCMD (1<<6) // 0x40
|
|
#define ERROR_INVALIDARG (1<<5) // 0x20
|
|
|
|
// 1x = 75 sectors per second
|
|
// PSXCLK = 1 sec in the ps
|
|
// so (PSXCLK / 75) = cdr read time (linuzappz)
|
|
#define cdReadTime (PSXCLK / 75)
|
|
|
|
enum drive_state {
|
|
DRIVESTATE_STANDBY = 0,
|
|
DRIVESTATE_LID_OPEN,
|
|
DRIVESTATE_RESCAN_CD,
|
|
DRIVESTATE_PREPARE_CD,
|
|
DRIVESTATE_STOPPED,
|
|
};
|
|
|
|
// for cdr.Seeked
|
|
enum seeked_state {
|
|
SEEK_PENDING = 0,
|
|
SEEK_DONE = 1,
|
|
};
|
|
|
|
static struct CdrStat stat;
|
|
|
|
extern unsigned int msf2sec(const char *msf);
|
|
extern void sec2msf(unsigned int s, const char *msf);
|
|
|
|
// for that weird psemu API..
|
|
static unsigned int fsm2sec(const u8 *msf) {
|
|
return ((msf[2] * 60 + msf[1]) * 75) + msf[0];
|
|
}
|
|
|
|
|
|
extern long CALLBACK ISOinit(void);
|
|
extern void CALLBACK SPUirq(void);
|
|
extern SPUregisterCallback SPU_registerCallback;
|
|
|
|
// A bit of a kludge, but it will get rid of the "macro redefined" warnings
|
|
|
|
#ifdef H_SPUirqAddr
|
|
#undef H_SPUirqAddr
|
|
#endif
|
|
|
|
#ifdef H_SPUaddr
|
|
#undef H_SPUaddr
|
|
#endif
|
|
|
|
#ifdef H_SPUctrl
|
|
#undef H_SPUctrl
|
|
#endif
|
|
|
|
#define H_SPUirqAddr 0x1f801da4
|
|
#define H_SPUaddr 0x1f801da6
|
|
#define H_SPUctrl 0x1f801daa
|
|
#define H_CDLeft 0x1f801db0
|
|
#define H_CDRight 0x1f801db2
|
|
|
|
|
|
// cdrInterrupt
|
|
#define CDR_INT(eCycle) { \
|
|
psxRegs.interrupt |= (1 << PSXINT_CDR); \
|
|
psxRegs.intCycle[PSXINT_CDR].cycle = eCycle; \
|
|
psxRegs.intCycle[PSXINT_CDR].sCycle = psxRegs.cycle; \
|
|
}
|
|
|
|
// cdrReadInterrupt
|
|
#define CDREAD_INT(eCycle) { \
|
|
psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
|
|
psxRegs.intCycle[PSXINT_CDREAD].cycle = eCycle; \
|
|
psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
|
|
}
|
|
|
|
// cdrDecodedBufferInterrupt
|
|
#define CDRDBUF_INT(eCycle) { \
|
|
psxRegs.interrupt |= (1 << PSXINT_CDRDBUF); \
|
|
psxRegs.intCycle[PSXINT_CDRDBUF].cycle = eCycle; \
|
|
psxRegs.intCycle[PSXINT_CDRDBUF].sCycle = psxRegs.cycle; \
|
|
}
|
|
|
|
// cdrLidSeekInterrupt
|
|
#define CDRLID_INT(eCycle) { \
|
|
psxRegs.interrupt |= (1 << PSXINT_CDRLID); \
|
|
psxRegs.intCycle[PSXINT_CDRLID].cycle = eCycle; \
|
|
psxRegs.intCycle[PSXINT_CDRLID].sCycle = psxRegs.cycle; \
|
|
}
|
|
|
|
// cdrPlayInterrupt
|
|
#define CDRMISC_INT(eCycle) { \
|
|
psxRegs.interrupt |= (1 << PSXINT_CDRPLAY); \
|
|
psxRegs.intCycle[PSXINT_CDRPLAY].cycle = eCycle; \
|
|
psxRegs.intCycle[PSXINT_CDRPLAY].sCycle = psxRegs.cycle; \
|
|
}
|
|
|
|
#define StopReading() { \
|
|
if (cdr.Reading) { \
|
|
cdr.Reading = 0; \
|
|
psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
|
|
} \
|
|
cdr.StatP &= ~(STATUS_READ|STATUS_SEEK);\
|
|
}
|
|
|
|
#define StopCdda() { \
|
|
if (cdr.Play) { \
|
|
if (!Config.Cdda) CDR_stop(); \
|
|
cdr.StatP &= ~STATUS_PLAY; \
|
|
cdr.Play = FALSE; \
|
|
cdr.FastForward = 0; \
|
|
cdr.FastBackward = 0; \
|
|
SPU_registerCallback( SPUirq ); \
|
|
} \
|
|
}
|
|
|
|
#define SetResultSize(size) { \
|
|
cdr.ResultP = 0; \
|
|
cdr.ResultC = size; \
|
|
cdr.ResultReady = 1; \
|
|
}
|
|
|
|
static void setIrq(void)
|
|
{
|
|
if (cdr.Stat & cdr.Reg2)
|
|
psxHu32ref(0x1070) |= SWAP32((u32)0x4);
|
|
}
|
|
|
|
static void adjustTransferIndex(void)
|
|
{
|
|
unsigned int bufSize = 0;
|
|
|
|
switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
|
|
case MODE_SIZE_2340: bufSize = 2340; break;
|
|
case MODE_SIZE_2328: bufSize = 12 + 2328; break;
|
|
default:
|
|
case MODE_SIZE_2048: bufSize = 12 + 2048; break;
|
|
}
|
|
|
|
if (cdr.transferIndex >= bufSize)
|
|
cdr.transferIndex -= bufSize;
|
|
}
|
|
|
|
// FIXME: do this in SPU instead
|
|
void cdrDecodedBufferInterrupt()
|
|
{
|
|
#if 0
|
|
return;
|
|
#endif
|
|
|
|
|
|
// ISO reader only
|
|
if( CDR_init != ISOinit ) return;
|
|
|
|
|
|
// check dbuf IRQ still active
|
|
if( cdr.Play == 0 ) return;
|
|
if( (SPU_readRegister( H_SPUctrl ) & 0x40) == 0 ) return;
|
|
if( (SPU_readRegister( H_SPUirqAddr ) * 8) >= 0x800 ) return;
|
|
|
|
|
|
// turn off plugin SPU IRQ decoded buffer handling
|
|
SPU_registerCallback( 0 );
|
|
|
|
|
|
/*
|
|
Vib Ribbon
|
|
|
|
000-3FF = left CDDA
|
|
400-7FF = right CDDA
|
|
|
|
Assume IRQ every wrap
|
|
*/
|
|
|
|
// signal CDDA data ready
|
|
psxHu32ref(0x1070) |= SWAP32((u32)0x200);
|
|
|
|
|
|
// time for next full buffer
|
|
//CDRDBUF_INT( PSXCLK / 44100 * 0x200 );
|
|
CDRDBUF_INT( PSXCLK / 44100 * 0x100 );
|
|
}
|
|
|
|
// timing used in this function was taken from tests on real hardware
|
|
// (yes it's slow, but you probably don't want to modify it)
|
|
void cdrLidSeekInterrupt()
|
|
{
|
|
switch (cdr.DriveState) {
|
|
default:
|
|
case DRIVESTATE_STANDBY:
|
|
cdr.StatP &= ~STATUS_SEEK;
|
|
|
|
if (CDR_getStatus(&stat) == -1)
|
|
return;
|
|
|
|
if (stat.Status & STATUS_SHELLOPEN)
|
|
{
|
|
StopCdda();
|
|
cdr.DriveState = DRIVESTATE_LID_OPEN;
|
|
CDRLID_INT(0x800);
|
|
}
|
|
break;
|
|
|
|
case DRIVESTATE_LID_OPEN:
|
|
if (CDR_getStatus(&stat) == -1)
|
|
stat.Status &= ~STATUS_SHELLOPEN;
|
|
|
|
// 02, 12, 10
|
|
if (!(cdr.StatP & STATUS_SHELLOPEN)) {
|
|
StopReading();
|
|
cdr.StatP |= STATUS_SHELLOPEN;
|
|
|
|
// could generate error irq here, but real hardware
|
|
// only sometimes does that
|
|
// (not done when lots of commands are sent?)
|
|
|
|
CDRLID_INT(cdReadTime * 30);
|
|
break;
|
|
}
|
|
else if (cdr.StatP & STATUS_ROTATING) {
|
|
cdr.StatP &= ~STATUS_ROTATING;
|
|
}
|
|
else if (!(stat.Status & STATUS_SHELLOPEN)) {
|
|
// closed now
|
|
CheckCdrom();
|
|
|
|
// cdr.StatP STATUS_SHELLOPEN is "sticky"
|
|
// and is only cleared by CdlNop
|
|
|
|
cdr.DriveState = DRIVESTATE_RESCAN_CD;
|
|
CDRLID_INT(cdReadTime * 105);
|
|
break;
|
|
}
|
|
|
|
// recheck for close
|
|
CDRLID_INT(cdReadTime * 3);
|
|
break;
|
|
|
|
case DRIVESTATE_RESCAN_CD:
|
|
cdr.StatP |= STATUS_ROTATING;
|
|
cdr.DriveState = DRIVESTATE_PREPARE_CD;
|
|
|
|
// this is very long on real hardware, over 6 seconds
|
|
// make it a bit faster here...
|
|
CDRLID_INT(cdReadTime * 150);
|
|
break;
|
|
|
|
case DRIVESTATE_PREPARE_CD:
|
|
cdr.StatP |= STATUS_SEEK;
|
|
|
|
cdr.DriveState = DRIVESTATE_STANDBY;
|
|
CDRLID_INT(cdReadTime * 26);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("Find_CurTrack *** %02d %02d\n", cdr.CurTrack, current);
|
|
#endif
|
|
}
|
|
|
|
static void generate_subq(const u8 *time)
|
|
{
|
|
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);
|
|
|
|
cdr.TrackChanged = FALSE;
|
|
|
|
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] = itob(time[0]);
|
|
cdr.subq.Absolute[1] = itob(time[1]);
|
|
cdr.subq.Absolute[2] = itob(time[2]);
|
|
}
|
|
|
|
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;
|
|
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("ReadTrack *** %02x:%02x:%02x\n", tmp[0], tmp[1], tmp[2]);
|
|
#endif
|
|
|
|
cdr.RErr = CDR_readTrack(tmp);
|
|
memcpy(cdr.Prev, tmp, 3);
|
|
|
|
subq = (struct SubQ *)CDR_getBufferSub();
|
|
if (subq != NULL && cdr.CurTrack == 1) {
|
|
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);
|
|
}
|
|
else {
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("subq bad crc @%02x:%02x:%02x\n",
|
|
tmp[0], tmp[1], tmp[2]);
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
generate_subq(time);
|
|
}
|
|
|
|
#ifdef CDR_LOG
|
|
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]);
|
|
#endif
|
|
}
|
|
|
|
static void AddIrqQueue(unsigned short irq, unsigned long ecycle) {
|
|
if (cdr.Irq != 0) {
|
|
if (irq == cdr.Irq || irq + 0x100 == cdr.Irq) {
|
|
cdr.IrqRepeated = 1;
|
|
CDR_INT(ecycle);
|
|
return;
|
|
}
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr: override cmd %02x -> %02x\n", cdr.Irq, irq);
|
|
#endif
|
|
}
|
|
|
|
cdr.Irq = irq;
|
|
cdr.eCycle = ecycle;
|
|
|
|
CDR_INT(ecycle);
|
|
}
|
|
|
|
static void cdrPlayInterrupt_Autopause()
|
|
{
|
|
if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
|
|
#ifdef CDR_LOG
|
|
CDR_LOG( "CDDA STOP\n" );
|
|
#endif
|
|
// 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) {
|
|
CDRMISC_INT( 0x100 );
|
|
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();
|
|
}
|
|
|
|
if (cdr.SetlocPending) {
|
|
memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
|
|
cdr.SetlocPending = 0;
|
|
}
|
|
Find_CurTrack(cdr.SetSectorPlay);
|
|
ReadTrack(cdr.SetSectorPlay);
|
|
cdr.TrackChanged = FALSE;
|
|
}
|
|
|
|
if (!cdr.Play) return;
|
|
#ifdef CDR_LOG
|
|
CDR_LOG( "CDDA - %d:%d:%d\n",
|
|
cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2] );
|
|
#endif
|
|
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;
|
|
|
|
if (CDR_readCDDA && !cdr.Muted) {
|
|
CDR_readCDDA(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
|
|
cdr.SetSectorPlay[2], cdr.Transfer);
|
|
|
|
cdrAttenuate((s16 *)cdr.Transfer, CD_FRAMESIZE_RAW / 4, 1);
|
|
if (SPU_playCDDAchannel)
|
|
SPU_playCDDAchannel((short *)cdr.Transfer, CD_FRAMESIZE_RAW);
|
|
}
|
|
|
|
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
|
|
generate_subq(cdr.SetSectorPlay);
|
|
}
|
|
|
|
void cdrInterrupt() {
|
|
u16 Irq = cdr.Irq;
|
|
int no_busy_error = 0;
|
|
int start_rotating = 0;
|
|
int error = 0;
|
|
int delay;
|
|
|
|
// Reschedule IRQ
|
|
if (cdr.Stat) {
|
|
CDR_INT( 0x100 );
|
|
return;
|
|
}
|
|
|
|
cdr.Ctrl &= ~0x80;
|
|
|
|
// default response
|
|
SetResultSize(1);
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Stat = Acknowledge;
|
|
|
|
if (cdr.IrqRepeated) {
|
|
cdr.IrqRepeated = 0;
|
|
if (cdr.eCycle > psxRegs.cycle) {
|
|
CDR_INT(cdr.eCycle);
|
|
goto finish;
|
|
}
|
|
}
|
|
|
|
cdr.Irq = 0;
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("CDRINT %x %x %x %x\n", cdr.Seeked, cdr.Stat, Irq, cdr.IrqRepeated);
|
|
#endif
|
|
switch (Irq) {
|
|
case CdlSync:
|
|
// TOOD: sometimes/always return error?
|
|
break;
|
|
|
|
case CdlNop:
|
|
if (cdr.DriveState != DRIVESTATE_LID_OPEN)
|
|
cdr.StatP &= ~STATUS_SHELLOPEN;
|
|
no_busy_error = 1;
|
|
break;
|
|
|
|
case CdlSetloc:
|
|
break;
|
|
|
|
do_CdlPlay:
|
|
case CdlPlay:
|
|
StopCdda();
|
|
if (cdr.Seeked == SEEK_PENDING) {
|
|
// XXX: wrong, should seek instead..
|
|
cdr.Seeked = SEEK_DONE;
|
|
}
|
|
if (cdr.SetlocPending) {
|
|
memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
|
|
cdr.SetlocPending = 0;
|
|
}
|
|
|
|
// BIOS CD Player
|
|
// - Pause player, hit Track 01/02/../xx (Setloc issued!!)
|
|
|
|
if (cdr.ParamC == 0 || cdr.Param[0] == 0) {
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("PLAY Resume @ %d:%d:%d\n",
|
|
cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int track = btoi( cdr.Param[0] );
|
|
|
|
if (track <= cdr.ResultTN[1])
|
|
cdr.CurTrack = track;
|
|
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("PLAY track %d\n", cdr.CurTrack);
|
|
#endif
|
|
|
|
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 != CDDA_DISABLED)
|
|
CDR_play(cdr.SetSectorPlay);
|
|
|
|
// Vib Ribbon: gameplay checks flag
|
|
cdr.StatP &= ~STATUS_SEEK;
|
|
cdr.Result[0] = cdr.StatP;
|
|
|
|
cdr.StatP |= STATUS_PLAY;
|
|
|
|
// BIOS player - set flag again
|
|
cdr.Play = TRUE;
|
|
|
|
CDRMISC_INT( cdReadTime );
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlForward:
|
|
// TODO: error 80 if stopped
|
|
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:
|
|
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:
|
|
if (cdr.DriveState != DRIVESTATE_STOPPED) {
|
|
error = ERROR_INVALIDARG;
|
|
goto set_error;
|
|
}
|
|
AddIrqQueue(CdlStandby + 0x100, cdReadTime * 125 / 2);
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlStandby + 0x100:
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlStop:
|
|
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];
|
|
}
|
|
|
|
StopCdda();
|
|
StopReading();
|
|
|
|
delay = 0x800;
|
|
if (cdr.DriveState == DRIVESTATE_STANDBY)
|
|
delay = cdReadTime * 30 / 2;
|
|
|
|
cdr.DriveState = DRIVESTATE_STOPPED;
|
|
AddIrqQueue(CdlStop + 0x100, delay);
|
|
break;
|
|
|
|
case CdlStop + 0x100:
|
|
cdr.StatP &= ~STATUS_ROTATING;
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlPause:
|
|
/*
|
|
Gundam Battle Assault 2: much slower (*)
|
|
- Fixes boot, gameplay
|
|
|
|
Hokuto no Ken 2: slower
|
|
- Fixes intro + subtitles
|
|
|
|
InuYasha - Feudal Fairy Tale: slower
|
|
- Fixes battles
|
|
*/
|
|
AddIrqQueue(CdlPause + 0x100, cdReadTime * 3);
|
|
cdr.Ctrl |= 0x80;
|
|
break;
|
|
|
|
|
|
case CdlPause + 0x100:
|
|
cdr.StatP &= ~STATUS_READ;
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlInit:
|
|
AddIrqQueue(CdlInit + 0x100, cdReadTime * 6);
|
|
no_busy_error = 1;
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlInit + 0x100:
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlMute:
|
|
cdr.Muted = TRUE;
|
|
break;
|
|
|
|
case CdlDemute:
|
|
cdr.Muted = FALSE;
|
|
break;
|
|
|
|
case CdlSetfilter:
|
|
cdr.File = cdr.Param[0];
|
|
cdr.Channel = cdr.Param[1];
|
|
break;
|
|
|
|
case CdlSetmode:
|
|
no_busy_error = 1;
|
|
break;
|
|
|
|
case CdlGetmode:
|
|
SetResultSize(6);
|
|
cdr.Result[1] = cdr.Mode;
|
|
cdr.Result[2] = cdr.File;
|
|
cdr.Result[3] = cdr.Channel;
|
|
cdr.Result[4] = 0;
|
|
cdr.Result[5] = 0;
|
|
no_busy_error = 1;
|
|
break;
|
|
|
|
case CdlGetlocL:
|
|
SetResultSize(8);
|
|
memcpy(cdr.Result, cdr.Transfer, 8);
|
|
break;
|
|
|
|
case CdlGetlocP:
|
|
SetResultSize(8);
|
|
memcpy(&cdr.Result, &cdr.subq, 8);
|
|
|
|
if (!cdr.Play && CheckSBI(cdr.Result+5))
|
|
memset(cdr.Result+2, 0, 6);
|
|
if (!cdr.Play && !cdr.Reading)
|
|
cdr.Result[1] = 0; // HACK?
|
|
break;
|
|
|
|
case CdlReadT: // SetSession?
|
|
// really long
|
|
AddIrqQueue(CdlReadT + 0x100, cdReadTime * 290 / 4);
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlReadT + 0x100:
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlGetTN:
|
|
SetResultSize(3);
|
|
if (CDR_getTN(cdr.ResultTN) == -1) {
|
|
cdr.Stat = DiskError;
|
|
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:
|
|
cdr.Track = btoi(cdr.Param[0]);
|
|
SetResultSize(4);
|
|
if (CDR_getTD(cdr.Track, cdr.ResultTD) == -1) {
|
|
cdr.Stat = DiskError;
|
|
cdr.Result[0] |= STATUS_ERROR;
|
|
} else {
|
|
cdr.Stat = Acknowledge;
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Result[1] = itob(cdr.ResultTD[2]);
|
|
cdr.Result[2] = itob(cdr.ResultTD[1]);
|
|
cdr.Result[3] = itob(cdr.ResultTD[0]);
|
|
}
|
|
break;
|
|
|
|
case CdlSeekL:
|
|
case CdlSeekP:
|
|
StopCdda();
|
|
StopReading();
|
|
cdr.StatP |= STATUS_SEEK;
|
|
|
|
/*
|
|
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
|
|
*/
|
|
CDRMISC_INT(cdr.Seeked == SEEK_DONE ? 0x800 : cdReadTime * 4);
|
|
cdr.Seeked = SEEK_PENDING;
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlTest:
|
|
switch (cdr.Param[0]) {
|
|
case 0x20: // System Controller ROM Version
|
|
SetResultSize(4);
|
|
memcpy(cdr.Result, Test20, 4);
|
|
break;
|
|
case 0x22:
|
|
SetResultSize(8);
|
|
memcpy(cdr.Result, Test22, 4);
|
|
break;
|
|
case 0x23: case 0x24:
|
|
SetResultSize(8);
|
|
memcpy(cdr.Result, Test23, 4);
|
|
break;
|
|
}
|
|
no_busy_error = 1;
|
|
break;
|
|
|
|
case CdlID:
|
|
AddIrqQueue(CdlID + 0x100, 20480);
|
|
break;
|
|
|
|
case CdlID + 0x100:
|
|
SetResultSize(8);
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Result[1] = 0;
|
|
cdr.Result[2] = 0;
|
|
cdr.Result[3] = 0;
|
|
|
|
// 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
|
|
if (CDR_getStatus(&stat) == -1 || stat.Type == 0 || stat.Type == 0xff) {
|
|
cdr.Result[1] = 0xc0;
|
|
}
|
|
else {
|
|
if (stat.Type == 2)
|
|
cdr.Result[1] |= 0x10;
|
|
if (CdromId[0] == '\0')
|
|
cdr.Result[1] |= 0x80;
|
|
}
|
|
cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
|
|
|
|
strncpy((char *)&cdr.Result[4], "PCSX", 4);
|
|
cdr.Stat = Complete;
|
|
break;
|
|
|
|
case CdlReset:
|
|
// yes, it really sets STATUS_SHELLOPEN
|
|
cdr.StatP |= STATUS_SHELLOPEN;
|
|
cdr.DriveState = DRIVESTATE_RESCAN_CD;
|
|
CDRLID_INT(20480);
|
|
no_busy_error = 1;
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlGetQ:
|
|
// TODO?
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("got CdlGetQ\n");
|
|
#endif
|
|
break;
|
|
|
|
case CdlReadToc:
|
|
AddIrqQueue(CdlReadToc + 0x100, cdReadTime * 180 / 4);
|
|
no_busy_error = 1;
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
case CdlReadToc + 0x100:
|
|
cdr.Stat = Complete;
|
|
no_busy_error = 1;
|
|
break;
|
|
|
|
case CdlReadN:
|
|
case CdlReadS:
|
|
if (cdr.SetlocPending) {
|
|
memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
|
|
cdr.SetlocPending = 0;
|
|
}
|
|
Find_CurTrack(cdr.SetSectorPlay);
|
|
|
|
if ((cdr.Mode & MODE_CDDA) && cdr.CurTrack > 1)
|
|
// Read* acts as play for cdda tracks in cdda mode
|
|
goto do_CdlPlay;
|
|
|
|
cdr.Reading = 1;
|
|
cdr.FirstSector = 1;
|
|
|
|
// Fighting Force 2 - update subq time immediately
|
|
// - fixes new game
|
|
ReadTrack(cdr.SetSectorPlay);
|
|
|
|
|
|
// Crusaders of Might and Magic - update getlocl now
|
|
// - fixes cutscene speech
|
|
{
|
|
u8 *buf = CDR_getBuffer();
|
|
if (buf != NULL)
|
|
memcpy(cdr.Transfer, buf, 8);
|
|
}
|
|
|
|
/*
|
|
Duke Nukem: Land of the Babes - seek then delay read for one frame
|
|
- fixes cutscenes
|
|
C-12 - Final Resistance - doesn't like seek
|
|
*/
|
|
|
|
if (cdr.Seeked != SEEK_DONE) {
|
|
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) : cdReadTime * 2);
|
|
} else {
|
|
cdr.StatP |= STATUS_READ;
|
|
cdr.StatP &= ~STATUS_SEEK;
|
|
|
|
CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime) : cdReadTime * 2);
|
|
}
|
|
|
|
cdr.Result[0] = cdr.StatP;
|
|
start_rotating = 1;
|
|
break;
|
|
|
|
default:
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("Invalid command: %02x\n", Irq);
|
|
#endif
|
|
error = ERROR_INVALIDCMD;
|
|
// FALLTHROUGH
|
|
|
|
set_error:
|
|
SetResultSize(2);
|
|
cdr.Result[0] = cdr.StatP | STATUS_ERROR;
|
|
cdr.Result[1] = error;
|
|
cdr.Stat = DiskError;
|
|
break;
|
|
}
|
|
|
|
if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
|
|
cdr.DriveState = DRIVESTATE_STANDBY;
|
|
cdr.StatP |= STATUS_ROTATING;
|
|
}
|
|
|
|
if (!no_busy_error) {
|
|
switch (cdr.DriveState) {
|
|
case DRIVESTATE_LID_OPEN:
|
|
case DRIVESTATE_RESCAN_CD:
|
|
case DRIVESTATE_PREPARE_CD:
|
|
SetResultSize(2);
|
|
cdr.Result[0] = cdr.StatP | STATUS_ERROR;
|
|
cdr.Result[1] = ERROR_NOTREADY;
|
|
cdr.Stat = DiskError;
|
|
break;
|
|
}
|
|
}
|
|
|
|
finish:
|
|
setIrq();
|
|
cdr.ParamC = 0;
|
|
|
|
#ifdef CDR_LOG_IO
|
|
{
|
|
int i;
|
|
CDR_LOG_IO("CDR IRQ %d cmd %02x stat %02x: ",
|
|
!!(cdr.Stat & cdr.Reg2), Irq, cdr.Stat);
|
|
for (i = 0; i < cdr.ResultC; i++)
|
|
CDR_LOG_IO("%02x ", cdr.Result[i]);
|
|
CDR_LOG_IO("\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#define ssat32_to_16(v) do { \
|
|
if (v < -32768) v = -32768; \
|
|
else if (v > 32767) v = 32767; \
|
|
} while (0)
|
|
|
|
void cdrAttenuate(s16 *buf, int samples, int stereo)
|
|
{
|
|
int i, l, r;
|
|
int ll = cdr.AttenuatorLeftToLeft;
|
|
int lr = cdr.AttenuatorLeftToRight;
|
|
int rl = cdr.AttenuatorRightToLeft;
|
|
int rr = cdr.AttenuatorRightToRight;
|
|
|
|
if (lr == 0 && rl == 0 && 0x78 <= ll && ll <= 0x88 && 0x78 <= rr && rr <= 0x88)
|
|
return;
|
|
|
|
if (!stereo && ll == 0x40 && lr == 0x40 && rl == 0x40 && rr == 0x40)
|
|
return;
|
|
|
|
if (stereo) {
|
|
for (i = 0; i < samples; i++) {
|
|
l = buf[i * 2];
|
|
r = buf[i * 2 + 1];
|
|
l = (l * ll + r * rl) >> 7;
|
|
r = (r * rr + l * lr) >> 7;
|
|
ssat32_to_16(l);
|
|
ssat32_to_16(r);
|
|
buf[i * 2] = l;
|
|
buf[i * 2 + 1] = r;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < samples; i++) {
|
|
l = buf[i];
|
|
l = l * (ll + rl) >> 7;
|
|
//r = r * (rr + lr) >> 7;
|
|
ssat32_to_16(l);
|
|
//ssat32_to_16(r);
|
|
buf[i] = l;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cdrReadInterrupt() {
|
|
u8 *buf;
|
|
|
|
if (!cdr.Reading)
|
|
return;
|
|
|
|
if (cdr.Irq || cdr.Stat) {
|
|
CDREAD_INT(0x100);
|
|
return;
|
|
}
|
|
|
|
if ((psxHu32ref(0x1070) & psxHu32ref(0x1074) & SWAP32((u32)0x4)) && !cdr.ReadRescheduled) {
|
|
// HACK: with BIAS 2, emulated CPU is often slower than real thing,
|
|
// game may be unfinished with prev data read, so reschedule
|
|
// (Brave Fencer Musashi)
|
|
CDREAD_INT(cdReadTime / 2);
|
|
cdr.ReadRescheduled = 1;
|
|
return;
|
|
}
|
|
|
|
cdr.OCUP = 1;
|
|
SetResultSize(1);
|
|
cdr.StatP |= STATUS_READ|STATUS_ROTATING;
|
|
cdr.StatP &= ~STATUS_SEEK;
|
|
cdr.Result[0] = cdr.StatP;
|
|
cdr.Seeked = SEEK_DONE;
|
|
|
|
ReadTrack(cdr.SetSectorPlay);
|
|
|
|
buf = CDR_getBuffer();
|
|
if (buf == NULL)
|
|
cdr.RErr = -1;
|
|
|
|
if (cdr.RErr == -1) {
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("cdrReadInterrupt() Log: err\n");
|
|
#endif
|
|
memset(cdr.Transfer, 0, DATA_SIZE);
|
|
cdr.Stat = DiskError;
|
|
cdr.Result[0] |= STATUS_ERROR;
|
|
CDREAD_INT((cdr.Mode & 0x80) ? (cdReadTime / 2) : cdReadTime);
|
|
return;
|
|
}
|
|
|
|
memcpy(cdr.Transfer, buf, DATA_SIZE);
|
|
CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
|
|
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("cdrReadInterrupt() Log: cdr.Transfer %x:%x:%x\n", cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
|
|
#endif
|
|
|
|
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.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) {
|
|
cdrAttenuate(cdr.Xa.pcm, cdr.Xa.nsamples, cdr.Xa.stereo);
|
|
SPU_playADPCMchannel(&cdr.Xa);
|
|
cdr.FirstSector = 0;
|
|
}
|
|
else cdr.FirstSector = -1;
|
|
}
|
|
}
|
|
|
|
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]++;
|
|
}
|
|
}
|
|
|
|
cdr.Readed = 0;
|
|
cdr.ReadRescheduled = 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();
|
|
}
|
|
|
|
// update for CdlGetlocP
|
|
ReadTrack(cdr.SetSectorPlay);
|
|
}
|
|
|
|
/*
|
|
cdrRead0:
|
|
bit 0,1 - mode
|
|
bit 2 - unknown
|
|
bit 3 - unknown
|
|
bit 4 - unknown
|
|
bit 5 - 1 result ready
|
|
bit 6 - 1 dma ready
|
|
bit 7 - 1 command being processed
|
|
*/
|
|
|
|
unsigned char cdrRead0(void) {
|
|
if (cdr.ResultReady)
|
|
cdr.Ctrl |= 0x20;
|
|
else
|
|
cdr.Ctrl &= ~0x20;
|
|
|
|
if (cdr.OCUP)
|
|
cdr.Ctrl |= 0x40;
|
|
// else
|
|
// cdr.Ctrl &= ~0x40;
|
|
|
|
// What means the 0x10 and the 0x08 bits? I only saw it used by the bios
|
|
cdr.Ctrl |= 0x18;
|
|
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr r0: %02x\n", cdr.Ctrl);
|
|
#endif
|
|
return psxHu8(0x1800) = cdr.Ctrl;
|
|
}
|
|
|
|
void cdrWrite0(unsigned char rt) {
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr w0: %02x\n", rt);
|
|
#endif
|
|
cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
|
|
}
|
|
|
|
unsigned char cdrRead1(void) {
|
|
if ((cdr.ResultP & 0xf) < cdr.ResultC)
|
|
psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
|
|
else
|
|
psxHu8(0x1801) = 0;
|
|
cdr.ResultP++;
|
|
if (cdr.ResultP == cdr.ResultC)
|
|
cdr.ResultReady = 0;
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr r1: %02x\n", psxHu8(0x1801));
|
|
#endif
|
|
return psxHu8(0x1801);
|
|
}
|
|
|
|
void cdrWrite1(unsigned char rt) {
|
|
u8 set_loc[3];
|
|
int i;
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr w1: %02x\n", rt);
|
|
#endif
|
|
switch (cdr.Ctrl & 3) {
|
|
case 0:
|
|
break;
|
|
case 3:
|
|
cdr.AttenuatorRightToRightT = rt;
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
cdr.Cmd = rt;
|
|
cdr.OCUP = 0;
|
|
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("CD1 write: %x (%s)", rt, CmdName[rt]);
|
|
if (cdr.ParamC) {
|
|
CDR_LOG_IO(" Param[%d] = {", cdr.ParamC);
|
|
for (i = 0; i < cdr.ParamC; i++)
|
|
CDR_LOG_IO(" %x,", cdr.Param[i]);
|
|
CDR_LOG_IO("}\n");
|
|
} else {
|
|
CDR_LOG_IO("\n");
|
|
}
|
|
#endif
|
|
|
|
cdr.ResultReady = 0;
|
|
cdr.Ctrl |= 0x80;
|
|
// cdr.Stat = NoIntr;
|
|
AddIrqQueue(cdr.Cmd, 0x800);
|
|
|
|
switch (cdr.Cmd) {
|
|
case CdlSetloc:
|
|
for (i = 0; i < 3; i++)
|
|
set_loc[i] = btoi(cdr.Param[i]);
|
|
|
|
i = msf2sec(cdr.SetSectorPlay);
|
|
i = abs(i - (int)msf2sec(set_loc));
|
|
if (i > 16)
|
|
cdr.Seeked = SEEK_PENDING;
|
|
|
|
memcpy(cdr.SetSector, set_loc, 3);
|
|
cdr.SetSector[3] = 0;
|
|
cdr.SetlocPending = 1;
|
|
break;
|
|
|
|
case CdlReadN:
|
|
case CdlReadS:
|
|
case CdlPause:
|
|
StopCdda();
|
|
StopReading();
|
|
break;
|
|
|
|
case CdlReset:
|
|
case CdlInit:
|
|
cdr.Seeked = SEEK_DONE;
|
|
StopCdda();
|
|
StopReading();
|
|
break;
|
|
|
|
case CdlSetmode:
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("cdrWrite1() Log: Setmode %x\n", cdr.Param[0]);
|
|
#endif
|
|
cdr.Mode = cdr.Param[0];
|
|
|
|
// Squaresoft on PlayStation 1998 Collector's CD Vol. 1
|
|
// - fixes choppy movie sound
|
|
if( cdr.Play && (cdr.Mode & MODE_CDDA) == 0 )
|
|
StopCdda();
|
|
break;
|
|
}
|
|
}
|
|
|
|
unsigned char cdrRead2(void) {
|
|
unsigned char ret;
|
|
|
|
if (cdr.Readed == 0) {
|
|
ret = 0;
|
|
} else {
|
|
ret = cdr.Transfer[cdr.transferIndex];
|
|
cdr.transferIndex++;
|
|
adjustTransferIndex();
|
|
}
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr r2: %02x\n", ret);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void cdrWrite2(unsigned char rt) {
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr w2: %02x\n", rt);
|
|
#endif
|
|
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.AttenuatorLeftToLeftT = rt;
|
|
return;
|
|
case 3:
|
|
cdr.AttenuatorRightToLeftT = rt;
|
|
return;
|
|
}
|
|
}
|
|
|
|
unsigned char cdrRead3(void) {
|
|
if (cdr.Ctrl & 0x1)
|
|
psxHu8(0x1803) = cdr.Stat | 0xE0;
|
|
else
|
|
psxHu8(0x1803) = cdr.Reg2 | 0xE0;
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr r3: %02x\n", psxHu8(0x1803));
|
|
#endif
|
|
return psxHu8(0x1803);
|
|
}
|
|
|
|
void cdrWrite3(unsigned char rt) {
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("cdr w3: %02x\n", rt);
|
|
#endif
|
|
switch (cdr.Ctrl & 3) {
|
|
case 0:
|
|
break; // transfer
|
|
case 1:
|
|
cdr.Stat &= ~rt;
|
|
|
|
if (rt & 0x40)
|
|
cdr.ParamC = 0;
|
|
return;
|
|
case 2:
|
|
cdr.AttenuatorLeftToRightT = rt;
|
|
return;
|
|
case 3:
|
|
if (rt & 0x20) {
|
|
memcpy(&cdr.AttenuatorLeftToLeft, &cdr.AttenuatorLeftToLeftT, 4);
|
|
#ifdef CDR_LOG_IO
|
|
CDR_LOG_IO("CD-XA Volume: %02x %02x | %02x %02x\n",
|
|
cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
|
|
cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((rt & 0x80) && cdr.Readed == 0) {
|
|
cdr.Readed = 1;
|
|
cdr.transferIndex = 0;
|
|
|
|
switch (cdr.Mode & (MODE_SIZE_2340|MODE_SIZE_2328)) {
|
|
case MODE_SIZE_2328:
|
|
case MODE_SIZE_2048:
|
|
cdr.transferIndex += 12;
|
|
break;
|
|
|
|
case MODE_SIZE_2340:
|
|
cdr.transferIndex += 0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void psxDma3(u32 madr, u32 bcr, u32 chcr) {
|
|
u32 cdsize;
|
|
int i;
|
|
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 & (MODE_SIZE_2340|MODE_SIZE_2328)) {
|
|
case MODE_SIZE_2340: cdsize = 2340; break;
|
|
case MODE_SIZE_2328: cdsize = 2328; break;
|
|
default:
|
|
case MODE_SIZE_2048: cdsize = 2048; break;
|
|
}
|
|
}
|
|
|
|
|
|
ptr = (u8 *)PSXM(madr);
|
|
if (ptr == NULL) {
|
|
#ifdef CDR_LOG
|
|
CDR_LOG("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/*
|
|
GS CDX: Enhancement CD crash
|
|
- Setloc 0:0:0
|
|
- CdlPlay
|
|
- Spams DMA3 and gets buffer overrun
|
|
*/
|
|
for(i = 0; i < cdsize; ++i) {
|
|
ptr[i] = cdr.Transfer[cdr.transferIndex];
|
|
cdr.transferIndex++;
|
|
adjustTransferIndex();
|
|
}
|
|
#ifdef PSXREC
|
|
psxCpu->Clear(madr, cdsize / 4);
|
|
#endif
|
|
// 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;
|
|
}
|
|
|
|
HW_DMA3_CHCR &= SWAP32(~0x01000000);
|
|
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;
|
|
cdr.transferIndex = 0;
|
|
cdr.Reg2 = 0x1f;
|
|
cdr.Stat = NoIntr;
|
|
cdr.DriveState = DRIVESTATE_STANDBY;
|
|
cdr.StatP = STATUS_ROTATING;
|
|
|
|
// BIOS player - default values
|
|
cdr.AttenuatorLeftToLeft = 0x80;
|
|
cdr.AttenuatorLeftToRight = 0x00;
|
|
cdr.AttenuatorRightToLeft = 0x00;
|
|
cdr.AttenuatorRightToRight = 0x80;
|
|
|
|
getCdInfo();
|
|
}
|
|
|
|
int cdrFreeze(gzFile f, int Mode) {
|
|
u8 tmpp[3];
|
|
|
|
if (Mode == 0 && Config.Cdda != CDDA_DISABLED)
|
|
CDR_stop();
|
|
|
|
gzfreeze(&cdr, sizeof(cdr));
|
|
|
|
if (Mode == 1)
|
|
cdr.ParamP = cdr.ParamC;
|
|
|
|
if (Mode == 0) {
|
|
getCdInfo();
|
|
|
|
// read right sub data
|
|
memcpy(tmpp, cdr.Prev, 3);
|
|
cdr.Prev[0]++;
|
|
ReadTrack(tmpp);
|
|
|
|
if (cdr.Play) {
|
|
Find_CurTrack(cdr.SetSectorPlay);
|
|
if (Config.Cdda != CDDA_DISABLED)
|
|
CDR_play(cdr.SetSectorPlay);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void LidInterrupt() {
|
|
getCdInfo();
|
|
StopCdda();
|
|
cdrLidSeekInterrupt();
|
|
}
|