mirror of
https://github.com/holub/mame
synced 2025-06-02 10:59:52 +03:00
1470 lines
26 KiB
C
1470 lines
26 KiB
C
#include "emu.h"
|
|
#include "psxcd.h"
|
|
#include "debugger.h"
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//#define debug_cdrom
|
|
//#define debug_cdrom_registers
|
|
//#define skip_reads
|
|
//#define dump_subheader
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
enum cdrom_events
|
|
{
|
|
event_cmd_complete=0,
|
|
event_preread_sector,
|
|
event_read_sector,
|
|
event_play_sector,
|
|
event_change_disk
|
|
};
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
enum intr_status
|
|
{
|
|
intr_nointr=0,
|
|
intr_dataready,
|
|
intr_acknowledge,
|
|
intr_complete,
|
|
intr_dataend,
|
|
intr_diskerror
|
|
};
|
|
|
|
enum mode_flags
|
|
{
|
|
mode_double_speed=0x80,
|
|
mode_adpcm=0x40,
|
|
mode_size=0x20,
|
|
mode_size2=0x10,
|
|
mode_size_shift=4,
|
|
mode_size_mask=(3<<mode_size_shift),
|
|
mode_channel=0x08,
|
|
mode_report=0x04,
|
|
mode_autopause=0x02,
|
|
mode_cdda=0x01
|
|
};
|
|
|
|
enum status_f
|
|
{
|
|
status_playing=0x80,
|
|
status_seeking=0x40,
|
|
status_reading=0x20,
|
|
status_shellopen=0x10,
|
|
status_invalid=0x08,
|
|
status_seekerror=0x04,
|
|
status_standby=0x02,
|
|
status_error=0x01
|
|
};
|
|
|
|
struct subheader
|
|
{
|
|
unsigned char file,
|
|
channel,
|
|
submode,
|
|
coding;
|
|
};
|
|
|
|
enum submode_flags
|
|
{
|
|
submode_eof=0x80,
|
|
submode_realtime=0x40,
|
|
submode_form=0x20,
|
|
submode_trigger=0x10,
|
|
submode_data=0x08,
|
|
submode_audio=0x04,
|
|
submode_video=0x02,
|
|
submode_eor=0x01
|
|
};
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
//**************************************************************************
|
|
// DEVICE DEFINITIONS
|
|
//**************************************************************************
|
|
|
|
const device_type PSXCD = &device_creator<psxcd_device>;
|
|
|
|
static struct cdrom_interface psx_cdrom =
|
|
{
|
|
"psx_cdrom",
|
|
NULL
|
|
};
|
|
|
|
psxcd_device::psxcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
|
|
cdrom_image_device(mconfig, PSXCD, "Cdrom", tag, owner, clock, "psx_cd", __FILE__),
|
|
m_irq_handler(*this)
|
|
{
|
|
static_set_static_config(*this, &psx_cdrom);
|
|
}
|
|
|
|
|
|
void psxcd_device::device_start()
|
|
{
|
|
cdrom_image_device::device_start();
|
|
m_irq_handler.resolve_safe();
|
|
|
|
m_maincpu = machine().device<cpu_device>("maincpu");
|
|
m_spu = machine().device<spu_device>("spu");
|
|
|
|
unsigned int sysclk=m_maincpu->clock()/2;
|
|
start_read_delay=(sysclk/60);
|
|
read_sector_cycles=(sysclk/75);
|
|
preread_delay=(read_sector_cycles>>2)-500;
|
|
|
|
m_sysclock = sysclk;
|
|
|
|
secleft = 0;
|
|
secsize = 2048;
|
|
res_queue = NULL;
|
|
cur_res = NULL;
|
|
open = false;
|
|
streaming = false;
|
|
sechead = 0;
|
|
sectail = 0;
|
|
secskip = 0;
|
|
next_read_event = -1;
|
|
cbp = cmdbuf;
|
|
m_mute = false;
|
|
|
|
status=status_shellopen;
|
|
sr=8|1;
|
|
res=0;
|
|
ir=0;
|
|
mode=0;
|
|
|
|
for (int i = 0; i < MAX_PSXCD_TIMERS; i++)
|
|
{
|
|
m_timers[i] = timer_alloc(i);
|
|
m_timerinuse[i] = false;
|
|
}
|
|
|
|
curpos.w = 0;
|
|
m_param_count = 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::device_reset()
|
|
{
|
|
stop_read();
|
|
open=false;
|
|
|
|
for (int i = 0; i < MAX_PSXCD_TIMERS; i++)
|
|
{
|
|
m_timers[i]->adjust(attotime::never, 0, attotime::never);
|
|
m_timerinuse[i] = false;
|
|
}
|
|
|
|
next_read_event = -1;
|
|
|
|
if(cur_res)
|
|
{
|
|
global_free(cur_res);
|
|
cur_res = NULL;
|
|
}
|
|
|
|
while(res_queue)
|
|
{
|
|
cur_res = res_queue->next;
|
|
global_free(res_queue);
|
|
res_queue = cur_res;
|
|
}
|
|
|
|
m_param_count = 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
bool psxcd_device::call_load()
|
|
{
|
|
bool ret = cdrom_image_device::call_load();
|
|
open = true;
|
|
if(ret == IMAGE_INIT_PASS)
|
|
add_system_event(event_change_disk, m_sysclock, NULL); // 1 sec to spin up the disk
|
|
return ret;
|
|
}
|
|
|
|
void psxcd_device::call_unload()
|
|
{
|
|
stop_read();
|
|
cdrom_image_device::call_unload();
|
|
open = true;
|
|
status = status_shellopen;
|
|
send_result(intr_diskerror);
|
|
}
|
|
|
|
READ8_MEMBER( psxcd_device::read )
|
|
{
|
|
unsigned char ret = 0;
|
|
|
|
switch (offset&3)
|
|
{
|
|
/*
|
|
x--- ---- command/parameter busy flag
|
|
-x-- ---- data fifo full (active low)
|
|
--x- ---- response fifo empty (active low)
|
|
---x ---- parameter fifo full (active low)
|
|
---- x--- parameter fifo empty (active high)
|
|
---- --xx cmd mode
|
|
*/
|
|
case 0: ret=sr; break;
|
|
case 1:
|
|
ret=res;
|
|
if ((cur_res) && (rdp<cur_res->sz))
|
|
{
|
|
res=cur_res->data[rdp++];
|
|
sr|=(1<<5);
|
|
} else
|
|
{
|
|
if ((cur_res) && (cur_res->res&0x10))
|
|
{
|
|
global_free(cur_res);
|
|
cur_res=NULL;
|
|
}
|
|
|
|
sr&=~(1<<5);
|
|
}
|
|
break;
|
|
case 2: ret=0; break;
|
|
case 3: ret=ir; break;
|
|
}
|
|
|
|
#ifdef debug_cdrom_registers
|
|
printf("cdrom: read byte %08x = %02x (PC=%08x)\n",offset,ret,space.device().safe_pc());
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
WRITE8_MEMBER( psxcd_device::write )
|
|
{
|
|
#ifdef debug_cdrom_registers
|
|
printf("cdrom: write byte %08x = %02x (PC=%08x)\n",offset,data,space.device().safe_pc());
|
|
#endif
|
|
|
|
switch (offset&3)
|
|
{
|
|
case 0:
|
|
//if(data & 2)
|
|
// popmessage("cmdmode = %02x, contact MESSdev",data);
|
|
|
|
cmdmode=data&1;
|
|
if (cmdmode==0)
|
|
{
|
|
cbp=cmdbuf;
|
|
} else
|
|
{
|
|
if (! cur_res)
|
|
{
|
|
if (cur_res) global_free(cur_res);
|
|
|
|
if (res_queue)
|
|
{
|
|
#ifdef debug_cdrom_registers
|
|
printf("cdrom: nextres\n");
|
|
#endif
|
|
|
|
cur_res=res_queue;
|
|
res_queue=res_queue->next;
|
|
ir=cur_res->res&0xf;
|
|
rdp=0;
|
|
|
|
if (cur_res->sz)
|
|
{
|
|
res=cur_res->data[rdp++];
|
|
sr|=(1<<5);
|
|
} else
|
|
{
|
|
sr&=~(1<<5);
|
|
}
|
|
} else
|
|
{
|
|
//ir=0;
|
|
cur_res=NULL;
|
|
}
|
|
}
|
|
/*else
|
|
{
|
|
if (rdp>=cur_res->sz)
|
|
{
|
|
sr&=~(1<<5);
|
|
} else
|
|
{
|
|
sr|=~(1<<5);
|
|
res=cur_res->data[rdp++];
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
if (cmdmode==0)
|
|
{
|
|
write_command(data);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
if (cmdmode==0)
|
|
{
|
|
*cbp++=data;
|
|
m_param_count++;
|
|
} else
|
|
{
|
|
// ?flush buffer?
|
|
//if(data & 0xf8)
|
|
//popmessage("Interrupt enable register mode 1 [%02x] -> %02x",offset,data);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
x--- ---- unknown
|
|
-x-- ---- Reset parameter FIFO
|
|
--x- ---- unknown (used on transitions, so it certainly resets something)
|
|
---x ---- Command start
|
|
---- -xxx Response received
|
|
*/
|
|
case 3:
|
|
//if(data & 0x78)
|
|
// popmessage("IRQ flag = %02x, contact MESSdev",data);
|
|
|
|
if (data==0x07)
|
|
{
|
|
if (cur_res)
|
|
{
|
|
global_free(cur_res);
|
|
cur_res=NULL;
|
|
sr&=~(1<<5);
|
|
}
|
|
ir=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
psxcd_device::command_info psxcd_device::cmd_table[num_commands]=
|
|
{
|
|
{ &psxcd_device::cdcmd_sync, "sync" }, // 00
|
|
{ &psxcd_device::cdcmd_nop, "nop" }, // 01
|
|
{ &psxcd_device::cdcmd_setloc, "setloc" }, // 02
|
|
{ &psxcd_device::cdcmd_play, "play" }, // 03
|
|
{ &psxcd_device::cdcmd_forward, "forward" }, // 04
|
|
{ &psxcd_device::cdcmd_backward, "backward" }, // 05
|
|
{ &psxcd_device::cdcmd_readn, "readn" }, // 06
|
|
{ &psxcd_device::cdcmd_standby, "standby" }, // 07
|
|
{ &psxcd_device::cdcmd_stop, "stop" }, // 08
|
|
{ &psxcd_device::cdcmd_pause, "pause" }, // 09
|
|
{ &psxcd_device::cdcmd_init, "init" }, // 0a
|
|
{ &psxcd_device::cdcmd_mute, "mute" }, // 0b
|
|
{ &psxcd_device::cdcmd_demute, "demute" }, // 0c
|
|
{ &psxcd_device::cdcmd_setfilter, "setfilter" }, // 0d
|
|
{ &psxcd_device::cdcmd_setmode, "setmode" }, // 0e
|
|
{ &psxcd_device::cdcmd_getparam, "getparam" }, // 0f
|
|
{ &psxcd_device::cdcmd_getlocl, "getlocl" }, // 10
|
|
{ &psxcd_device::cdcmd_getlocp, "getlocp" }, // 11
|
|
{ &psxcd_device::cdcmd_illegal, "illegal" }, // 12
|
|
{ &psxcd_device::cdcmd_gettn, "gettn" }, // 13
|
|
{ &psxcd_device::cdcmd_gettd, "gettd" }, // 14
|
|
{ &psxcd_device::cdcmd_seekl, "seekl" }, // 15
|
|
{ &psxcd_device::cdcmd_seekp, "seekp" }, // 16
|
|
{ &psxcd_device::cdcmd_illegal, "illegal" }, // 17
|
|
{ &psxcd_device::cdcmd_illegal, "illegal" }, // 18
|
|
{ &psxcd_device::cdcmd_test, "test" }, // 19
|
|
{ &psxcd_device::cdcmd_id, "id" }, // 1a
|
|
{ &psxcd_device::cdcmd_reads, "reads" }, // 1b
|
|
{ &psxcd_device::cdcmd_reset, "reset" }, // 1c
|
|
{ &psxcd_device::cdcmd_illegal, "illegal" }, // 1d
|
|
{ &psxcd_device::cdcmd_readtoc, "readtoc" }, // 1e
|
|
};
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::write_command(const unsigned char byte)
|
|
{
|
|
assert(byte<num_commands);
|
|
(this->*cmd_table[byte].func)();
|
|
m_param_count = 0;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::cdcmd_sync()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: sync\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_nop()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: nop\n");
|
|
#endif
|
|
|
|
//stop_read();
|
|
|
|
if (!open)
|
|
status &= ~status_shellopen;
|
|
|
|
send_result(intr_complete);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_setloc()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: setloc %08x:%08x:%08x\n",
|
|
cmdbuf[0],
|
|
cmdbuf[1],
|
|
cmdbuf[2]);
|
|
#endif
|
|
|
|
stop_read();
|
|
|
|
|
|
CDPOS l;
|
|
l.b[M]=bcd_to_decimal(cmdbuf[0]);
|
|
l.b[S]=bcd_to_decimal(cmdbuf[1]);
|
|
l.b[F]=bcd_to_decimal(cmdbuf[2]);
|
|
|
|
if ((l.b[M]>0) || (l.b[S]>=2))
|
|
loc.w=l.w;
|
|
else
|
|
logerror("setloc out of range: %02d:%02d:%02d\n",l.b[M],l.b[S],l.b[F]);
|
|
|
|
send_result(intr_complete);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_play()
|
|
{
|
|
if(cmdbuf[0] && m_param_count)
|
|
loc.w = lba_to_msf_ps(cdrom_get_track_start(m_cdrom_handle, bcd_to_decimal(cmdbuf[0]) - 1));
|
|
|
|
curpos.w = loc.w;
|
|
if (!curpos.w)
|
|
curpos.b[S] = 2;
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: play %02x %02x %02x => %d\n", loc.b[M], loc.b[S], loc.b[F], msf_to_lba_ps(loc.w));
|
|
#endif
|
|
|
|
stop_read();
|
|
start_play();
|
|
send_result(intr_complete);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_forward()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: forward\n");
|
|
#endif
|
|
}
|
|
|
|
void psxcd_device::cdcmd_backward()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: backward\n");
|
|
#endif
|
|
}
|
|
|
|
void psxcd_device::cdcmd_readn()
|
|
{
|
|
if(!open)
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: readn\n");
|
|
#endif
|
|
|
|
curpos.w=loc.w;
|
|
|
|
stop_read();
|
|
start_read();
|
|
send_result(intr_complete);
|
|
} else
|
|
{
|
|
send_result(intr_diskerror);
|
|
}
|
|
}
|
|
|
|
void psxcd_device::cdcmd_standby()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: standby\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_stop()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: stop\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_pause()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: pause\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_init()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: init\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
mode=0;
|
|
sr|=0x10;
|
|
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_mute()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: mute\n");
|
|
#endif
|
|
|
|
m_mute = true;
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_demute()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: demute\n");
|
|
#endif
|
|
|
|
m_mute = false;
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_setfilter()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: setfilter %08x,%08x\n",cmdbuf[0],cmdbuf[1]);
|
|
#endif
|
|
|
|
filter_file=cmdbuf[0];
|
|
filter_channel=cmdbuf[1];
|
|
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_setmode()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: setmode %08x\n",cmdbuf[0]);
|
|
#endif
|
|
|
|
mode=cmdbuf[0];
|
|
|
|
switch ((mode&mode_size_mask)>>mode_size_shift)
|
|
{
|
|
case 1:
|
|
secsize=2328;
|
|
secskip=24;
|
|
break;
|
|
|
|
case 2:
|
|
secsize=2340;
|
|
secskip=12;
|
|
break;
|
|
|
|
default:
|
|
secsize=2048;
|
|
secskip=24;
|
|
break;
|
|
}
|
|
|
|
send_result(intr_complete);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_getparam()
|
|
{
|
|
unsigned char data[6]=
|
|
{
|
|
status,
|
|
mode,
|
|
filter_file,
|
|
filter_channel,
|
|
0,
|
|
0
|
|
};
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: getparam [%02x %02x %02x %02x %02x %02x]\n",
|
|
data[0],
|
|
data[1],
|
|
data[2],
|
|
data[3],
|
|
data[4],
|
|
data[5]);
|
|
#endif
|
|
|
|
send_result(intr_complete,data,6);
|
|
}
|
|
|
|
UINT32 psxcd_device::sub_loc(CDPOS src1, CDPOS src2)
|
|
{
|
|
CDPOS dst;
|
|
int f=src1.b[F]-src2.b[F],
|
|
s=src1.b[S]-src2.b[S],
|
|
m=src1.b[M]-src2.b[M];
|
|
while (f<0) { s--; f+=75; }
|
|
while (s<0) { m--; s+=60; }
|
|
|
|
if (m<0)
|
|
m=s=f=0;
|
|
|
|
dst.b[M]=m;
|
|
dst.b[S]=s;
|
|
dst.b[F]=f;
|
|
|
|
return dst.w;
|
|
}
|
|
|
|
void psxcd_device::cdcmd_getlocl()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: getlocl\n");
|
|
#endif
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: getlocl [%02x %02x %02x %02x %02x %02x %02x %02x]\n",
|
|
lastsechdr[0],
|
|
lastsechdr[1],
|
|
lastsechdr[2],
|
|
lastsechdr[3],
|
|
lastsechdr[4],
|
|
lastsechdr[5],
|
|
lastsechdr[6],
|
|
lastsechdr[7]);
|
|
#endif
|
|
|
|
send_result(intr_complete,lastsechdr,8);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_getlocp()
|
|
{
|
|
CDPOS tloc, start;
|
|
UINT8 track = cdrom_get_track(m_cdrom_handle, msf_to_lba_ps(loc.w) + 150) + 1;
|
|
start.w = (track == 1) ? 0x000200 : lba_to_msf_ps(cdrom_get_track_start(m_cdrom_handle, track - 1));
|
|
tloc.w = sub_loc(loc, start);
|
|
|
|
unsigned char data[8]=
|
|
{
|
|
decimal_to_bcd(track), // track
|
|
0x01, // index
|
|
decimal_to_bcd(tloc.b[M]), // min
|
|
decimal_to_bcd(tloc.b[S]), // sec
|
|
decimal_to_bcd(tloc.b[F]), // frame
|
|
decimal_to_bcd(loc.b[M]), // amin
|
|
decimal_to_bcd(loc.b[S]), // asec
|
|
decimal_to_bcd(loc.b[F]) // aframe
|
|
};
|
|
|
|
//unsigned char data[8]={ 2,1,0,0xff,0xff,0xff,0xff,0xff };
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: getlocp [%02x %02x %02x %02x %02x %02x %02x %02x]\n",
|
|
data[0],
|
|
data[1],
|
|
data[2],
|
|
data[3],
|
|
data[4],
|
|
data[5],
|
|
data[6],
|
|
data[7]);
|
|
#endif
|
|
|
|
send_result(intr_complete,data,8);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_illegal()
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_gettn()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: gettn\n");
|
|
#endif
|
|
|
|
|
|
if(!open)
|
|
{
|
|
unsigned char data[3]=
|
|
{
|
|
status,
|
|
decimal_to_bcd(1),
|
|
decimal_to_bcd(cdrom_get_last_track(m_cdrom_handle))
|
|
};
|
|
|
|
//stop_read();
|
|
send_result(intr_complete,data,3);
|
|
}
|
|
else
|
|
{
|
|
status |= status_error;
|
|
send_result(intr_diskerror);
|
|
}
|
|
}
|
|
|
|
void psxcd_device::cdcmd_gettd()
|
|
{
|
|
UINT8 track = bcd_to_decimal(cmdbuf[0]);
|
|
UINT8 last = cdrom_get_last_track(m_cdrom_handle);
|
|
if(track <= last)
|
|
{
|
|
CDPOS trkstart;
|
|
if(!track) // length of disk
|
|
trkstart.w = lba_to_msf_ps(cdrom_get_track_start(m_cdrom_handle, 0xaa));
|
|
else
|
|
trkstart.w = lba_to_msf_ps(cdrom_get_track_start(m_cdrom_handle, track - 1));
|
|
|
|
unsigned char data[3]=
|
|
{
|
|
status,
|
|
decimal_to_bcd(trkstart.b[M]),
|
|
decimal_to_bcd(trkstart.b[S])
|
|
};
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: gettd %02x [%02x %02x %02x]\n",
|
|
cmdbuf[0],
|
|
data[0],
|
|
data[1],
|
|
data[2]);
|
|
#endif
|
|
|
|
//stop_read();
|
|
send_result(intr_acknowledge,data,3);
|
|
}
|
|
else
|
|
{
|
|
status |= status_error;
|
|
send_result(intr_diskerror);
|
|
}
|
|
}
|
|
|
|
void psxcd_device::cdcmd_seekl()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: seekl [%02d:%02d:%02d]\n",loc.b[M],loc.b[S],loc.b[F]);
|
|
#endif
|
|
|
|
curpos.w=loc.w;
|
|
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_seekp()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: seekp\n");
|
|
#endif
|
|
|
|
curpos.w=loc.w;
|
|
|
|
send_result(intr_acknowledge);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_test()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: test %08x\n",cmdbuf[0]);
|
|
#endif
|
|
|
|
static unsigned char data[4]=
|
|
{
|
|
0x95,
|
|
0x07,
|
|
0x06,
|
|
0xff
|
|
};
|
|
|
|
send_result(intr_complete,data,4);
|
|
}
|
|
|
|
void psxcd_device::cdcmd_id()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: id\n");
|
|
#endif
|
|
|
|
if (!open)
|
|
{
|
|
static unsigned char gamedata[8] = { 0x00, 0x00, 0x00, 0x00, 'S', 'C', 'E', 'A' };
|
|
static unsigned char audiodata[8] = { 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // drops into the audio CD player. 08 80 goes to the menu.
|
|
|
|
if(cdrom_get_track_type(m_cdrom_handle, 0) == CD_TRACK_AUDIO)
|
|
{
|
|
audiodata[0] = status | status_invalid;
|
|
send_result(intr_acknowledge,audiodata,8);
|
|
}
|
|
else
|
|
{
|
|
gamedata[0] = status;
|
|
send_result(intr_acknowledge,gamedata,8);
|
|
}
|
|
} else
|
|
{
|
|
status |= status_error;
|
|
send_result(intr_diskerror);
|
|
}
|
|
}
|
|
|
|
void psxcd_device::cdcmd_reads()
|
|
{
|
|
curpos.w=loc.w;
|
|
|
|
#ifdef skip_reads
|
|
#ifdef debug_cdrom
|
|
log("cdrom: reads [SKIPPING - RETURN COMPLETE]\n");
|
|
#endif
|
|
|
|
send_result(intr_complete);
|
|
#else
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: reads\n");
|
|
#endif
|
|
|
|
stop_read();
|
|
start_streaming();
|
|
#endif
|
|
}
|
|
|
|
void psxcd_device::cdcmd_reset()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: reset\n");
|
|
#endif
|
|
}
|
|
|
|
void psxcd_device::cdcmd_readtoc()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: readtoc\n");
|
|
#endif
|
|
|
|
static unsigned char data[4]=
|
|
{
|
|
0x00,
|
|
0x00,
|
|
0xff,
|
|
0xfe
|
|
};
|
|
|
|
send_result(intr_complete|0x10,data,2);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::cmd_complete(command_result *res)
|
|
{
|
|
bool doint=((res->res&0x10)==0);
|
|
#ifdef debug_cdrom
|
|
command_result *rf;
|
|
#endif
|
|
|
|
if (doint)
|
|
{
|
|
m_irq_handler(1);
|
|
}
|
|
|
|
add_result(res);
|
|
|
|
#ifdef debug_cdrom
|
|
if (doint)
|
|
{
|
|
printf("cdrom: irq [");
|
|
for (rf=res_queue; ((rf) && (rf->next)); rf=rf->next);
|
|
printf("%d ",rf->res);
|
|
printf("]\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::add_result(command_result *res)
|
|
{
|
|
command_result *rf;
|
|
|
|
if (res_queue)
|
|
{
|
|
for (rf=res_queue; rf->next; rf=rf->next);
|
|
rf->next=res;
|
|
} else
|
|
{
|
|
res_queue=res;
|
|
}
|
|
|
|
res->next=NULL;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::send_result(const unsigned int res,
|
|
const unsigned char *data,
|
|
const unsigned int sz,
|
|
const unsigned int delay)
|
|
{
|
|
command_result *cr=global_alloc(command_result);
|
|
|
|
cr->res=res;
|
|
if (sz)
|
|
{
|
|
assert(sz<sizeof(cr->data));
|
|
memcpy(cr->data,data,sz);
|
|
cr->sz=sz;
|
|
} else
|
|
{
|
|
cr->data[0]=status;
|
|
cr->sz=1;
|
|
}
|
|
status &= ~status_error;
|
|
|
|
// Avoid returning results after sector read results -
|
|
// delay the sector read slightly if necessary
|
|
|
|
UINT64 systime = m_maincpu->total_cycles();
|
|
if ((next_read_event != -1) && ((systime+delay)>(next_sector_t)))
|
|
{
|
|
UINT32 hz = m_sysclock / (delay + 2000);
|
|
m_timers[next_read_event]->adjust(attotime::from_hz(hz), 0, attotime::never);
|
|
}
|
|
|
|
add_system_event(event_cmd_complete, delay, (void *)cr);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::start_dma(UINT8 *mainram, UINT32 size)
|
|
{
|
|
if ((int)size>secleft)
|
|
{
|
|
logerror("cdrom: dma past end of sector (secleft=%d sz=%d)\n",secleft,size);
|
|
}
|
|
|
|
// printf("cdrom: start dma %d bytes, %d remaining, secptr %p\n", size, secleft, secptr);
|
|
#ifdef debug_cdrom
|
|
if (size==12)
|
|
{
|
|
char poo[1024],*pp=poo;
|
|
for (unsigned int i=0; i<12; i++)
|
|
{
|
|
sprintf(pp,"%02x ",secptr[i]);
|
|
pp+=3;
|
|
}
|
|
printf("cdrom: data=%s\n",poo);
|
|
}
|
|
#endif
|
|
|
|
memcpy(mainram, secptr, size);
|
|
memset(secptr, 0xff, size);
|
|
|
|
secptr += size;
|
|
secleft -= size;
|
|
|
|
if (secleft<0) secleft=0;
|
|
if (secleft==0) sr&=~0x40;
|
|
}
|
|
|
|
bool psxcd_device::read_next_sector()
|
|
{
|
|
UINT32 pos=msf_to_lba_ps(curpos.w);
|
|
unsigned char *buf=secbuf[sechead];
|
|
|
|
// printf("read_next_sector: sec %d, sechead %d, raw_sector_size %d\n", pos, sechead, raw_sector_size);
|
|
if (cdrom_read_data(m_cdrom_handle, pos, buf, CD_TRACK_RAW_DONTCARE))
|
|
{
|
|
// printf("buf contents = %02x %02x | %02x %02x\n", buf[0], buf[1], buf[0x20], buf[0x21]);
|
|
|
|
sechead=(sechead+1)&(sector_buffer_size-1);
|
|
|
|
memcpy(lastsechdr,&secbuf[sectail][12],8);
|
|
|
|
return true;
|
|
} else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::read_sector()
|
|
{
|
|
next_read_event=-1;
|
|
|
|
if (status & status_reading)
|
|
{
|
|
bool isend=false;
|
|
|
|
if (read_next_sector())
|
|
{
|
|
unsigned char *rawsec;
|
|
|
|
if ((mode&mode_adpcm) && (streaming))
|
|
{
|
|
rawsec=secbuf[sectail];
|
|
secptr=rawsec+24;
|
|
secleft=2048;
|
|
}
|
|
else
|
|
{
|
|
rawsec=secbuf[sectail];
|
|
secptr=rawsec+secskip;
|
|
secleft=secsize;
|
|
}
|
|
|
|
sectail=(sectail+1)&(sector_buffer_size-1);
|
|
|
|
subheader *sub=(subheader *)(rawsec+16);
|
|
|
|
#ifdef dump_subheader
|
|
printf("cdrom: subheader file=%02x chan=%02x submode=%02x coding=%02x [%02x%02x%02x%02x]\n",
|
|
sub->file,
|
|
sub->channel,
|
|
sub->submode,
|
|
sub->coding,
|
|
rawsec[0xc],
|
|
rawsec[0xd],
|
|
rawsec[0xe],
|
|
rawsec[0xf]);
|
|
#endif
|
|
|
|
status&=~status_playing;
|
|
bool isxa=((mode&mode_adpcm) && (sub->submode&submode_audio));
|
|
|
|
if (((mode&mode_channel)==0) ||
|
|
((sub->file==filter_file) && (sub->channel==filter_channel)))
|
|
{
|
|
if (isxa)
|
|
{
|
|
if (sub->submode&submode_eof)
|
|
{
|
|
isend=true;
|
|
//printf("end of file\n");
|
|
}
|
|
m_spu->play_xa(0,rawsec+16);
|
|
|
|
status|=status_playing;
|
|
}
|
|
}
|
|
|
|
if ((mode&mode_autopause)==0)
|
|
isend=false;
|
|
|
|
//
|
|
|
|
curpos.b[F]++;
|
|
if (curpos.b[F]==75)
|
|
{
|
|
curpos.b[F]=0;
|
|
curpos.b[S]++;
|
|
if (curpos.b[S]==60)
|
|
{
|
|
curpos.b[S]=0;
|
|
curpos.b[M]++;
|
|
}
|
|
}
|
|
|
|
loc.w=curpos.w;
|
|
|
|
sr|=0x40;
|
|
|
|
if(!(streaming && isxa))
|
|
{
|
|
command_result *res=global_alloc(command_result);
|
|
res->res=isend?intr_dataend:intr_dataready;
|
|
res->data[0]=status;
|
|
res->sz=1;
|
|
cmd_complete(res);
|
|
}
|
|
}
|
|
|
|
if (! isend)
|
|
{
|
|
unsigned int cyc=read_sector_cycles;
|
|
if (mode&mode_double_speed) cyc>>=1;
|
|
|
|
next_sector_t+=cyc;
|
|
|
|
next_read_event = add_system_event(event_read_sector, cyc, NULL);
|
|
|
|
//read_next_sector();
|
|
} else
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("autopause xa\n");
|
|
#endif
|
|
stop_read();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::play_sector()
|
|
{
|
|
next_read_event=-1;
|
|
|
|
if (status&status_playing)
|
|
{
|
|
if(!m_mute)
|
|
m_spu->play_cdda(0,secbuf[sectail]);
|
|
sectail=(sectail+1)&(sector_buffer_size-1);
|
|
|
|
curpos.b[F]++;
|
|
if (curpos.b[F]==75)
|
|
{
|
|
curpos.b[F]=0;
|
|
curpos.b[S]++;
|
|
if (curpos.b[S]==60)
|
|
{
|
|
curpos.b[S]=0;
|
|
curpos.b[M]++;
|
|
}
|
|
}
|
|
|
|
loc.w=curpos.w;
|
|
|
|
//
|
|
|
|
UINT32 sector = msf_to_lba_ps(loc.w);
|
|
|
|
if (mode&mode_autopause)
|
|
{
|
|
if (sector>=autopause_sector)
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("autopause cdda\n");
|
|
#endif
|
|
stop_read();
|
|
command_result *res=global_alloc(command_result);
|
|
res->res=intr_dataend;
|
|
res->data[0]=status;
|
|
res->sz=1;
|
|
cmd_complete(res);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((mode&mode_report) && !(sector & 15)) // slow the int rate
|
|
{
|
|
command_result *res=global_alloc(command_result);
|
|
UINT8 track = cdrom_get_track(m_cdrom_handle, sector) + 1;
|
|
res->res=intr_dataready;
|
|
|
|
res->data[0]=status;
|
|
res->data[1]=decimal_to_bcd(track);
|
|
res->data[2]=1;
|
|
if(sector & 0x10)
|
|
{
|
|
CDPOS tloc, start;
|
|
start.w = (track == 1) ? 0x000200 : lba_to_msf_ps(cdrom_get_track_start(m_cdrom_handle, track - 1));
|
|
tloc.w = sub_loc(loc, start);
|
|
res->data[3]=decimal_to_bcd(tloc.b[M]);
|
|
res->data[4]=decimal_to_bcd(tloc.b[S]) | 0x80;
|
|
res->data[5]=decimal_to_bcd(tloc.b[F]);
|
|
}
|
|
else
|
|
{
|
|
res->data[3]=decimal_to_bcd(loc.b[M]);
|
|
res->data[4]=decimal_to_bcd(loc.b[S]);
|
|
res->data[5]=decimal_to_bcd(loc.b[F]);
|
|
}
|
|
|
|
res->sz=8;
|
|
|
|
cmd_complete(res);
|
|
}
|
|
|
|
unsigned int cyc=read_sector_cycles;
|
|
|
|
next_sector_t+=cyc>>1;
|
|
|
|
next_read_event = add_system_event(event_play_sector, next_sector_t - m_maincpu->total_cycles(), NULL);
|
|
|
|
if(!read_next_sector())
|
|
{
|
|
stop_read(); // assume we've reached the end
|
|
command_result *res=global_alloc(command_result);
|
|
res->res=intr_dataend;
|
|
res->data[0]=status;
|
|
res->sz=1;
|
|
cmd_complete(res);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::preread_sector()
|
|
{
|
|
UINT64 next_clock;
|
|
int type;
|
|
next_read_event=-1;
|
|
|
|
UINT32 pos=msf_to_lba_ps(curpos.w);
|
|
//
|
|
if(!(mode & mode_cdda) && (cdrom_get_track_type(m_cdrom_handle, cdrom_get_track(m_cdrom_handle, pos + 150)) == CD_TRACK_AUDIO))
|
|
{
|
|
command_result *res=global_alloc(command_result);
|
|
res->res=intr_diskerror;
|
|
res->data[0]=status | status_error;
|
|
res->data[1]=0x40;
|
|
res->sz=2;
|
|
cmd_complete(res);
|
|
return;
|
|
}
|
|
|
|
unsigned char *buf=secbuf[sechead];
|
|
if (! cdrom_read_data(m_cdrom_handle, pos, buf, CD_TRACK_RAW_DONTCARE))
|
|
{
|
|
next_clock=(m_sysclock/60);
|
|
type=event_preread_sector;
|
|
|
|
unsigned int cyc=read_sector_cycles;
|
|
if (mode&mode_double_speed) cyc>>=1;
|
|
next_sector_t=next_clock+(cyc-preread_delay)+m_maincpu->total_cycles();
|
|
} else
|
|
{
|
|
memcpy(lastsechdr,buf+12,8);
|
|
|
|
//
|
|
|
|
command_result *res=global_alloc(command_result);
|
|
res->res=intr_complete;
|
|
res->data[0]=status;
|
|
res->sz=1;
|
|
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: read acknowledge\n");
|
|
#endif
|
|
|
|
cmd_complete(res);
|
|
|
|
//
|
|
|
|
next_clock=next_sector_t - m_maincpu->total_cycles();
|
|
type=event_read_sector;
|
|
|
|
//read_next_sector();
|
|
}
|
|
|
|
next_read_event = add_system_event(type, next_clock, NULL);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::start_read()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: start read\n");
|
|
#endif
|
|
|
|
assert((status&(status_reading|status_playing))==0);
|
|
|
|
status|=status_reading;
|
|
|
|
sechead=sectail=0;
|
|
|
|
unsigned int cyc=read_sector_cycles;
|
|
if (mode&mode_double_speed) cyc>>=1;
|
|
|
|
INT64 systime=m_maincpu->total_cycles();
|
|
|
|
systime+=start_read_delay;
|
|
|
|
next_sector_t=systime+cyc;
|
|
next_read_event = add_system_event(event_preread_sector, start_read_delay+preread_delay, NULL);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::start_streaming()
|
|
{
|
|
assert(! streaming);
|
|
|
|
streaming=true;
|
|
start_read();
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::start_play()
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom: start play\n");
|
|
#endif
|
|
|
|
status|=status_playing;
|
|
|
|
sechead=sectail=0;
|
|
|
|
if (mode&mode_autopause)
|
|
{
|
|
UINT8 track = cdrom_get_track(m_cdrom_handle, msf_to_lba_ps(curpos.w) + 150);
|
|
autopause_sector = cdrom_get_track_start(m_cdrom_handle, track) + cdrom_get_toc(m_cdrom_handle)->tracks[track].frames;
|
|
// printf("pos=%d auto=%d\n",pos,autopause_sector);
|
|
}
|
|
|
|
unsigned int cyc=read_sector_cycles;
|
|
|
|
next_sector_t=m_maincpu->total_cycles()+cyc;
|
|
|
|
next_sector_t+=cyc>>1;
|
|
|
|
next_read_event = add_system_event(event_play_sector, next_sector_t - m_maincpu->total_cycles(), NULL);
|
|
|
|
read_next_sector();
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void psxcd_device::stop_read()
|
|
{
|
|
#ifdef debug_cdrom
|
|
if (status&status_reading)
|
|
printf("cdrom: stop read\n");
|
|
#endif
|
|
status&=~(status_reading|status_playing);
|
|
streaming=false;
|
|
|
|
if (next_read_event != -1)
|
|
{
|
|
m_timers[next_read_event]->adjust(attotime::never, 0, attotime::never);
|
|
m_timerinuse[next_read_event] = false;
|
|
next_read_event = -1;
|
|
}
|
|
|
|
UINT32 sector=msf_to_lba_ps(curpos.w);
|
|
m_spu->flush_xa(sector);
|
|
m_spu->flush_cdda(sector);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void psxcd_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
|
|
{
|
|
if (!m_timerinuse[tid])
|
|
{
|
|
printf("cdrom:: timer fired for free event\n");
|
|
return;
|
|
}
|
|
|
|
m_timerinuse[tid] = false;
|
|
switch (param)
|
|
{
|
|
case event_cmd_complete:
|
|
{
|
|
#ifdef debug_cdrom
|
|
printf("cdrom:: event cmd complete\n");
|
|
#endif
|
|
|
|
cmd_complete((command_result *)ptr);
|
|
break;
|
|
}
|
|
|
|
case event_preread_sector:
|
|
preread_sector();
|
|
break;
|
|
|
|
case event_read_sector:
|
|
read_sector();
|
|
break;
|
|
|
|
case event_play_sector:
|
|
play_sector();
|
|
break;
|
|
|
|
case event_change_disk:
|
|
open = false;
|
|
status |= status_standby;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int psxcd_device::add_system_event(int type, UINT64 t, void *ptr)
|
|
{
|
|
// t is in maincpu clock cycles
|
|
UINT32 hz = m_sysclock / t;
|
|
// printf("add_system_event: event type %d for %d hz (using timer %d)\n", ev->type, hz, tnum);
|
|
for(int i = 0; i < MAX_PSXCD_TIMERS; i++)
|
|
{
|
|
if(!m_timerinuse[i])
|
|
{
|
|
m_timers[i]->adjust(attotime::from_hz(hz), type, attotime::never);
|
|
m_timers[i]->set_ptr(ptr);
|
|
m_timerinuse[i] = true;
|
|
return i;
|
|
}
|
|
}
|
|
fatalerror("psxcd: out of timers\n");
|
|
return 0;
|
|
}
|