diff options
Diffstat (limited to 'src/mess/machine/psxcd.c')
-rw-r--r-- | src/mess/machine/psxcd.c | 1686 |
1 files changed, 1686 insertions, 0 deletions
diff --git a/src/mess/machine/psxcd.c b/src/mess/machine/psxcd.c new file mode 100644 index 00000000000..e1d8eeb2e3f --- /dev/null +++ b/src/mess/machine/psxcd.c @@ -0,0 +1,1686 @@ +#include "emu.h" +#include "includes/psx.h" +#include "psxcd.h" +#include "machine/devhelpr.h" +#include "sound/spu.h" +#include "debugger.h" + +// +// +// + +//#define debug_cdrom +//#define debug_cdrom_registers +//#define skip_reads +//#define dump_subheader +//#define disable_xa_prefetch +//#define disable_cdda_prefetch + +// +// +// + +enum cdrom_events +{ + event_cmd_complete=0, + event_preread_sector, + event_read_sector, + event_play_sector +}; + +// +// +// + +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_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 +}; + +// +// +// + +static const unsigned int max_xa_prefetch_distance=32, + max_cdda_prefetch_distance=32; + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type PSXCD = &device_creator<psxcd_device>; + +void psxcd_device::static_set_devname(device_t &device, const char *devname) +{ + psxcd_device &psxcd = downcast<psxcd_device &>(device); + psxcd.m_devname = devname; +} + +psxcd_device::psxcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, PSXCD, "PSXCD", tag, owner, clock) +{ +} + +void psxcd_device::device_start() +{ + unsigned int sysclk=machine().device<cpu_device>("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; + secin = 0; + secskip = 0; + next_read_event = NULL; + cbp = cmdbuf; + first_open = true; + + status=status_standby; + sr=8|1; + res=0; + ir=0; + mode=0; + + driver = NULL; + + for (int i = 0; i < MAX_PSXCD_TIMERS; i++) + { + m_timers[i] = timer_alloc(i, NULL); + m_timerinuse[i] = false; + } +} + +// +// +// + +void psxcd_device::device_reset() +{ + stop_read(); + open=false; + + for (int i = 0; i < MAX_PSXCD_TIMERS; i++) + { + m_timerinuse[i] = false; + } + + m_cddevice = machine().device<cdrom_image_device>(m_devname); + if (m_cddevice) + { + m_cd = m_cddevice->get_cdrom_file(); + + if (m_cd) + { +// printf("psxcd: found disc!\n"); + driver = open_mess_drv(); + driver->set_machine(machine()); + driver->set_cdrom_file(m_cd); + } + else + { + driver = NULL; +// printf("psxcd: Found device, but no disc\n"); + } + } + else + { + driver = NULL; +// printf("psxcd: Device [%s] not found!\n", m_devname); + } + +} + +// +// +// + +unsigned char psxcd_device::read_byte(const unsigned int addr) +{ + unsigned char ret = 0; + + switch (addr&3) + { + 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=%x)\n",addr,ret,cpu_get_pc(machine().device("maincpu"))); + #endif + + return ret; +} + +// +// +// + +void psxcd_device::write_byte(const unsigned int addr, const unsigned char byte) +{ + #ifdef debug_cdrom_registers + printf("cdrom: write byte %08x = %02x (PC=%x)\n",addr,byte,cpu_get_pc(machine().device("maincpu"))); + #endif + + switch (addr&3) + { + case 0: + cmdmode=byte&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(byte); + } + break; + + case 2: + if (cmdmode==0) + { + *cbp++=byte; + } else + { + // ?flush buffer? + } + break; + + case 3: + if (byte==0x07) + { + if (cur_res) + { + global_free(cur_res); + cur_res=NULL; + sr&=~(1<<5); + } + ir=0; + } + break; + } +} + +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)(); +} + +// +// +// + +void psxcd_device::cdcmd_sync() +{ + #ifdef debug_cdrom + printf("cdrom: sync\n"); + #endif + + stop_read(); + send_result(intr_acknowledge); +} + +static int open_nops=0; + +void psxcd_device::cdcmd_nop() +{ + #ifdef debug_cdrom + printf("cdrom: nop\n"); + #endif + + //stop_read(); + + if ((! open) && (driver)) + { + if (open_nops==0) + { + open=driver->read_toc(); + } else + { + open_nops--; + } + } + + 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(); + + unsigned char l[3]; + l[0]=bcd_to_decimal(cmdbuf[0]); + l[1]=bcd_to_decimal(cmdbuf[1]); + l[2]=bcd_to_decimal(cmdbuf[2]); + + if ((l[0]>0) || (l[1]>=2)) + { + loc[0]=l[0]; + loc[1]=l[1]; + loc[2]=l[2]; + } else + { + printf("setloc out of range: %02d:%02d:%02d\n", + l[0],l[1],l[2]); + } + + send_result(intr_complete); +} + +void psxcd_device::cdcmd_play() +{ + #ifdef debug_cdrom + printf("cdrom: play %02x %02x %02x => %d\n", loc[0], loc[1], loc[2], msf_to_sector(loc)); + #endif + + curpos[0]=loc[0]; + curpos[1]=loc[1]; + curpos[2]=loc[2]; + + if ((curpos[0]==0) && + (curpos[1]==0) && + (curpos[2]==0)) + { + send_result(intr_acknowledge); + } else + { + stop_read(); + start_play(); + send_result(intr_acknowledge); + } +} + +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 (driver) + { + #ifdef debug_cdrom + printf("cdrom: readn\n"); + #endif + + curpos[0]=loc[0]; + curpos[1]=loc[1]; + curpos[2]=loc[2]; + + stop_read(); + start_read(); + send_result(intr_complete); + } else + { + printf("read without driver\n"); + 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 + + send_result(intr_acknowledge); +} + +void psxcd_device::cdcmd_demute() +{ + #ifdef debug_cdrom + printf("cdrom: demute\n"); + #endif + + 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); +} + +#if 0 +static void add_loc(unsigned char *dst, const unsigned char *src1, const unsigned char *src2) +{ + int f=src1[2]+src2[2], + s=src1[1]+src2[1], + m=src1[0]+src2[0]; + while (f>=75) { s++; f-=75; } + while (s>=60) { m++; s-=60; } + + dst[0]=m; + dst[1]=s; + dst[2]=f; +} +#endif +static void sub_loc(unsigned char *dst, const unsigned char *src1, const unsigned char *src2) +{ + int f=src1[2]-src2[2], + s=src1[1]-src2[1], + m=src1[0]-src2[0]; + while (f<0) { s--; f+=75; } + while (s<0) { m--; s+=60; } + + if (m<0) + m=s=f=0; + + dst[0]=m; + dst[1]=s; + dst[2]=f; +} + +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() +{ + unsigned char tloc[3], + twosec[3]={ 0,2,0 }; + sub_loc(tloc,loc,twosec); + + unsigned char data[8]= + { + 0x01, // track + 0x01, // index + decimal_to_bcd(tloc[0]), // min + decimal_to_bcd(tloc[1]), // sec + decimal_to_bcd(tloc[2]), // frame + decimal_to_bcd(loc[0]), // amin + decimal_to_bcd(loc[1]), // asec + decimal_to_bcd(loc[2]) // 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 + + assert(driver); + + unsigned char data[3]= + { + status, + decimal_to_bcd(driver->get_first_track()), + decimal_to_bcd(driver->get_num_tracks()) + }; + + //stop_read(); + send_result(intr_acknowledge,data,3); +} + +void psxcd_device::cdcmd_gettd() +{ + unsigned char addr[3]; + driver->get_track_address(bcd_to_decimal(cmdbuf[0]), addr); + + unsigned char data[3]= + { + status, + decimal_to_bcd(addr[0]), + decimal_to_bcd(addr[1]) + }; + + #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); +} + +void psxcd_device::cdcmd_seekl() +{ + #ifdef debug_cdrom + printf("cdrom: seekl [%02d:%02d:%02d]\n",loc[0],loc[1],loc[2]); + #endif + + curpos[0]=loc[0]; + curpos[1]=loc[1]; + curpos[2]=loc[2]; + + send_result(intr_acknowledge); +} + +void psxcd_device::cdcmd_seekp() +{ + #ifdef debug_cdrom + printf("cdrom: seekp\n"); + #endif + + curpos[0]=loc[0]; + curpos[1]=loc[1]; + curpos[2]=loc[2]; + + 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) + { + if (driver) + open=driver->read_toc(); + } + + if (open) + { + static unsigned char gamedata[8] = { 0x08, 0x00, 0x00, 0x00, 'S', 'C', 'E', 'A' }; + static unsigned char audiodata[8] = { 0x08, 0x90, 0x00, 0x00, 'S', 'C', 'E', 'A' }; // drops into the audio CD player. 08 80 goes to the menu. + + if (cdrom_get_track_type(m_cd, 0) == CD_TRACK_AUDIO) + { + send_result(intr_acknowledge,audiodata,8); + } + else + { + send_result(intr_acknowledge,gamedata,8); + } + } else + { + status=status_error|status_shellopen; + send_result(intr_diskerror); + } +} + +void psxcd_device::cdcmd_reads() +{ + curpos[0]=loc[0]; + curpos[1]=loc[1]; + curpos[2]=loc[2]; + + #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) + { + psx_irq_set(machine(), 0x0004); + } + + 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; +} + +// +// +// + +event *psxcd_device::send_result(const unsigned int res, + const unsigned char *data, + const unsigned int sz, + const unsigned int delay) +{ + // Update shell open status + + if (! open) + { + status=status_error|status_shellopen; + } + else + { + if (status&status_shellopen) + status=status_standby; + } + + // Prepare event + + event *ev=new event; + ev->t=delay; + ev->type=event_cmd_complete; + + command_result *cr=(command_result *)ev->data; + + 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; + } + + // Avoid returning results after sector read results - + // delay the sector read slightly if necessary + + UINT64 systime = machine().device<cpu_device>("maincpu")->total_cycles(); + if ((next_read_event) && ((systime+ev->t)>(next_sector_t))) + { + UINT32 hz = m_sysclock / (delay + 2000); + next_read_event->timer->adjust(attotime::from_hz(hz), 0, attotime::never); + + for (int i = 0; i < MAX_PSXCD_TIMERS; i++) + { + if (m_eventfortimer[i] == next_read_event) + { + printf("Adjusting timer %d to %d hz]n", i, hz); + } + } + } + + add_system_event(ev); + return ev; +} + +// +// +// + +void psxcd_device::start_dma(UINT8 *mainram, UINT32 size) +{ + if ((int)size>secleft) + { + printf("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; +} + +// +// +// + +void psxcd_device::prefetch_next_sector() +{ + unsigned int pos=msf_to_sector(curpos); + assert(driver); + driver->prefetch_sector(pos); +} + +// +// +// + +bool psxcd_device::read_next_sector(const bool block) +{ + unsigned int pos=msf_to_sector(curpos); + unsigned char *buf=&secbuf[sechead*raw_sector_size]; + assert(driver); + assert(secin<sector_buffer_size); + +// printf("read_next_sector: sec %d, sechead %d, raw_sector_size %d\n", pos, sechead, raw_sector_size); + if (driver->read_sector(pos, buf, block)) + { +// printf("buf contents = %02x %02x | %02x %02x\n", buf[0], buf[1], buf[0x20], buf[0x21]); + + sechead=(sechead+1)&(sector_buffer_size-1); + secin++; + + memcpy(lastsechdr,&secbuf[raw_sector_size*sectail]+12,8); + + return true; + } else + { + return false; + } +} + +// +// +// + +void psxcd_device::read_sector() +{ + next_read_event=NULL; + + if (status & status_reading) + { + bool isend=false; + + if (read_next_sector(false)) + { + unsigned char *rawsec; + + if ((mode&mode_adpcm) && (streaming)) + { + rawsec=&secbuf[raw_sector_size*sectail]; + secptr=rawsec+24; + secleft=2048; + } + else + { + rawsec=&secbuf[raw_sector_size*sectail]; + secptr=rawsec+secskip; + secleft=secsize; + } + + secin--; + sectail=(sectail+1)&(sector_buffer_size-1); + + // + + bool isxa=false; + + 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; + 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"); + } + + #ifndef disable_xa_prefetch + unsigned int cursec=msf_to_sector(curpos); + + if (xa_prefetch_sector==-1) + { + xa_prefetch_sector=cursec; + machine().device<spu_device>("spu")->flush_xa(); + } + + unsigned char *xaptr; + unsigned int xasecsz; + + while (1) + { + int dist=xa_prefetch_sector-cursec; + if (dist>max_xa_prefetch_distance) break; + + xaptr=driver->get_prefetch_sector(xa_prefetch_sector,&xasecsz); + if (! xaptr) break; + + switch (xasecsz) + { + case 2336: + break; + + default: + xaptr+=16; + break; + } + + subheader *xasub=(subheader *)xaptr; + + if ((xasub->submode&submode_audio) && + (((mode&mode_channel)==0) || + ((xasub->file==sub->file) && + (xasub->channel==sub->channel)))) + { + if (! machine().device<spu_device>("spu")->play_xa(xa_prefetch_sector,xaptr)) + break; + } + xa_prefetch_sector++; + } + #else + machine().device<spu_device>("spu")->play_xa(0,rawsec+16); + #endif + + status|=status_playing; + } + } + + if ((mode&mode_autopause)==0) + isend=false; + + // + + curpos[2]++; + if (curpos[2]==75) + { + curpos[2]=0; + curpos[1]++; + if (curpos[1]==60) + { + curpos[1]=0; + curpos[0]++; + } + } + + loc[0]=curpos[0]; + loc[1]=curpos[1]; + loc[2]=curpos[2]; + + // + + command_result *res=new command_result; + res->res=isend?intr_dataend:intr_dataready; + res->data[0]=status; + res->sz=1; + + sr|=0x40; + + if ((streaming) && (isxa)) + { + global_free(res); + res=NULL; + } + + if (res) + { + #ifdef debug_cdrom + printf("cdrom:: data ready\n"); + #endif + + cmd_complete(res); + } + } + + if (! isend) + { + unsigned int cyc=read_sector_cycles; + if (mode&mode_double_speed) cyc>>=1; + + next_sector_t+=cyc; + + event *ev=new event; + ev->t=cyc; + ev->type=event_read_sector; + next_read_event=ev; + add_system_event(ev); + + //read_next_sector(); + } else + { + printf("autopause xa\n"); + stop_read(); + } + } +} + +// +// +// + +bool psxcd_device::play_cdda_sector(const unsigned int sector, + unsigned char *rawsec) +{ + bool isdata=true; + + if (rawsec[0]!=0) + { + isdata=false; + } else + { + for (int i=0; i<10; i++) + if (rawsec[i+1]!=0xff) + { + isdata=false; + break; + } + } + + if (! isdata) + { + return machine().device<spu_device>("spu")->play_cdda(sector,rawsec); + } else + { + return true; + } +} + +// +// +// + +void psxcd_device::play_sector() +{ + next_read_event=NULL; + + if (status&status_playing) + { + #ifdef disable_cdda_prefetch + unsigned char *rawsec=&secbuf[raw_sector_size*sectail]; + #endif + secin--; + sectail=(sectail+1)&(sector_buffer_size-1); + + // + + #ifndef disable_cdda_prefetch + unsigned int cursec=msf_to_sector(curpos); + + if (cdda_prefetch_sector==-1) + { + cdda_prefetch_sector=cursec; + machine().device<spu_device>("spu")->flush_cdda(); + } + + unsigned char *cddaptr; +// bool full=false; + unsigned int cddasecsz; + + while (1) + { + int dist=cdda_prefetch_sector-cursec; + if (dist>max_cdda_prefetch_distance) break; + + cddaptr=driver->get_prefetch_sector(cdda_prefetch_sector,&cddasecsz); + if (! cddaptr) break; + + if (! play_cdda_sector(cdda_prefetch_sector,cddaptr)) + break; + + cdda_prefetch_sector++; + } + #else + play_cdda_sector(0,rawsec); + #endif + + // + + curpos[2]++; + if (curpos[2]==75) + { + curpos[2]=0; + curpos[1]++; + if (curpos[1]==60) + { + curpos[1]=0; + curpos[0]++; + } + } + + loc[0]=curpos[0]; + loc[1]=curpos[1]; + loc[2]=curpos[2]; + + // + + if (mode&mode_autopause) + { + if (msf_to_sector(loc)>=autopause_sector) + { + printf("autopause cdda\n"); + + command_result *res=new command_result; + res->res=intr_dataend; + res->data[0]=status_standby; + res->sz=1; + cmd_complete(res); + stop_read(); + return; + } + } + + if (mode&mode_report) + { + command_result *res=new command_result; + res->res=intr_complete; //dataready; + + res->data[0]=status_playing|status_standby; + res->data[1]=0x01; + res->data[2]=0x80; + res->data[3]=decimal_to_bcd(loc[0]); + res->data[4]=decimal_to_bcd(loc[1])|0x80; + res->data[5]=decimal_to_bcd(loc[2]); + + res->sz=8; + + cmd_complete(res); + } + + unsigned int cyc=read_sector_cycles; + + event *ev=new event; + ev->t=next_sector_t - machine().device<cpu_device>("maincpu")->total_cycles(); + ev->type=event_play_sector; + + next_sector_t+=cyc; + + next_read_event=ev; + add_system_event(ev); + + read_next_sector(); + } +} + + +// +// +// + +void psxcd_device::preread_sector() +{ + next_read_event=NULL; + + // + + event *ev=new event; + + unsigned int pos=msf_to_sector(curpos); + unsigned char *buf=&secbuf[sechead*raw_sector_size]; + if (! driver->read_sector(pos,buf,false)) + { + ev->t=(m_sysclock/60); + ev->type=event_preread_sector; + + unsigned int cyc=read_sector_cycles; + if (mode&mode_double_speed) cyc>>=1; + next_sector_t=ev->t+(cyc-preread_delay)+machine().device<cpu_device>("maincpu")->total_cycles(); + } else + { + memcpy(lastsechdr,buf+12,8); + + // + + command_result *res=new 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); + + // + + ev->t=next_sector_t - machine().device<cpu_device>("maincpu")->total_cycles(); + ev->type=event_read_sector; + + //read_next_sector(); + } + + next_read_event=ev; + add_system_event(ev); +} + +// +// +// + +void psxcd_device::start_read() +{ + #ifdef debug_cdrom + printf("cdrom: start read\n"); + #endif + + assert((status&(status_reading|status_playing))==0); + + status|=status_reading; + + secin=sechead=sectail=0; + xa_prefetch_sector=-1; + + unsigned int cyc=read_sector_cycles; + if (mode&mode_double_speed) cyc>>=1; + + INT64 systime=machine().device<cpu_device>("maincpu")->total_cycles(); + + systime+=start_read_delay; + + event *ev=new event; + ev->t=start_read_delay+preread_delay; + ev->type=event_preread_sector; + + next_sector_t=systime+cyc; + next_read_event=ev; + add_system_event(ev); + + prefetch_next_sector(); +} + +// +// +// + +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; + + secin=sechead=sectail=0; + cdda_prefetch_sector=-1; + + if (mode&mode_autopause) + { + unsigned int pos=msf_to_sector(curpos); + driver->find_track(pos+150,NULL,&autopause_sector); +// printf("pos=%d auto=%d\n",pos,autopause_sector); + } + + unsigned int cyc=read_sector_cycles; + + next_sector_t=machine().device<cpu_device>("maincpu")->total_cycles()+cyc; + + event *ev=new event; + ev->t=next_sector_t - machine().device<cpu_device>("maincpu")->total_cycles(); + ev->type=event_play_sector; + + next_sector_t+=cyc; + + next_read_event=ev; + add_system_event(ev); + + 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) + { + next_read_event->timer->adjust(attotime::never, 0, attotime::never); + for (int i = 0; i < MAX_PSXCD_TIMERS; i++) + { + if (m_timers[i] == next_read_event->timer) + { + m_timerinuse[i] = false; + m_eventfortimer[i] = NULL; + break; + } + } + global_free(next_read_event); + next_read_event=NULL; + } + + unsigned int sector=msf_to_sector(curpos); + machine().device<spu_device>("spu")->flush_xa(sector); + machine().device<spu_device>("spu")->flush_cdda(sector); +} + +// +// +// + +void psxcd_device::set_driver(cdrom_driver *d) +{ + char err[1024]; + + if (d) + { + if (d->is_usable(err,1024)) + { + driver=d; + open=false; + + if (! first_open) + { + command_result *res=new command_result; + res->res=intr_acknowledge; + res->data[0]=status_standby; + res->sz=1; + + cmd_complete(res); + + open_nops=10; + } + + first_open=false; + } + } else + { + driver=NULL; + open=false; + + command_result *res=new command_result; + res->res=intr_acknowledge; + res->data[0]=status_shellopen; + res->sz=1; + + cmd_complete(res); + } +} + +// +// +// +void psxcd_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + event *ev = m_eventfortimer[tid]; + +// printf("timer %d fired, performing event type %d\n", tid, ev->type); + + switch (ev->type) + { + case event_cmd_complete: + { + #ifdef debug_cdrom + printf("cdrom:: event cmd complete\n"); + #endif + + command_result *res=new command_result; + memcpy(res,ev->data,sizeof(command_result)); + cmd_complete(res); + break; + } + + case event_preread_sector: + preread_sector(); + break; + + case event_read_sector: + read_sector(); + break; + + case event_play_sector: + play_sector(); + break; + } + + // free the timer +// printf("Freeing timer %d\n", tid); + m_timers[tid]->adjust(attotime::never, 0, attotime::never); + m_timerinuse[tid] = false; + m_eventfortimer[tid] = NULL; + + global_free(ev); +} + +void psxcd_device::add_system_event(event *ev) +{ + emu_timer *timer = NULL; + int tnum = -1; + + // allocate a timer for this event + for (int i = 0; i < MAX_PSXCD_TIMERS; i++) + { + if (!m_timerinuse[i]) + { + tnum = i; + timer = m_timers[i]; + m_timerinuse[i] = true; + m_eventfortimer[i] = ev; + break; + } + } + + if (tnum == -1) + { + fatalerror("PSXCD: ran out of timers!\n"); + } + + // ev->t is in maincpu clock cycles + UINT32 hz = m_sysclock / ev->t; +// printf("add_system_event: event type %d for %d hz (using timer %d)\n", ev->type, hz, tnum); + timer->adjust(attotime::from_hz(hz), tnum, attotime::never); + + // back-reference the timer from the event + ev->timer = timer; +} + |