// license:BSD-3-Clause // copyright-holders:smf,R. Belmont,pSXAuthor,Carl #include "emu.h" #include "psxcd.h" #include "debugger.h" #define VERBOSE_LEVEL ( 0 ) static inline void ATTR_PRINTF(3,4) verboselog( device_t& device, int n_level, const char *s_fmt, ... ) { if( VERBOSE_LEVEL >= n_level ) { va_list v; char buf[ 32768 ]; va_start( v, s_fmt ); vsprintf( buf, s_fmt, v ); va_end( v ); device.logerror( "%s: %s", device.machine().describe_context(), buf ); } } enum cdrom_events { event_cmd_complete=0, 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_mask=0x30, 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 { uint8_t 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 //************************************************************************** DEFINE_DEVICE_TYPE(PSXCD, psxcd_device, "psx_cd", "PSX CD-ROM") psxcd_device::psxcd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cdrom_image_device(mconfig, PSXCD, tag, owner, clock), m_irq_handler(*this) { set_interface("psx_cdrom"); } void psxcd_device::device_start() { cdrom_image_device::device_start(); m_irq_handler.resolve_safe(); m_maincpu = machine().device("maincpu"); m_spu = machine().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; res_queue = nullptr; rdp = 0; status=status_shellopen; mode=0; for (int i = 0; i < MAX_PSXCD_TIMERS; i++) { m_timers[i] = timer_alloc(i); m_timerinuse[i] = false; } save_item(NAME(cmdbuf)); save_item(NAME(mode)); save_item(NAME(secbuf)); save_item(NAME(filter_file)); save_item(NAME(filter_channel)); save_item(NAME(lastsechdr)); save_item(NAME(status)); save_item(NAME(rdp)); save_item(NAME(m_cursec)); save_item(NAME(sectail)); save_item(NAME(m_transcurr)); save_item(NAME(m_transbuf)); save_item(NAME(loc.w)); save_item(NAME(curpos.w)); save_item(NAME(open)); save_item(NAME(m_mute)); save_item(NAME(m_dmaload)); save_item(NAME(next_read_event)); save_item(NAME(next_sector_t)); save_item(NAME(autopause_sector)); save_item(NAME(m_param_count)); } void psxcd_device::device_stop() { for (int i = 0; i < MAX_PSXCD_TIMERS; i++) { if(m_timerinuse[i] && m_timers[i]->ptr()) global_free((command_result *)m_timers[i]->ptr()); } while(res_queue) { command_result *res = res_queue->next; global_free(res_queue); res_queue = res; } } void psxcd_device::device_reset() { next_read_event = -1; stop_read(); for (int i = 0; i < MAX_PSXCD_TIMERS; i++) { if(m_timerinuse[i] && m_timers[i]->ptr()) global_free((command_result *)m_timers[i]->ptr()); m_timers[i]->adjust(attotime::never, 0, attotime::never); m_timerinuse[i] = false; } open = true; if(m_cdrom_handle) add_system_event(event_change_disk, m_sysclock, nullptr); while(res_queue) { command_result *res = res_queue->next; global_free(res_queue); res_queue = res; } m_param_count = 0; m_regs.sr = 0x18; m_regs.ir = 0; m_regs.imr = 0x1f; sectail = 0; m_cursec = 0; m_mute = false; m_dmaload = false; m_int1 = nullptr; curpos.w = 0; } image_init_result psxcd_device::call_load() { image_init_result ret = cdrom_image_device::call_load(); open = true; if(ret == image_init_result::PASS) add_system_event(event_change_disk, m_sysclock, nullptr); // 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 ) { uint8_t 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 = m_regs.sr; break; case 1: if ((res_queue) && (rdp < res_queue->sz)) { ret = res_queue->data[rdp++]; if(rdp == res_queue->sz) m_regs.sr &= ~0x20; else m_regs.sr |= 0x20; } else ret = 0; break; case 2: if(!m_dmaload) ret = 0; else { ret = m_transbuf[m_transcurr++]; if(m_transcurr >= raw_sector_size) { m_dmaload = false; m_regs.sr &= ~0x40; } } break; case 3: if(m_regs.sr & 1) ret = m_regs.ir | 0xe0; else ret = m_regs.imr | 0xe0; break; } verboselog(*this, 2, "psxcd: read byte %08x = %02x\n",offset,ret); return ret; } WRITE8_MEMBER( psxcd_device::write ) { verboselog(*this, 2, "psxcd: write byte %08x = %02x\n",offset,data); switch ((offset & 3) | ((m_regs.sr & 3) << 4)) { case 0x00: case 0x10: case 0x20: case 0x30: m_regs.sr = (m_regs.sr & ~3) | (data & 3); break; case 0x01: write_command(data); break; case 0x11: case 0x21: break; case 0x31: m_regs.vol.rr = data; break; case 0x02: cmdbuf[m_param_count] = data; m_param_count++; break; case 0x12: m_regs.imr = data & 0x1f; break; case 0x22: m_regs.vol.ll = data; break; case 0x32: m_regs.vol.rl = 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 0x03: if((data & 0x80) && !m_dmaload) { m_dmaload = true; memcpy(m_transbuf, secbuf[m_cursec], raw_sector_size); m_regs.sr |= 0x40; m_cursec++; m_cursec %= sector_buffer_size; switch(mode & mode_size_mask) { case 0x00: default: m_transcurr = 24; break; case 0x10: m_transcurr = 24; break; case 0x20: m_transcurr = 12; break; } #if (VERBOSE_LEVEL > 0) char str[1024]; for (int i=0; i<12; i++) sprintf(&str[i*4], "%02x ", m_transbuf[i+12]); verboselog(*this, 1, "psxcd: request data=%s\n",str); #endif } else if(!(data & 0x80)) { m_dmaload = false; m_regs.sr &= ~0x40; } break; case 0x13: if(data & 0x1f) { m_regs.ir &= ~(data & 0x1f); if(res_queue && !m_regs.ir) { command_result *res = res_queue; if(res == m_int1) m_int1 = nullptr; res_queue = res->next; global_free(res); m_regs.sr &= ~0x20; rdp = 0; if(res_queue) { m_regs.sr |= 0x20; m_regs.ir = res_queue->res; } verboselog(*this, 1, "psxcd: nextres\n"); } } if(data & 0x40) m_param_count = 0; break; case 0x23: m_regs.vol.lr = data; break; case 0x33: break; } } const psxcd_device::cdcmd psxcd_device::cmd_table[]= { &psxcd_device::cdcmd_sync, &psxcd_device::cdcmd_nop, &psxcd_device::cdcmd_setloc, &psxcd_device::cdcmd_play, &psxcd_device::cdcmd_forward, &psxcd_device::cdcmd_backward, &psxcd_device::cdcmd_readn, &psxcd_device::cdcmd_standby, &psxcd_device::cdcmd_stop, &psxcd_device::cdcmd_pause, &psxcd_device::cdcmd_init, &psxcd_device::cdcmd_mute, &psxcd_device::cdcmd_demute, &psxcd_device::cdcmd_setfilter, &psxcd_device::cdcmd_setmode, &psxcd_device::cdcmd_getparam, &psxcd_device::cdcmd_getlocl, &psxcd_device::cdcmd_getlocp, &psxcd_device::cdcmd_unknown12, &psxcd_device::cdcmd_gettn, &psxcd_device::cdcmd_gettd, &psxcd_device::cdcmd_seekl, &psxcd_device::cdcmd_seekp, &psxcd_device::cdcmd_illegal17, &psxcd_device::cdcmd_illegal18, &psxcd_device::cdcmd_test, &psxcd_device::cdcmd_id, &psxcd_device::cdcmd_reads, &psxcd_device::cdcmd_reset, &psxcd_device::cdcmd_illegal1d, &psxcd_device::cdcmd_readtoc }; void psxcd_device::write_command(uint8_t byte) { if(byte > 31) illegalcmd(byte); else (this->*cmd_table[byte])(); m_param_count = 0; } void psxcd_device::cdcmd_sync() { verboselog(*this, 1, "psxcd: sync\n"); stop_read(); send_result(intr_acknowledge); } void psxcd_device::cdcmd_nop() { verboselog(*this, 1, "psxcd: nop\n"); if (!open) status &= ~status_shellopen; send_result(intr_complete); } void psxcd_device::cdcmd_setloc() { verboselog(*this, 1, "psxcd: setloc %08x:%08x:%08x\n", cmdbuf[0], cmdbuf[1], cmdbuf[2]); 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 verboselog(*this, 0, "psxcd: 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; verboselog(*this, 1, "psxcd: play %02x %02x %02x => %d\n", decimal_to_bcd(loc.b[M]), decimal_to_bcd(loc.b[S]), decimal_to_bcd(loc.b[F]), msf_to_lba_ps(loc.w)); stop_read(); start_play(); send_result(intr_complete); } void psxcd_device::cdcmd_forward() { verboselog(*this, 1, "psxcd: forward\n"); } void psxcd_device::cdcmd_backward() { verboselog(*this, 1, "psxcd: backward\n"); } void psxcd_device::cdcmd_readn() { if(!open) { verboselog(*this, 1, "psxcd: readn\n"); curpos.w=loc.w; stop_read(); start_read(); } else { send_result(intr_diskerror, nullptr, 0, 0x80); } } void psxcd_device::cdcmd_standby() { verboselog(*this, 1, "psxcd: standby\n"); stop_read(); send_result(intr_acknowledge); } void psxcd_device::cdcmd_stop() { verboselog(*this, 1, "psxcd: stop\n"); stop_read(); send_result(intr_acknowledge); } void psxcd_device::cdcmd_pause() { verboselog(*this, 1, "psxcd: pause\n"); stop_read(); send_result(intr_acknowledge); } void psxcd_device::cdcmd_init() { verboselog(*this, 1, "psxcd: init\n"); stop_read(); mode=0; m_regs.sr |= 0x10; send_result(intr_acknowledge); } void psxcd_device::cdcmd_mute() { verboselog(*this, 1, "psxcd: mute\n"); m_mute = true; send_result(intr_complete); } void psxcd_device::cdcmd_demute() { verboselog(*this, 1, "psxcd: demute\n"); m_mute = false; send_result(intr_complete); } void psxcd_device::cdcmd_setfilter() { verboselog(*this, 1, "psxcd: setfilter %08x,%08x\n",cmdbuf[0],cmdbuf[1]); filter_file=cmdbuf[0]; filter_channel=cmdbuf[1]; send_result(intr_acknowledge); } void psxcd_device::cdcmd_setmode() { verboselog(*this, 1, "psxcd: setmode %08x\n",cmdbuf[0]); mode=cmdbuf[0]; send_result(intr_complete); } void psxcd_device::cdcmd_getparam() { unsigned char data[6]= { status, mode, filter_file, filter_channel, 0, 0 }; verboselog(*this, 1, "psxcd: getparam [%02x %02x %02x %02x %02x %02x]\n", data[0], data[1], data[2], data[3], data[4], data[5]); send_result(intr_complete,data,6); } uint32_t 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() { verboselog(*this, 1, "psxcd: 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]); send_result(intr_complete,lastsechdr,8); } void psxcd_device::cdcmd_getlocp() { CDPOS tloc, start; uint8_t track = cdrom_get_track(m_cdrom_handle, msf_to_lba_ps(loc.w)) + 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 }; verboselog(*this, 1, "psxcd: 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]); send_result(intr_complete,data,8); } void psxcd_device::cdcmd_gettn() { verboselog(*this, 1, "psxcd: gettn\n"); if(!open) { unsigned char data[3]= { status, decimal_to_bcd(1), decimal_to_bcd(cdrom_get_last_track(m_cdrom_handle)) }; send_result(intr_complete,data,3); } else { send_result(intr_diskerror, nullptr, 0, 0x80); } } void psxcd_device::cdcmd_gettd() { uint8_t track = bcd_to_decimal(cmdbuf[0]); uint8_t 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]) }; verboselog(*this, 1, "psxcd: gettd %02x [%02x %02x %02x]\n", cmdbuf[0], data[0], data[1], data[2]); send_result(intr_acknowledge,data,3); } else { send_result(intr_diskerror, nullptr, 0, 0x80); } } void psxcd_device::cdcmd_seekl() { verboselog(*this, 1, "psxcd: seekl [%02d:%02d:%02d]\n", loc.b[M], loc.b[S], loc.b[F]); curpos.w=loc.w; send_result(intr_acknowledge); } void psxcd_device::cdcmd_seekp() { verboselog(*this, 1, "psxcd: seekp\n"); curpos.w=loc.w; send_result(intr_acknowledge); } void psxcd_device::cdcmd_test() { verboselog(*this, 1, "psxcd: test %02x\n", cmdbuf[0]); switch(cmdbuf[0]) { case 0x20: { static uint8_t data[4]= { 0x95, 0x07, 0x06, 0xff }; send_result(intr_complete,data,4); break; } default: verboselog(*this, 0, "psxcd: unimplemented test cmd %02x\n", cmdbuf[0]); cmd_complete(prepare_result(intr_diskerror, nullptr, 0, 0x10)); break; } } void psxcd_device::cdcmd_id() { verboselog(*this, 1, "psxcd: id\n"); if (!open) { uint8_t cdid[8]; int irq; memset(cdid, '\0', 8); send_result(intr_complete); if(cdrom_get_track_type(m_cdrom_handle, 0) == CD_TRACK_AUDIO) { irq = intr_diskerror; cdid[0] = status | status_invalid; cdid[1] = 0x90; } else { irq = intr_acknowledge; cdid[0] = status; cdid[4] = 'S'; cdid[5] = 'C'; cdid[6] = 'E'; cdid[7] = 'A'; } send_result(irq,cdid,8,default_irq_delay*3); } else { send_result(intr_diskerror, nullptr, 0, 0x80); } } void psxcd_device::cdcmd_reads() { if(!open) { verboselog(*this, 1, "psxcd: reads\n"); curpos.w=loc.w; stop_read(); start_read(); } else { send_result(intr_diskerror, nullptr, 0, 0x80); } } void psxcd_device::cdcmd_reset() { verboselog(*this, 1, "psxcd: reset\n"); } void psxcd_device::cdcmd_readtoc() { verboselog(*this, 1, "psxcd: readtoc\n"); send_result(intr_complete); send_result(intr_acknowledge, nullptr, 0, default_irq_delay*3); // ? } void psxcd_device::cdcmd_unknown12() { verboselog(*this, 1, "psxcd: unknown 12\n"); // set session? readt? if(cmdbuf[0] == 1) send_result(intr_complete); else send_result(intr_diskerror, nullptr, 0, 0x40); } void psxcd_device::cdcmd_illegal17() { illegalcmd(0x17); // set clock? } void psxcd_device::cdcmd_illegal18() { illegalcmd(0x18); // get clock? } void psxcd_device::cdcmd_illegal1d() { illegalcmd(0x1d); // read q subchannel } void psxcd_device::illegalcmd(uint8_t cmd) { verboselog(*this, 0, "psxcd: unimplemented cd command %02x\n", cmd); send_result(intr_diskerror, nullptr, 0, 0x40); } void psxcd_device::cmd_complete(command_result *res) { command_result *rf; verboselog(*this, 1, "psxcd: irq [%d]\n", res->res); if (res_queue) { for (rf=res_queue; rf->next; rf=rf->next); rf->next=res; } else { res_queue = res; m_regs.ir = res_queue->res; if(m_regs.ir & m_regs.imr) m_irq_handler(1); m_regs.sr |= 0x20; } res->next = nullptr; } psxcd_device::command_result *psxcd_device::prepare_result(uint8_t res, uint8_t *data, int sz, uint8_t errcode) { auto cr=global_alloc(command_result); cr->res=res; if (sz) { assert(szdata)); memcpy(cr->data,data,sz); cr->sz=sz; } else { if((res == intr_diskerror) && errcode) { cr->data[0] = status | status_error; cr->data[1] = errcode; cr->sz = 2; } else { cr->data[0]=status; cr->sz=1; } } status &= ~status_error; return cr; } void psxcd_device::send_result(uint8_t res, uint8_t *data, int sz, int delay, uint8_t errcode) { // Avoid returning results after sector read results - // delay the sector read slightly if necessary uint64_t systime = m_maincpu->total_cycles(); if ((next_read_event != -1) && ((systime+delay)>(next_sector_t))) { uint32_t 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, prepare_result(res, data, sz, errcode)); } void psxcd_device::start_dma(uint8_t *mainram, uint32_t size) { uint32_t sector_size; verboselog(*this, 1, "psxcd: start dma %d bytes at %d\n", size, m_transcurr); if(!m_dmaload) return; switch(mode & mode_size_mask) { case 0x00: default: sector_size = 2048 + 24; break; case 0x10: sector_size = 2328 + 12; break; case 0x20: sector_size = 2340 + 12; break; } if(size > (sector_size - m_transcurr)) size = (sector_size - m_transcurr); memcpy(mainram, &m_transbuf[m_transcurr], size); m_transcurr += size; if(sector_size <= m_transcurr) { m_dmaload = false; m_regs.sr &= ~0x40; } } void psxcd_device::read_sector() { next_read_event=-1; if (status & status_reading) { bool isend = false; uint32_t sector = msf_to_lba_ps(curpos.w); uint8_t *buf = secbuf[sectail]; if (cdrom_read_data(m_cdrom_handle, sector, buf, CD_TRACK_RAW_DONTCARE)) { subheader *sub=(subheader *)(buf+16); memcpy(lastsechdr, buf+12, 8); verboselog(*this, 2, "psxcd: subheader file=%02x chan=%02x submode=%02x coding=%02x [%02x%02x%02x%02x]\n", sub->file, sub->channel, sub->submode, sub->coding, buf[0xc], buf[0xd], buf[0xe], buf[0xf]); if ((mode & mode_adpcm) && (sub->submode & submode_audio)) { if ((sub->submode & submode_eof) && (mode & mode_autopause)) { isend=true; //printf("end of file\n"); } if((!(mode & mode_channel) || ((sub->file == filter_file) && (sub->channel == filter_channel))) && !m_mute) m_spu->play_xa(0,buf+16); } else { if(!m_int1) m_cursec = sectail; m_int1 = prepare_result(intr_dataready); cmd_complete(m_int1); sectail++; sectail %= sector_buffer_size; } 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; } else isend = true; 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, nullptr); } else { verboselog(*this, 1, "psxcd: autopause xa\n"); cmd_complete(prepare_result(intr_dataend)); stop_read(); } } } void psxcd_device::play_sector() { next_read_event=-1; if (status&status_playing) { uint32_t sector = msf_to_lba_ps(curpos.w); if(cdrom_read_data(m_cdrom_handle, sector, secbuf[sectail], CD_TRACK_AUDIO)) { if(!m_mute) m_spu->play_cdda(0, secbuf[sectail]); } else { if(!cdrom_read_data(m_cdrom_handle, sector, secbuf[sectail], CD_TRACK_RAW_DONTCARE)) { stop_read(); // assume we've reached the end cmd_complete(prepare_result(intr_dataend)); return; } } 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; sector++; sectail++; sectail %= sector_buffer_size; if (mode&mode_autopause) { if (sector>=autopause_sector) { verboselog(*this, 1, "psxcd: autopause cdda\n"); stop_read(); cmd_complete(prepare_result(intr_dataend)); return; } } if ((mode&mode_report) && !(sector & 15)) // slow the int rate { auto res=global_alloc(command_result); uint8_t 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(), nullptr); } } void psxcd_device::start_read() { uint32_t sector = msf_to_lba_ps(curpos.w); assert((status&(status_reading|status_playing))==0); if(!(mode & mode_cdda) && (cdrom_get_track_type(m_cdrom_handle, cdrom_get_track(m_cdrom_handle, sector + 150)) == CD_TRACK_AUDIO)) { stop_read(); cmd_complete(prepare_result(intr_diskerror, nullptr, 0, 0x40)); return; } send_result(intr_complete); status |= status_reading; m_cursec=sectail=0; unsigned int cyc=read_sector_cycles; if (mode&mode_double_speed) cyc>>=1; int64_t systime=m_maincpu->total_cycles(); systime+=start_read_delay; next_sector_t=systime+cyc; next_read_event = add_system_event(event_read_sector, start_read_delay+preread_delay, nullptr); } void psxcd_device::start_play() { uint8_t track = cdrom_get_track(m_cdrom_handle, msf_to_lba_ps(curpos.w) + 150); if(cdrom_get_track_type(m_cdrom_handle, track) != CD_TRACK_AUDIO) verboselog(*this, 0, "psxcd: playing data track\n"); status|=status_playing; m_cursec=sectail=0; if (mode&mode_autopause) { 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(), nullptr); } void psxcd_device::stop_read() { if (status & (status_reading|status_playing)) verboselog(*this, 1, "psxcd: stop read\n"); status&=~(status_reading|status_playing); 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_t 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]) { verboselog(*this, 0, "psxcd: timer fired for free event\n"); return; } m_timerinuse[tid] = false; switch (param) { case event_cmd_complete: { verboselog(*this, 1, "psxcd: event cmd complete\n"); cmd_complete((command_result *)ptr); 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 t, command_result *ptr) { // t is in maincpu clock cycles uint32_t hz = m_sysclock / t; 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"); } /* * TODO: * Declare the MCU (Motorola 68HC05 [?] at 4MHz [?]) and hookup * these ROM images so that we actually emulate the PSX CDROM unit * * More technical info is available at these links: * http://www.psxdev.net/forum/viewtopic.php?f=60&t=573 * http://www.psxdev.net/forum/viewtopic.php?f=70&t=557 * */ ROM_START( psxcd ) ROM_REGION( 0x10000, "cdrom_mcu", 0 ) /* Retail PlayStation CD-ROM Firmware: * * Still missing: * SCPH-1001, 3000, 3500, 5003, 5502, 5503, 7000W, 7001, 7002, 7003, 7503, 9003, 100, 101, 102 and 103. */ ROM_SYSTEM_BIOS( 0, "scph-1000-later", "SCPH-1000 NTSC:J (Later Ver.) [424660]" ) ROMX_LOAD( "424660.bin", 0x0000, 0x4200, CRC(f82a2a46) SHA1(095434948d4c71cdfaa069e91053443887a6d139), ROM_BIOS(1) ) ROM_SYSTEM_BIOS( 1, "scph-1000-early", "SCPH-1000 NTSC:J (Early Ver.) [424666]" ) ROMX_LOAD( "424666.bin", 0x0000, 0x4200, CRC(60bc954e) SHA1(80674353daf95ffb4bd15cc4bb8cfa713370dd45), ROM_BIOS(2) ) ROM_SYSTEM_BIOS( 2, "scph-1002-early", "SCPH-1002 PAL (Early PU-8) [424684]" ) ROMX_LOAD( "424684.bin", 0x0000, 0x4200, CRC(84d46b2a) SHA1(9b06b1d407b784095ddbd45aeabafd689d2ee347), ROM_BIOS(3) ) /* Chip markings: C 1021 / SC430916PB / G63C 185 / JSAB9624F */ ROM_SYSTEM_BIOS( 3, "scph-5000", "SCPH-5000 NTSC:J [SC430916]" ) ROMX_LOAD( "sc430916.s19", 0x0000, 0xb195, CRC(487c8a40) SHA1(0ae8348fb43ab80845b0166494edc3e1565a3ef7), ROM_BIOS(4) ) /* Chip markings: C 1030 / SC430925PB / G63C 185 / JSBK9708C Board Type: PU-18 / 1-664-537-11 */ ROM_SYSTEM_BIOS( 4, "scph-5500", "SCPH-5500 NTSC:J [SC430925]" ) ROMX_LOAD( "sc430925.s19", 0x0000, 0xb195, CRC(c09aa0c2) SHA1(b9ad66cc8ea4d6e2eb2709ffb77c9647f679097a), ROM_BIOS(5) ) /* Chip markings: C 2030 / SC430930PB / G63C 185 / SSJZ9748A Board Type: PU-18 / 1-664-537-62 */ ROM_SYSTEM_BIOS( 5, "scph-5501", "SCPH-5501 NTSC:U/C [SC430930]" ) ROMX_LOAD( "sc430930.s19", 0x0000, 0xb195, CRC(587b84c2) SHA1(556c3adc37e4eb64fd463c54f7a310c483e0e835), ROM_BIOS(6) ) /* ROM dump is the same as SCPH-5552 */ ROM_SYSTEM_BIOS( 6, "scph-5502", "SCPH-5502 PAL [SC430929]" ) ROMX_LOAD( "sc430929.bin", 0x0000, 0x4200, CRC(ba87a3e0) SHA1(f23458d13a518616a8592b8ddd668c052bc9be5a), ROM_BIOS(7) ) ROM_SYSTEM_BIOS( 7, "scph-5903", "SCPH-5903 NTSC:J [SC430924PB]" ) ROMX_LOAD( "sc430924.s19", 0x0000, 0xb195, CRC(dbe694b2) SHA1(ac72cb616b1449fe29e52faf6aad389118852d73), ROM_BIOS(8) ) /* Chip markings: C 1040 / SC430934PB / G63C 185 / SSDG9745D */ ROM_SYSTEM_BIOS( 8, "scph-7000", "SCPH-7000 NTSC:J [SC430934]" ) ROMX_LOAD( "sc430934.s19", 0x0000, 0xb195, CRC(6443740c) SHA1(d9734c7135c75dbe7733079a2d4244a28c9e966e), ROM_BIOS(9) ) /* Chip markings: C 1050 / SC430938PB / G63C 185 / SSAM9850C */ ROM_SYSTEM_BIOS( 9, "scph-7500", "SCPH-7500 NTSC:J [SC430938]" ) ROMX_LOAD( "sc430938.s19", 0x0000, 0xb195, CRC(9744977a) SHA1(f017d34a98a8a023f6752ba9ed749bb9e2b836d5), ROM_BIOS(10) ) /* Chip markings: C 2050 / SC430940PB / G63C 185 / SSDL9838A */ ROM_SYSTEM_BIOS( 10, "scph-7501", "SCPH-7501 NTSC:U/C [SC430940]" ) ROMX_LOAD( "sc430940.s19", 0x0000, 0xb195, CRC(fd1c6ee7) SHA1(e72b5093a3e25de1548be7668179ff3e001e3ec5), ROM_BIOS(11) ) ROM_SYSTEM_BIOS( 11, "scph-7502", "SCPH-7502 PAL [SC430939]" ) ROMX_LOAD( "sc430939.bin", 0x0000, 0x4200, CRC(9eafb045) SHA1(25d98454e567e064c06f840d57f763fb7c8b7219), ROM_BIOS(12) ) ROM_SYSTEM_BIOS( 12, "scph-9000", "SCPH-9000 NTSC:J [SC430942]" ) ROMX_LOAD( "sc430942.bin", 0x0000, 0x4200, NO_DUMP, ROM_BIOS(13) ) /* Chip markings: C 2060 / SC430944PB / G63C 185 / SSBR9924C */ ROM_SYSTEM_BIOS( 13, "scph-9001", "SCPH-9001 NTSC:U/C [SC430944]" ) ROMX_LOAD( "sc430944.s19", 0x0000, 0xb195, CRC(24011dfd) SHA1(db72ba02466942d1a1a07c4d855edd18f84de92e), ROM_BIOS(14) ) ROM_SYSTEM_BIOS( 14, "scph-9002", "SCPH-9002 PAL [SC430943]" ) ROMX_LOAD( "sc430943.bin", 0x0000, 0x4200, CRC(2669a1a7) SHA1(62999e7f8429f381e19d44d2399b6017959f4f13), ROM_BIOS(15) ) /* Development PlayStation CD-ROM Firmware: * * Still missing: * DTL-H1000, 1000H, 1001, 1001H, 1002, 1100, 1101, 1102, 1200, 1201, 2000, 2500, 2700, 3000, 3001 and 3002. */ /* Chip markings: D 2021 / SC430920PB / G63C 185 / JSAA9810A * Note: Although this is a PAL unit, the ID string in the * code is "for US/AEP", so it may be the same for all the * debug consoles. */ ROM_SYSTEM_BIOS( 15, "dtl-h1202", "DTL-H1202 PAL [SC430920]" ) ROMX_LOAD( "sc430920.s19", 0x0000, 0xb195, CRC(8380a5a2) SHA1(6fe45fd6fb96b12a25a45f39b5efd0be5e3f3e86), ROM_BIOS(16) ) ROM_END const tiny_rom_entry *psxcd_device::device_rom_region() const { return ROM_NAME( psxcd ); }