diff options
Diffstat (limited to 'src/mame/machine/pce_cd.cpp')
-rw-r--r-- | src/mame/machine/pce_cd.cpp | 1661 |
1 files changed, 0 insertions, 1661 deletions
diff --git a/src/mame/machine/pce_cd.cpp b/src/mame/machine/pce_cd.cpp deleted file mode 100644 index 50602b4e1cb..00000000000 --- a/src/mame/machine/pce_cd.cpp +++ /dev/null @@ -1,1661 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Wilbert Pol, Angelo Salese -/************************************************************ - -PC Engine CD HW notes: - -TODO: -- Dragon Ball Z: ADPCM dies after the first upload; -- Dragon Slayer - The Legend of Heroes: black screen; (actually timing/raster irq) -- Mirai Shonen Conan: dies at new game selection; (actually timing/raster irq) -- Snatcher: black screen after Konami logo, tries set up CD-DA - while transferring data? (fixed) -- Steam Heart's: needs transfer ready irq to get past the - gameplay hang, don't know exactly when it should fire -- Steam Heart's: bad ADPCM irq, dialogue is cutted due of it; -- Unsafe on debugger access; - -============================================================= - -CD Interface Register 0x00 - CDC status -x--- ---- busy signal --x-- ---- request signal ----x ---- cd signal ----- x--- i/o signal - -CD Interface Register 0x03 - BRAM lock / CD status --x-- ---- acknowledge signal ---x- ---- done signal ----x ---- bram signal ----- x--- ADPCM 2 ----- -x-- ADPCM 1 ----- --x- CDDA left/right speaker select - -CD Interface Register 0x05 - CD-DA Volume low 8-bit port - -CD Interface Register 0x06 - CD-DA Volume high 8-bit port - -CD Interface Register 0x07 - BRAM unlock / CD status -x--- ---- Enables BRAM - -CD Interface Register 0x0c - ADPCM status -x--- ---- ADPCM is reading data ----- x--- ADPCM playback (0) stopped (1) currently playing ----- -x-- pending ADPCM data write ----- ---x ADPCM playback (1) stopped (0) currently playing - -CD Interface Register 0x0d - ADPCM address control -x--- ---- ADPCM reset --x-- ---- ADPCM play ---x- ---- ADPCM repeat ----x ---- ADPCM set length ----- x--- ADPCM set read address ----- --xx ADPCM set write address -(note: some games reads bit 5 and wants it to be low otherwise they hangs, surely NOT an ADPCM repeat flag read because it doesn't make sense) - -CD Interface Register 0x0e - ADPCM playback rate - -CD Interface Register 0x0f - ADPCM fade in/out register ----- xxxx command setting: -0x00 ADPCM/CD-DA Fade-in -0x01 CD-DA fade-in -0x08 CD-DA fade-out (short) ADPCM fade-in -0x09 CD-DA fade-out (long) -0x0a ADPCM fade-out (long) -0x0c CD-DA fade-out (short) ADPCM fade-in -0x0d CD-DA fade-out (short) -0x0e ADPCM fade-out (short) - -*************************************************************/ - -#include "emu.h" -#include "coreutil.h" -#include "machine/pce_cd.h" - - -#define PCE_CD_CLOCK 9216000 - - -// TODO: it's actually a common interface with PC-8801, find actual number part -DEFINE_DEVICE_TYPE(PCE_CD, pce_cd_device, "pcecd", "PCE CD Add-on") - -// registers 9, e and f are known to be write only -void pce_cd_device::regs_map(address_map &map) -{ - map(0x00, 0x00).rw(FUNC(pce_cd_device::cdc_status_r), FUNC(pce_cd_device::cdc_status_w)); - map(0x01, 0x01).rw(FUNC(pce_cd_device::cdc_data_r), FUNC(pce_cd_device::cdc_data_w)); - map(0x02, 0x02).rw(FUNC(pce_cd_device::irq_mask_r), FUNC(pce_cd_device::irq_mask_w)); - map(0x03, 0x03).r(FUNC(pce_cd_device::irq_status_r)); - map(0x04, 0x04).rw(FUNC(pce_cd_device::cdc_reset_r), FUNC(pce_cd_device::cdc_reset_w)); - map(0x05, 0x06).r(FUNC(pce_cd_device::cdda_data_r)); - map(0x07, 0x07).rw(FUNC(pce_cd_device::bram_status_r), FUNC(pce_cd_device::bram_unlock_w)); - map(0x08, 0x08).rw(FUNC(pce_cd_device::cd_data_r), FUNC(pce_cd_device::adpcm_address_lo_w)); - map(0x09, 0x09).w(FUNC(pce_cd_device::adpcm_address_hi_w)); - map(0x0a, 0x0a).rw(FUNC(pce_cd_device::adpcm_data_r), FUNC(pce_cd_device::adpcm_data_w)); - map(0x0b, 0x0b).rw(FUNC(pce_cd_device::adpcm_dma_control_r), FUNC(pce_cd_device::adpcm_dma_control_w)); - map(0x0c, 0x0c).r(FUNC(pce_cd_device::adpcm_status_r)); - map(0x0d, 0x0d).rw(FUNC(pce_cd_device::adpcm_address_control_r), FUNC(pce_cd_device::adpcm_address_control_w)); - map(0x0e, 0x0e).w(FUNC(pce_cd_device::adpcm_playback_rate_w)); - map(0x0f, 0x0f).w(FUNC(pce_cd_device::fade_register_w)); -} - -pce_cd_device::pce_cd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, PCE_CD, tag, owner, clock) - , device_memory_interface(mconfig, *this) - , m_space_config("io", ENDIANNESS_LITTLE, 8, 4, 0, address_map_constructor(FUNC(pce_cd_device::regs_map), this)) - , m_maincpu(*this, ":maincpu") - , m_msm(*this, "msm5205") - , m_cdda(*this, "cdda") - , m_nvram(*this, "bram") - , m_cdrom(*this, "cdrom") -{ -} - -device_memory_interface::space_config_vector pce_cd_device::memory_space_config() const -{ - return space_config_vector { - std::make_pair(AS_IO, &m_space_config) - }; -} - -void pce_cd_device::device_start() -{ - /* Initialize BRAM */ - m_bram = std::make_unique<uint8_t[]>(PCE_BRAM_SIZE * 2); - memset(m_bram.get(), 0, PCE_BRAM_SIZE); - memset(m_bram.get() + PCE_BRAM_SIZE, 0xff, PCE_BRAM_SIZE); - m_bram_locked = 1; - - m_nvram->set_base(m_bram.get(), PCE_BRAM_SIZE); - - /* set up adpcm related things */ - m_adpcm_ram = make_unique_clear<uint8_t[]>(PCE_ADPCM_RAM_SIZE); - m_adpcm_clock_divider = 1; - - /* Set up cd command buffer */ - m_command_buffer = make_unique_clear<uint8_t[]>(PCE_CD_COMMAND_BUFFER_SIZE); - m_command_buffer_index = 0; - - /* Set up Arcade Card RAM buffer */ - m_acard_ram = make_unique_clear<uint8_t[]>(PCE_ACARD_RAM_SIZE); - - m_data_buffer = make_unique_clear<uint8_t[]>(8192); - m_data_buffer_size = 0; - m_data_buffer_index = 0; - - m_subcode_buffer = std::make_unique<uint8_t[]>(96); - - m_data_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::data_timer_callback),this)); - m_data_timer->adjust(attotime::never); - m_adpcm_dma_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::adpcm_dma_timer_callback),this)); - m_adpcm_dma_timer->adjust(attotime::never); - - m_cdda_fadeout_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::cdda_fadeout_callback),this)); - m_cdda_fadeout_timer->adjust(attotime::never); - m_cdda_fadein_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::cdda_fadein_callback),this)); - m_cdda_fadein_timer->adjust(attotime::never); - - m_adpcm_fadeout_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::adpcm_fadeout_callback),this)); - m_adpcm_fadeout_timer->adjust(attotime::never); - m_adpcm_fadein_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(pce_cd_device::adpcm_fadein_callback),this)); - m_adpcm_fadein_timer->adjust(attotime::never); - - // m_cd_file pointer is setup at a later stage because it is still empty when this function is called - - // TODO: add proper restore for the cd data... - save_pointer(NAME(m_bram), PCE_BRAM_SIZE * 2); - save_pointer(NAME(m_adpcm_ram), PCE_ADPCM_RAM_SIZE); - save_item(NAME(m_bram_locked)); - save_item(NAME(m_adpcm_read_ptr)); - save_item(NAME(m_adpcm_read_buf)); - save_item(NAME(m_adpcm_write_ptr)); - save_item(NAME(m_adpcm_write_buf)); - save_item(NAME(m_adpcm_length)); - save_item(NAME(m_adpcm_clock_divider)); - save_item(NAME(m_msm_start_addr)); - save_item(NAME(m_msm_end_addr)); - save_item(NAME(m_msm_half_addr)); - save_item(NAME(m_msm_nibble)); - save_item(NAME(m_msm_idle)); - save_item(NAME(m_msm_repeat)); - save_item(NAME(m_scsi_BSY)); - save_item(NAME(m_scsi_SEL)); - save_item(NAME(m_scsi_CD)); - save_item(NAME(m_scsi_IO)); - save_item(NAME(m_scsi_MSG)); - save_item(NAME(m_scsi_REQ)); - save_item(NAME(m_scsi_ACK)); - save_item(NAME(m_scsi_ATN)); - save_item(NAME(m_scsi_RST)); - save_item(NAME(m_scsi_last_RST)); - save_item(NAME(m_cd_motor_on)); - save_item(NAME(m_selected)); - save_pointer(NAME(m_command_buffer), PCE_CD_COMMAND_BUFFER_SIZE); - save_item(NAME(m_command_buffer_index)); - save_item(NAME(m_status_sent)); - save_item(NAME(m_message_after_status)); - save_item(NAME(m_message_sent)); - save_pointer(NAME(m_data_buffer), 8192); - save_item(NAME(m_data_buffer_size)); - save_item(NAME(m_data_buffer_index)); - save_item(NAME(m_data_transferred)); - save_pointer(NAME(m_acard_ram), PCE_ACARD_RAM_SIZE); - save_item(NAME(m_acard_latch)); - save_item(NAME(m_acard_ctrl)); - save_item(NAME(m_acard_base_addr)); - save_item(NAME(m_acard_addr_offset)); - save_item(NAME(m_acard_addr_inc)); - save_item(NAME(m_acard_shift)); - save_item(NAME(m_acard_shift_reg)); - save_item(NAME(m_current_frame)); - save_item(NAME(m_end_frame)); - save_item(NAME(m_last_frame)); - save_item(NAME(m_cdda_status)); - save_item(NAME(m_cdda_play_mode)); - save_pointer(NAME(m_subcode_buffer), 96); - save_item(NAME(m_end_mark)); - save_item(NAME(m_cdda_volume)); - save_item(NAME(m_adpcm_volume)); - - // internal regs - save_item(NAME(m_reset_reg)); - save_item(NAME(m_irq_mask)); - save_item(NAME(m_irq_status)); - save_item(NAME(m_cdc_status)); - save_item(NAME(m_cdc_data)); - save_item(NAME(m_bram_status)); - save_item(NAME(m_adpcm_status)); - save_item(NAME(m_adpcm_latch_address)); - save_item(NAME(m_adpcm_control)); - save_item(NAME(m_fade_reg)); - save_item(NAME(m_adpcm_dma_reg)); -} - -void pce_cd_device::device_reset() -{ - m_adpcm_read_buf = 0; - m_adpcm_write_buf = 0; - - // TODO: add CD-DA stop command here - //m_cdda_status = PCE_CD_CDDA_OFF; - //m_cdda->stop_audio(); - - m_adpcm_status |= PCE_CD_ADPCM_STOP_FLAG; - m_adpcm_status &= ~PCE_CD_ADPCM_PLAY_FLAG; - //m_irq_status = (m_irq_status & ~0x0c) | (PCE_CD_SAMPLE_STOP_PLAY); - m_msm_idle = 1; - - m_scsi_RST = 0; - m_scsi_last_RST = 0; - m_scsi_SEL = 0; - m_scsi_BSY = 0; - m_selected = 0; - m_scsi_ATN = 0; - m_end_mark = 0; -} - - -void pce_cd_device::late_setup() -{ - // at device start, the cdrom is not 'ready' yet, so we postpone this part of the initialization at machine_start in the driver - m_cd_file = m_cdrom->get_cdrom_file(); - if (m_cd_file) - { - m_toc = cdrom_get_toc(m_cd_file); - m_cdda->set_cdrom(m_cd_file); - m_last_frame = cdrom_get_track_start(m_cd_file, cdrom_get_last_track(m_cd_file) - 1); - m_last_frame += m_toc->tracks[cdrom_get_last_track(m_cd_file) - 1].frames; - m_end_frame = m_last_frame; - } - - // MSM5205 might be initialized after PCE CD as well... - m_msm->set_unscaled_clock((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); -} - -void pce_cd_device::nvram_init(nvram_device &nvram, void *data, size_t size) -{ - static const uint8_t init[8] = { 0x48, 0x55, 0x42, 0x4d, 0x00, 0xa0, 0x10, 0x80 }; - - memset(data, 0x00, size); - memcpy(data, init, sizeof(init)); -} - -// TODO: left and right speaker tags should be passed from the parent config, instead of using the hard-coded ones below!?! -void pce_cd_device::device_add_mconfig(machine_config &config) -{ - NVRAM(config, m_nvram).set_custom_handler(FUNC(pce_cd_device::nvram_init)); - - CDROM(config, m_cdrom).set_interface("pce_cdrom"); - - MSM5205(config, m_msm, PCE_CD_CLOCK / 6); - m_msm->vck_legacy_callback().set(FUNC(pce_cd_device::msm5205_int)); /* interrupt function */ - m_msm->set_prescaler_selector(msm5205_device::S48_4B); /* 1/48 prescaler, 4bit data */ - m_msm->add_route(ALL_OUTPUTS, "^lspeaker", 0.50); - m_msm->add_route(ALL_OUTPUTS, "^rspeaker", 0.50); - - CDDA(config, m_cdda); - m_cdda->add_route(0, "^lspeaker", 1.00); - m_cdda->add_route(1, "^rspeaker", 1.00); -} - -void pce_cd_device::adpcm_stop(uint8_t irq_flag) -{ - m_adpcm_status |= PCE_CD_ADPCM_STOP_FLAG; - m_adpcm_status &= ~PCE_CD_ADPCM_PLAY_FLAG; - //m_irq_status = (m_irq_status & ~0x0c) | (PCE_CD_SAMPLE_STOP_PLAY); - if (irq_flag) - set_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE); - m_adpcm_control &= ~0x60; - m_msm_idle = 1; -} - -void pce_cd_device::adpcm_play() -{ - m_adpcm_status &= ~PCE_CD_ADPCM_STOP_FLAG; - m_adpcm_status |= PCE_CD_ADPCM_PLAY_FLAG; - set_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE); - m_irq_status = (m_irq_status & ~0x0c); - m_msm_idle = 0; -} - - -/* Callback for new data from the MSM5205. - The PCE cd unit actually divides the clock signal supplied to - the MSM5205. Currently we can only use static clocks for the - MSM5205. - */ -WRITE_LINE_MEMBER( pce_cd_device::msm5205_int ) -{ - uint8_t msm_data; - - // popmessage("%08x %08x %08x %02x %02x",m_msm_start_addr,m_msm_end_addr,m_msm_half_addr,m_adpcm_status,m_adpcm_control); - - if (m_msm_idle) - return; - - /* Supply new ADPCM data */ - msm_data = (m_msm_nibble) ? (m_adpcm_ram[m_msm_start_addr] & 0x0f) : ((m_adpcm_ram[m_msm_start_addr] & 0xf0) >> 4); - - m_msm->write_data(msm_data); - m_msm_nibble ^= 1; - - if (m_msm_nibble == 0) - { - m_msm_start_addr++; - - if (m_msm_start_addr == m_msm_half_addr) - { - //set_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE); - //set_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, ASSERT_LINE); - } - - if (m_msm_start_addr > m_msm_end_addr) - { - //set_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE); - //set_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE); - adpcm_stop(1); - m_msm->reset_w(1); - } - } -} - - - -#define SCSI_STATUS_OK 0x00 -#define SCSI_CHECK_CONDITION 0x02 - -void pce_cd_device::reply_status_byte(uint8_t status) -{ - logerror("Setting CD in reply_status_byte\n"); - m_scsi_CD = m_scsi_IO = m_scsi_REQ = 1; - m_scsi_MSG = 0; - m_message_after_status = 1; - m_status_sent = m_message_sent = 0; - - if (status == SCSI_STATUS_OK) - { - m_cdc_data = 0x00; - } - else - { - m_cdc_data = 0x01; - } -} - -/* 0x00 - TEST UNIT READY */ -void pce_cd_device::test_unit_ready() -{ - logerror("test unit ready\n"); - if (m_cd_file) - { - logerror("Sending STATUS_OK status\n"); - reply_status_byte(SCSI_STATUS_OK); - } - else - { - logerror("Sending CHECK_CONDITION status\n"); - reply_status_byte(SCSI_CHECK_CONDITION); - } -} - -/* 0x08 - READ (6) */ -void pce_cd_device::read_6() -{ - uint32_t frame = ((m_command_buffer[1] & 0x1f) << 16) | (m_command_buffer[2] << 8) | m_command_buffer[3]; - uint32_t frame_count = m_command_buffer[4]; - printf("%08x %08x\n",frame,frame_count); - - /* Check for presence of a CD */ - if (!m_cd_file) - { - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - if (m_cdda_status != PCE_CD_CDDA_OFF) - { - m_cdda_status = PCE_CD_CDDA_OFF; - m_cdda->stop_audio(); - m_end_mark = 0; - } - - m_current_frame = frame; - m_end_frame = frame + frame_count; - - if (frame_count == 0) - { - /* Star Breaker uses this */ - popmessage("Read Sector frame count == 0, contact MESSdev"); - reply_status_byte(SCSI_STATUS_OK); - } - else - { - m_data_timer->adjust(attotime::from_hz(PCE_CD_DATA_FRAMES_PER_SECOND), 0, attotime::from_hz(PCE_CD_DATA_FRAMES_PER_SECOND)); - } - - /* TODO: correct place? */ - set_irq_line(PCE_CD_IRQ_TRANSFER_READY, ASSERT_LINE); -} - -/* 0xD8 - SET AUDIO PLAYBACK START POSITION (NEC) */ -void pce_cd_device::nec_set_audio_start_position() -{ - uint32_t frame = 0; - - if (!m_cd_file) - { - /* Throw some error here */ - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - switch (m_command_buffer[9] & 0xC0) - { - case 0x00: - popmessage("CD-DA set start mode 0x00, contact MESSdev"); - frame = (m_command_buffer[3] << 16) | (m_command_buffer[4] << 8) | m_command_buffer[5]; - break; - case 0x40: - { - uint8_t m,s,f; - - m = bcd_2_dec(m_command_buffer[2]); - s = bcd_2_dec(m_command_buffer[3]); - f = bcd_2_dec(m_command_buffer[4]); - - frame = f + 75 * (s + m * 60); - // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap - // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?) - frame -= m_toc->tracks[cdrom_get_track(m_cd_file, frame)].pregap; - break; - } - case 0x80: - frame = m_toc->tracks[ bcd_2_dec(m_command_buffer[2]) - 1 ].logframeofs; - break; - default: - popmessage("CD-DA set start mode 0xc0, contact MESSdev"); - //assert(nullptr == nec_set_audio_start_position); - break; - } - - m_current_frame = frame; - - m_cdda_status = PCE_CD_CDDA_PAUSED; - - // old code for reference, seems unlikely that this puts status in standby (and breaks Snatcher at the title screen) -// if (m_cdda_status == PCE_CD_CDDA_PAUSED) -// { -// m_cdda_status = PCE_CD_CDDA_OFF; -// m_cdda->stop_audio(); -// m_end_frame = m_last_frame; -// m_end_mark = 0; -// } -// else - { - if (m_command_buffer[1] & 0x03) - { - //m_cdda_status = PCE_CD_CDDA_PLAYING; - m_end_frame = m_last_frame; //get the end of the CD - m_cdda->start_audio(m_current_frame, m_end_frame - m_current_frame); - m_cdda_play_mode = (m_command_buffer[1] & 0x02) ? 2 : 3; // mode 2 sets IRQ at end - m_end_mark = (m_command_buffer[1] & 0x02) ? 1 : 0; - } - else - { - //m_cdda_status = PCE_CD_CDDA_PLAYING; - m_end_frame = m_toc->tracks[ cdrom_get_track(m_cd_file, m_current_frame) + 1 ].logframeofs; //get the end of THIS track - m_cdda->start_audio(m_current_frame, m_end_frame - m_current_frame); - m_end_mark = 0; - m_cdda_play_mode = 3; - } - } - - reply_status_byte(SCSI_STATUS_OK); - set_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE); -} - -/* 0xD9 - SET AUDIO PLAYBACK END POSITION (NEC) */ -void pce_cd_device::nec_set_audio_stop_position() -{ - uint32_t frame = 0; - - if (!m_cd_file) - { - /* Throw some error here */ - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - switch (m_command_buffer[9] & 0xC0) - { - case 0x00: - popmessage("CD-DA set end mode 0x00, contact MESSdev"); - frame = (m_command_buffer[3] << 16) | (m_command_buffer[4] << 8) | m_command_buffer[5]; - break; - case 0x40: - { - uint8_t m,s,f; - - m = bcd_2_dec(m_command_buffer[2]); - s = bcd_2_dec(m_command_buffer[3]); - f = bcd_2_dec(m_command_buffer[4]); - - frame = f + 75 * (s + m * 60); - // if (frame >= 525) // TODO: seven seconds gap? O_o - // frame -= 525; - break; - } - case 0x80: - frame = m_toc->tracks[ bcd_2_dec(m_command_buffer[2]) - 1 ].logframeofs; - break; - default: - popmessage("CD-DA set end mode 0xc0, contact MESSdev"); - //assert(nullptr == nec_set_audio_start_position); - break; - } - - m_end_frame = frame; - m_cdda_play_mode = m_command_buffer[1] & 0x03; - - if (m_cdda_play_mode) - { - if (m_cdda_status == PCE_CD_CDDA_PAUSED) - { - m_cdda->pause_audio(0); - } - else - { - //printf("%08x %08x\n",m_current_frame,m_end_frame - m_current_frame); - m_cdda->start_audio(m_current_frame, m_end_frame - m_current_frame); - m_end_mark = 1; - } - m_cdda_status = PCE_CD_CDDA_PLAYING; - } - else - { - m_cdda_status = PCE_CD_CDDA_OFF; - m_cdda->stop_audio(); - m_end_frame = m_last_frame; - m_end_mark = 0; -// assert(nullptr == nec_set_audio_stop_position); - } - - reply_status_byte(SCSI_STATUS_OK); - set_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE); -} - -/* 0xDA - PAUSE (NEC) */ -void pce_cd_device::nec_pause() -{ - /* If no cd mounted throw an error */ - if (!m_cd_file) - { - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - /* If there was no cdda playing, throw an error */ - if (m_cdda_status == PCE_CD_CDDA_OFF) - { - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - m_cdda_status = PCE_CD_CDDA_PAUSED; - m_current_frame = m_cdda->get_audio_lba(); - m_cdda->pause_audio(1); - reply_status_byte(SCSI_STATUS_OK); -} - -/* 0xDD - READ SUBCHANNEL Q (NEC) */ -void pce_cd_device::nec_get_subq() -{ - /* WP - I do not have access to chds with subchannel information yet, so I'm faking something here */ - uint32_t msf_abs, msf_rel, track, frame; - - if (!m_cd_file) - { - /* Throw some error here */ - reply_status_byte(SCSI_CHECK_CONDITION); - return; - } - - frame = m_current_frame; - - switch (m_cdda_status) - { - case PCE_CD_CDDA_PAUSED: - m_data_buffer[0] = 2; - frame = m_cdda->get_audio_lba(); - break; - case PCE_CD_CDDA_PLAYING: - m_data_buffer[0] = 0; - frame = m_cdda->get_audio_lba(); - break; - default: - m_data_buffer[0] = 3; - break; - } - - msf_abs = lba_to_msf_alt(frame); - track = cdrom_get_track(m_cd_file, frame); - msf_rel = lba_to_msf_alt(frame - cdrom_get_track_start(m_cd_file, track)); - - m_data_buffer[1] = 0x01 | ((cdrom_get_track_type(m_cd_file, cdrom_get_track(m_cd_file, track+1)) == CD_TRACK_AUDIO) ? 0x00 : 0x40); - m_data_buffer[2] = dec_2_bcd(track+1); /* track */ - m_data_buffer[3] = 1; /* index */ - m_data_buffer[4] = dec_2_bcd((msf_rel >> 16) & 0xFF);/* M (relative) */ - m_data_buffer[5] = dec_2_bcd((msf_rel >> 8) & 0xFF); /* S (relative) */ - m_data_buffer[6] = dec_2_bcd(msf_rel & 0xFF); /* F (relative) */ - m_data_buffer[7] = dec_2_bcd((msf_abs >> 16) & 0xFF);/* M (absolute) */ - m_data_buffer[8] = dec_2_bcd((msf_abs >> 8) & 0xFF); /* S (absolute) */ - m_data_buffer[9] = dec_2_bcd(msf_abs & 0xFF); /* F (absolute) */ - m_data_buffer_size = 10; - - m_data_buffer_index = 0; - m_data_transferred = 1; - m_scsi_IO = 1; - m_scsi_CD = 0; -} - -/* 0xDE - GET DIR INFO (NEC) */ -void pce_cd_device::nec_get_dir_info() -{ - uint32_t frame, msf, track = 0; - const cdrom_toc *toc; - logerror("nec get dir info\n"); - - if (!m_cd_file) - { - /* Throw some error here */ - reply_status_byte(SCSI_CHECK_CONDITION); - } - - toc = cdrom_get_toc(m_cd_file); - - switch (m_command_buffer[1]) - { - case 0x00: /* Get first and last track numbers */ - m_data_buffer[0] = dec_2_bcd(1); - m_data_buffer[1] = dec_2_bcd(toc->numtrks); - m_data_buffer_size = 2; - break; - case 0x01: /* Get total disk size in MSF format */ - frame = toc->tracks[toc->numtrks-1].logframeofs; - frame += toc->tracks[toc->numtrks-1].frames; - msf = lba_to_msf(frame + 150); - - m_data_buffer[0] = (msf >> 16) & 0xFF; /* M */ - m_data_buffer[1] = (msf >> 8) & 0xFF; /* S */ - m_data_buffer[2] = msf & 0xFF; /* F */ - m_data_buffer_size = 3; - break; - case 0x02: /* Get track information */ - if (m_command_buffer[2] == 0xAA) - { - frame = toc->tracks[toc->numtrks-1].logframeofs; - frame += toc->tracks[toc->numtrks-1].frames; - m_data_buffer[3] = 0x04; /* correct? */ - } else - { - track = std::max(bcd_2_dec(m_command_buffer[2]), 1U); - frame = toc->tracks[track-1].logframeofs; - // PCE wants the start sector for data tracks to *not* include the pregap - if (toc->tracks[track-1].trktype != CD_TRACK_AUDIO) - { - frame += toc->tracks[track-1].pregap; - } - m_data_buffer[3] = (toc->tracks[track-1].trktype == CD_TRACK_AUDIO) ? 0x00 : 0x04; - } - logerror("track = %d, frame = %d\n", track, frame); - msf = lba_to_msf(frame + 150); - m_data_buffer[0] = (msf >> 16) & 0xFF; /* M */ - m_data_buffer[1] = (msf >> 8) & 0xFF; /* S */ - m_data_buffer[2] = msf & 0xFF; /* F */ - m_data_buffer_size = 4; - break; - default: -// assert(pce_cd_nec_get_dir_info == nullptr); // Not implemented yet - break; - } - - m_data_buffer_index = 0; - m_data_transferred = 1; - m_scsi_IO = 1; - m_scsi_CD = 0; -} - -void pce_cd_device::end_of_list() -{ - reply_status_byte(SCSI_CHECK_CONDITION); -} - -typedef void (pce_cd_device::*command_handler_func)(); - -void pce_cd_device::handle_data_output() -{ - static const struct { - uint8_t command_byte; - uint8_t command_size; - command_handler_func command_handler; - } pce_cd_commands[] = { - { 0x00, 6, &pce_cd_device::test_unit_ready }, /* TEST UNIT READY */ - { 0x08, 6, &pce_cd_device::read_6 }, /* READ (6) */ - { 0xD8,10, &pce_cd_device::nec_set_audio_start_position }, /* NEC SET AUDIO PLAYBACK START POSITION */ - { 0xD9,10, &pce_cd_device::nec_set_audio_stop_position }, /* NEC SET AUDIO PLAYBACK END POSITION */ - { 0xDA,10, &pce_cd_device::nec_pause }, /* NEC PAUSE */ - { 0xDD,10, &pce_cd_device::nec_get_subq }, /* NEC GET SUBCHANNEL Q */ - { 0xDE,10, &pce_cd_device::nec_get_dir_info }, /* NEC GET DIR INFO */ - { 0xFF, 1, &pce_cd_device::end_of_list } /* end of list marker */ - }; - - if (m_scsi_REQ && m_scsi_ACK) - { - /* Command byte received */ - logerror("Command byte $%02X received\n", m_cdc_data); - - /* Check for buffer overflow */ - assert(m_command_buffer_index < PCE_CD_COMMAND_BUFFER_SIZE); - - m_command_buffer[m_command_buffer_index] = m_cdc_data; - m_command_buffer_index++; - m_scsi_REQ = 0; - } - - if (! m_scsi_REQ && ! m_scsi_ACK && m_command_buffer_index) - { - int i = 0; - - logerror("Check if command done\n"); - - for(i = 0; m_command_buffer[0] > pce_cd_commands[i].command_byte; i++); - - /* Check for unknown commands */ - if (m_command_buffer[0] != pce_cd_commands[i].command_byte) - { - logerror("Unrecognized command: %02X\n", m_command_buffer[0]); - if (m_command_buffer[0] == 0x03) - popmessage("CD command 0x03 issued (Request Sense), contact MESSdev"); - } - assert(m_command_buffer[0] == pce_cd_commands[i].command_byte); - - if (m_command_buffer_index == pce_cd_commands[i].command_size) - { - //printf("%02x command issued\n",m_command_buffer[0]); - (this->*pce_cd_commands[i].command_handler)(); - m_command_buffer_index = 0; - } - else - { - m_scsi_REQ = 1; - } - } -} - -void pce_cd_device::handle_data_input() -{ - if (m_scsi_CD) - { - /* Command / Status byte */ - if (m_scsi_REQ && m_scsi_ACK) - { - logerror("status sent\n"); - m_scsi_REQ = 0; - m_status_sent = 1; - } - - if (! m_scsi_REQ && ! m_scsi_ACK && m_status_sent) - { - m_status_sent = 0; - if (m_message_after_status) - { - logerror("message after status\n"); - m_message_after_status = 0; - m_scsi_MSG = m_scsi_REQ = 1; - m_cdc_data = 0; - } - } - } - else - { - /* Data */ - if (m_scsi_REQ && m_scsi_ACK) - { - m_scsi_REQ = 0; - } - - if (! m_scsi_REQ && ! m_scsi_ACK) - { - if (m_data_buffer_index == m_data_buffer_size) - { - set_irq_line(PCE_CD_IRQ_TRANSFER_READY, CLEAR_LINE); - if (m_data_transferred) - { - m_data_transferred = 0; - reply_status_byte(SCSI_STATUS_OK); - set_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE); - } - } - else - { - logerror("Transfer byte %02x from offset %d %d\n",m_data_buffer[m_data_buffer_index] , m_data_buffer_index, m_current_frame); - m_cdc_data = m_data_buffer[m_data_buffer_index]; - m_data_buffer_index++; - m_scsi_REQ = 1; - } - } - } -} - -void pce_cd_device::handle_message_output() -{ - if (m_scsi_REQ && m_scsi_ACK) - m_scsi_REQ = 0; -} - -void pce_cd_device::handle_message_input() -{ - if (m_scsi_REQ && m_scsi_ACK) - { - m_scsi_REQ = 0; - m_message_sent = 1; - } - - if (! m_scsi_REQ && ! m_scsi_ACK && m_message_sent) - { - m_message_sent = 0; - m_scsi_BSY = 0; - } -} - -/* Update internal CD statuses */ -void pce_cd_device::update() -{ - /* Check for reset of CD unit */ - if (m_scsi_RST != m_scsi_last_RST) - { - if (m_scsi_RST) - { - logerror("Performing CD reset\n"); - /* Reset internal data */ - m_scsi_BSY = m_scsi_SEL = m_scsi_CD = m_scsi_IO = 0; - m_scsi_MSG = m_scsi_REQ = m_scsi_ATN = 0; - m_cd_motor_on = 0; - m_selected = 0; - m_cdda_status = PCE_CD_CDDA_OFF; - m_cdda->stop_audio(); - m_adpcm_dma_timer->adjust(attotime::never); // stop ADPCM DMA here - } - m_scsi_last_RST = m_scsi_RST; - } - - /* Check if bus can be freed */ - if (! m_scsi_SEL && ! m_scsi_BSY && m_selected) - { - logerror("freeing bus\n"); - m_selected = 0; - m_scsi_CD = m_scsi_MSG = m_scsi_IO = m_scsi_REQ = 0; - set_irq_line(PCE_CD_IRQ_TRANSFER_DONE, CLEAR_LINE); - } - - /* Select the CD device */ - if (m_scsi_SEL) - { - if (! m_selected) - { - m_selected = 1; - logerror("Setting CD in device selection\n"); - m_scsi_BSY = m_scsi_REQ = m_scsi_CD = 1; - m_scsi_MSG = m_scsi_IO = 0; - } - } - - if (m_scsi_ATN) - { - } - else - { - /* Check for data and message phases */ - if (m_scsi_BSY) - { - if (m_scsi_MSG) - { - /* message phase */ - if (m_scsi_IO) - { - handle_message_input(); - } - else - { - handle_message_output(); - } - } - else - { - /* data phase */ - if (m_scsi_IO) - { - /* Reading data from target */ - handle_data_input(); - } - else - { - /* Sending data to target */ - handle_data_output(); - } - } - } - } - - /* FIXME: presumably CD-DA needs an irq interface for this */ - if (m_cdda->audio_ended() && m_end_mark == 1) - { - switch (m_cdda_play_mode & 3) - { - case 1: m_cdda->start_audio(m_current_frame, m_end_frame - m_current_frame); m_end_mark = 1; break; //play with repeat - case 2: set_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE); m_end_mark = 0; break; //irq when finished - case 3: m_end_mark = 0; break; //play without repeat - } - } -} - -void pce_cd_device::set_irq_line(int num, int state) -{ - if (state == ASSERT_LINE) - m_irq_status |= num; - else - m_irq_status &= ~num; - - if (m_irq_mask & m_irq_status & 0x7c) - { - //printf("IRQ PEND = %02x MASK = %02x IRQ ENABLE %02X\n",m_irq_mask & m_irq_status & 0x7c,m_irq_mask & 0x7c,m_irq_status & 0x7c); - m_maincpu->set_input_line(1, ASSERT_LINE); - } - else - { - m_maincpu->set_input_line(1, CLEAR_LINE); - } -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::data_timer_callback) -{ - if (m_data_buffer_index == m_data_buffer_size) - { - /* Read next data sector */ - logerror("read sector %d\n", m_current_frame); - if (! cdrom_read_data(m_cd_file, m_current_frame, m_data_buffer.get(), CD_TRACK_MODE1)) - { - logerror("Mode1 CD read failed for frame #%d\n", m_current_frame); - } - else - { - logerror("Successfully read mode1 frame #%d\n", m_current_frame); - } - - m_data_buffer_index = 0; - m_data_buffer_size = 2048; - m_current_frame++; - - m_scsi_IO = 1; - m_scsi_CD = 0; - - if (m_current_frame == m_end_frame) - { - /* We are done, disable the timer */ - logerror("Last frame read from CD\n"); - m_data_transferred = 1; - // data transfer is done, issue a pause - m_cdda_status = PCE_CD_CDDA_PAUSED; - m_data_timer->adjust(attotime::never); - } - else - { - m_data_transferred = 0; - } - } -} - -WRITE8_MEMBER(pce_cd_device::bram_w) -{ - if (!m_bram_locked) - { - m_bram[offset & (PCE_BRAM_SIZE - 1)] = data; - } -} - -READ8_MEMBER(pce_cd_device::bram_r) -{ - return m_bram[(offset & (PCE_BRAM_SIZE - 1)) + m_bram_locked * PCE_BRAM_SIZE]; -} - -void pce_cd_device::set_adpcm_ram_byte(uint8_t val) -{ - if (m_adpcm_write_buf > 0) - { - m_adpcm_write_buf--; - } - else - { - m_adpcm_ram[m_adpcm_write_ptr] = val; - m_adpcm_write_ptr = ((m_adpcm_write_ptr + 1) & 0xffff); - //TODO: length + 1 - } -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::cdda_fadeout_callback) -{ - m_cdda_volume -= 0.1; - - if (m_cdda_volume <= 0) - { - m_cdda_volume = 0.0; - m_cdda_fadeout_timer->adjust(attotime::never); - } - else - { - m_cdda_fadeout_timer->adjust(attotime::from_usec(param), param); - } - - m_cdda->set_output_gain(ALL_OUTPUTS, m_cdda_volume / 100.0); -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::cdda_fadein_callback) -{ - m_cdda_volume += 0.1; - - if (m_cdda_volume >= 100.0) - { - m_cdda_volume = 100.0; - m_cdda_fadein_timer->adjust(attotime::never); - } - else - { - m_cdda_fadein_timer->adjust(attotime::from_usec(param), param); - } - - m_cdda->set_output_gain(ALL_OUTPUTS, m_cdda_volume / 100.0); -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::adpcm_fadeout_callback) -{ - m_adpcm_volume -= 0.1; - - if (m_adpcm_volume <= 0) - { - m_adpcm_volume = 0.0; - m_adpcm_fadeout_timer->adjust(attotime::never); - } - else - { - m_adpcm_fadeout_timer->adjust(attotime::from_usec(param), param); - } - - m_msm->set_output_gain(ALL_OUTPUTS, m_adpcm_volume / 100.0); -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::adpcm_fadein_callback) -{ - m_adpcm_volume += 0.1; - - if (m_adpcm_volume >= 100.0) - { - m_adpcm_volume = 100.0; - m_adpcm_fadein_timer->adjust(attotime::never); - } - else - { - m_adpcm_fadein_timer->adjust(attotime::from_usec(param), param); - } - - m_msm->set_output_gain(ALL_OUTPUTS, m_adpcm_volume / 100.0); -} - -/* - * - * Register section - * - */ - -// CD Interface Register 0x00 - CDC status -// x--- ---- busy signal -// -x-- ---- request signal -// ---x ---- cd signal -// ---- x--- i/o signal -READ8_MEMBER(pce_cd_device::cdc_status_r) -{ - uint8_t res = (m_cdc_status & 7); - res |= m_scsi_BSY ? 0x80 : 0; - res |= m_scsi_REQ ? 0x40 : 0; - res |= m_scsi_MSG ? 0x20 : 0; - res |= m_scsi_CD ? 0x10 : 0; - res |= m_scsi_IO ? 0x08 : 0; - return res; -} - -WRITE8_MEMBER(pce_cd_device::cdc_status_w) -{ - /* select device (which bits??) */ - m_scsi_SEL = 1; - update(); - m_scsi_SEL = 0; - m_adpcm_dma_timer->adjust(attotime::never); // stop ADPCM DMA here - /* any write here clears CD transfer irqs */ - set_irq_line(0x70, CLEAR_LINE); - m_cdc_status = data; -} - -// CD Interface Register 0x01 - CDC command / status / data -READ8_MEMBER(pce_cd_device::cdc_data_r) -{ - return m_cdc_data; -} - -WRITE8_MEMBER(pce_cd_device::cdc_data_w) -{ - m_cdc_data = data; -} - - -// CD Interface Register 0x02 - IRQ Mask and CD control -// x--- ---- to SCSI ACK -// -x-- ---- transfer ready irq -// --x- ---- transfer done irq -// ---x ---- BRAM irq? -// ---- x--- ADPCM FULL irq -// ---- -x-- ADPCM HALF irq -READ8_MEMBER(pce_cd_device::irq_mask_r) -{ - return m_irq_mask; -} - -WRITE8_MEMBER(pce_cd_device::irq_mask_w) -{ - m_scsi_ACK = data & 0x80; - m_irq_mask = data; - set_irq_line(0, 0); -} - -// CD Interface Register 0x03 - BRAM lock / CD status (read only) -// -x-- ---- acknowledge signal -// --x- ---- done signal -// ---x ---- bram signal -// ---- x--- ADPCM 2 -// ---- -x-- ADPCM 1 -// ---- --x- CDDA left/right speaker select -READ8_MEMBER(pce_cd_device::irq_status_r) -{ - uint8_t res = m_irq_status & 0x6e; - // a read here locks the BRAM - m_bram_locked = 1; - res |= (m_cd_motor_on ? 0x10 : 0); - // TODO: gross hack, needs actual behaviour of CDDA data select - m_irq_status ^= 0x02; - return res; -} - -// CD Interface Register 0x04 - CD reset -// ---- --x- to SCSI RST -READ8_MEMBER(pce_cd_device::cdc_reset_r) -{ - return m_reset_reg; -} - -WRITE8_MEMBER(pce_cd_device::cdc_reset_w) -{ - m_scsi_RST = data & 0x02; - m_reset_reg = data; -} - -// CD Interface Register 0x05 - CD-DA Volume low 8-bit port -// CD Interface Register 0x06 - CD-DA Volume high 8-bit port -// TODO: port 5 also converts? -READ8_MEMBER(pce_cd_device::cdda_data_r) -{ - uint8_t port_shift = offset ? 0 : 8; - - return ((m_cdda->get_channel_volume(m_irq_status & 2) ? 0 : 1) >> port_shift) & 0xff; -} - -// CD Interface Register 0x07 - BRAM unlock / CD status -// x--- ---- Enables BRAM -READ8_MEMBER(pce_cd_device::bram_status_r) -{ - uint8_t res = (m_bram_locked ? (m_bram_status & 0x7f) : (m_bram_status | 0x80)); - return res; -} - -WRITE8_MEMBER(pce_cd_device::bram_unlock_w) -{ - if (data & 0x80) - m_bram_locked = 0; - m_bram_status = data; -} - -// CD Interface Register 0x08 - CD data (R) / ADPCM address low (W) -READ8_MEMBER(pce_cd_device::cd_data_r) -{ - return get_cd_data_byte(); -} - -WRITE8_MEMBER(pce_cd_device::adpcm_address_lo_w) -{ - m_adpcm_latch_address = (data & 0xff) | (m_adpcm_latch_address & 0xff00); -} - -// CD Interface Register 0x09 - ADPCM address high (W) -WRITE8_MEMBER(pce_cd_device::adpcm_address_hi_w) -{ - m_adpcm_latch_address = (data << 8) | (m_adpcm_latch_address & 0xff); -} - -// CD interface Register 0x0a - ADPCM RAM data port -READ8_MEMBER(pce_cd_device::adpcm_data_r) -{ - return get_adpcm_ram_byte(); -} - -WRITE8_MEMBER(pce_cd_device::adpcm_data_w) -{ - set_adpcm_ram_byte(data); -} - -// CD interface Register 0x0b - ADPCM DMA control -READ8_MEMBER(pce_cd_device::adpcm_dma_control_r) -{ - return m_adpcm_dma_reg; -} - -WRITE8_MEMBER(pce_cd_device::adpcm_dma_control_w) -{ - if (data & 3) - { - m_adpcm_dma_timer->adjust(attotime::from_hz(PCE_CD_DATA_FRAMES_PER_SECOND * 2048), 0, attotime::from_hz(PCE_CD_DATA_FRAMES_PER_SECOND * 2048)); - m_adpcm_status |= 4; - } - m_adpcm_dma_reg = data; -} - -// CD Interface Register 0x0c - ADPCM status -// x--- ---- ADPCM is reading data -// ---- x--- ADPCM playback (0) stopped (1) currently playing -// ---- -x-- pending ADPCM data write -// ---- ---x ADPCM playback (1) stopped (0) currently playing -READ8_MEMBER(pce_cd_device::adpcm_status_r) -{ - return m_adpcm_status; -} - -// CD Interface Register 0x0d - ADPCM address control -// x--- ---- ADPCM reset -// -x-- ---- ADPCM play -// --x- ---- ADPCM repeat -// ---x ---- ADPCM set length -// ---- x--- ADPCM set read address -// ---- --xx ADPCM set write address -// TODO: some games reads bit 5 and wants it to be low otherwise they hangs, -// how that can cope with "repeat"? -READ8_MEMBER(pce_cd_device::adpcm_address_control_r) -{ - return m_adpcm_control; -} - -WRITE8_MEMBER(pce_cd_device::adpcm_address_control_w) -{ - if ((m_adpcm_control & 0x80) && !(data & 0x80)) // ADPCM reset - { - /* Reset ADPCM hardware */ - m_adpcm_read_ptr = 0; - m_adpcm_write_ptr = 0; - m_msm_start_addr = 0; - m_msm_end_addr = 0; - m_msm_half_addr = 0; - m_msm_nibble = 0; - adpcm_stop(0); - m_msm->reset_w(1); - } - - if ((data & 0x40) && ((m_adpcm_control & 0x40) == 0)) // ADPCM play - { - m_msm_start_addr = (m_adpcm_read_ptr); - m_msm_end_addr = (m_adpcm_read_ptr + m_adpcm_length) & 0xffff; - m_msm_half_addr = (m_adpcm_read_ptr + (m_adpcm_length / 2)) & 0xffff; - m_msm_nibble = 0; - adpcm_play(); - m_msm->reset_w(0); - - //popmessage("%08x %08x",m_adpcm_read_ptr,m_adpcm_length); - } - else if ((data & 0x40) == 0) - { - /* used by Buster Bros to cancel an in-flight sample */ - adpcm_stop(0); - m_msm->reset_w(1); - } - - m_msm_repeat = BIT(data, 5); - - if (data & 0x10) //ADPCM set length - { - m_adpcm_length = m_adpcm_latch_address; - } - if (data & 0x08) //ADPCM set read address - { - m_adpcm_read_ptr = m_adpcm_latch_address; - m_adpcm_read_buf = 2; - } - if ((data & 0x02) == 0x02) //ADPCM set write address - { - m_adpcm_write_ptr = m_adpcm_latch_address; - m_adpcm_write_buf = data & 1; - } - - m_adpcm_control = data; -} - -// CD Interface Register 0x0e - ADPCM playback rate -WRITE8_MEMBER(pce_cd_device::adpcm_playback_rate_w) -{ - m_adpcm_clock_divider = 0x10 - (data & 0x0f); - m_msm->set_unscaled_clock((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); -} - -// CD Interface Register 0x0f - ADPCM fade in/out register -// ---- xxxx command setting: -// 0x00 ADPCM/CD-DA Fade-in -// 0x01 CD-DA fade-in -// 0x08 CD-DA fade-out (short) ADPCM fade-in -// 0x09 CD-DA fade-out (long) -// 0x0a ADPCM fade-out (long) -// 0x0c CD-DA fade-out (short) ADPCM fade-in -// 0x0d CD-DA fade-out (short) -// 0x0e ADPCM fade-out (short) -WRITE8_MEMBER(pce_cd_device::fade_register_w) -{ - // TODO: timers needs HW tests - if (m_fade_reg != data) - { - switch (data & 0xf) - { - case 0x00: //CD-DA / ADPCM enable (100 msecs) - m_cdda_volume = 0.0; - m_cdda_fadein_timer->adjust(attotime::from_usec(100), 100); - m_adpcm_volume = 0.0; - m_adpcm_fadein_timer->adjust(attotime::from_usec(100), 100); - m_cdda_fadeout_timer->adjust(attotime::never); - m_adpcm_fadeout_timer->adjust(attotime::never); - break; - case 0x01: //CD-DA enable (100 msecs) - m_cdda_volume = 0.0; - m_cdda_fadein_timer->adjust(attotime::from_usec(100), 100); - m_cdda_fadeout_timer->adjust(attotime::never); - break; - case 0x08: //CD-DA short (1500 msecs) fade out / ADPCM enable - m_cdda_volume = 100.0; - m_cdda_fadeout_timer->adjust(attotime::from_usec(1500), 1500); - m_adpcm_volume = 0.0; - m_adpcm_fadein_timer->adjust(attotime::from_usec(100), 100); - m_cdda_fadein_timer->adjust(attotime::never); - m_adpcm_fadeout_timer->adjust(attotime::never); - break; - case 0x09: //CD-DA long (5000 msecs) fade out - m_cdda_volume = 100.0; - m_cdda_fadeout_timer->adjust(attotime::from_usec(5000), 5000); - m_cdda_fadein_timer->adjust(attotime::never); - break; - case 0x0a: //ADPCM long (5000 msecs) fade out - m_adpcm_volume = 100.0; - m_adpcm_fadeout_timer->adjust(attotime::from_usec(5000), 5000); - m_adpcm_fadein_timer->adjust(attotime::never); - break; - case 0x0c: //CD-DA short (1500 msecs) fade out / ADPCM enable - m_cdda_volume = 100.0; - m_cdda_fadeout_timer->adjust(attotime::from_usec(1500), 1500); - m_adpcm_volume = 0.0; - m_adpcm_fadein_timer->adjust(attotime::from_usec(100), 100); - m_cdda_fadein_timer->adjust(attotime::never); - m_adpcm_fadeout_timer->adjust(attotime::never); - break; - case 0x0d: //CD-DA short (1500 msecs) fade out - m_cdda_volume = 100.0; - m_cdda_fadeout_timer->adjust(attotime::from_usec(1500), 1500); - m_cdda_fadein_timer->adjust(attotime::never); - break; - case 0x0e: //ADPCM short (1500 msecs) fade out - m_adpcm_volume = 100.0; - m_adpcm_fadeout_timer->adjust(attotime::from_usec(1500), 1500); - m_adpcm_fadein_timer->adjust(attotime::never); - break; - default: - popmessage("CD-DA / ADPCM Fade effect mode %02x, contact MESSdev",data & 0x0f); - break; - } - } - m_fade_reg = data; -} - -TIMER_CALLBACK_MEMBER(pce_cd_device::clear_ack) -{ - update(); - m_scsi_ACK = 0; - update(); - if (m_scsi_CD) - { - m_adpcm_dma_reg &= 0xFC; - } -} - -uint8_t pce_cd_device::get_cd_data_byte() -{ - uint8_t data = m_cdc_data; - if (m_scsi_REQ && !m_scsi_ACK && !m_scsi_CD) - { - if (m_scsi_IO) - { - m_scsi_ACK = 1; - machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(15), timer_expired_delegate(FUNC(pce_cd_device::clear_ack),this)); - } - } - return data; -} - - -TIMER_CALLBACK_MEMBER(pce_cd_device::adpcm_dma_timer_callback) -{ - if (m_scsi_REQ && !m_scsi_ACK && !m_scsi_CD && m_scsi_IO) - { - m_adpcm_ram[m_adpcm_write_ptr] = get_cd_data_byte(); - m_adpcm_write_ptr = (m_adpcm_write_ptr + 1) & 0xFFFF; - - m_adpcm_status &= ~4; - } -} - -uint8_t pce_cd_device::get_adpcm_ram_byte() -{ - if (m_adpcm_read_buf > 0) - { - m_adpcm_read_buf--; - return 0; - } - else - { - uint8_t res; - - res = m_adpcm_ram[m_adpcm_read_ptr]; - m_adpcm_read_ptr = ((m_adpcm_read_ptr + 1) & 0xffff); - - return res; - } -} - -/* - * - * I/O accessors - * - */ -// TODO: more stuff actually belongs to the whole CD interface, -// cfr. pce_cd_intf_r/w in drivers/pce.cpp -READ8_MEMBER(pce_cd_device::intf_r) -{ - //logerror("%s: read from CD interface offset %02X\n", machine().describe_context(), offset ); - - address_space &io_space = this->space(AS_IO); - return io_space.read_byte(offset & 0xf); -} - -WRITE8_MEMBER(pce_cd_device::intf_w) -{ - //logerror("%s write to CD interface offset %02X, data %02X\n", machine().describe_context(), offset, data); - - address_space &io_space = this->space(AS_IO); - io_space.write_byte(offset & 0xf, data); -} - -/* - -PC Engine Arcade Card emulation - -*/ - -READ8_MEMBER(pce_cd_device::acard_r) -{ - uint8_t r_num; - - if ((offset & 0x2e0) == 0x2e0) - { - switch (offset & 0x2ef) - { - case 0x2e0: return (m_acard_shift >> 0) & 0xff; - case 0x2e1: return (m_acard_shift >> 8) & 0xff; - case 0x2e2: return (m_acard_shift >> 16) & 0xff; - case 0x2e3: return (m_acard_shift >> 24) & 0xff; - case 0x2e4: return (m_acard_shift_reg); - case 0x2e5: return m_acard_latch; - case 0x2ee: return 0x10; - case 0x2ef: return 0x51; - } - - return 0; - } - - r_num = (offset & 0x30) >> 4; - - switch (offset & 0x0f) - { - case 0x00: - case 0x01: - { - uint8_t res; - if (m_acard_ctrl[r_num] & 2) - res = m_acard_ram[(m_acard_base_addr[r_num] + m_acard_addr_offset[r_num]) & 0x1fffff]; - else - res = m_acard_ram[m_acard_base_addr[r_num] & 0x1fffff]; - - if (m_acard_ctrl[r_num] & 0x1) - { - if (m_acard_ctrl[r_num] & 0x10) - { - m_acard_base_addr[r_num] += m_acard_addr_inc[r_num]; - m_acard_base_addr[r_num] &= 0xffffff; - } - else - { - m_acard_addr_offset[r_num] += m_acard_addr_inc[r_num]; - } - } - - return res; - } - case 0x02: return (m_acard_base_addr[r_num] >> 0) & 0xff; - case 0x03: return (m_acard_base_addr[r_num] >> 8) & 0xff; - case 0x04: return (m_acard_base_addr[r_num] >> 16) & 0xff; - case 0x05: return (m_acard_addr_offset[r_num] >> 0) & 0xff; - case 0x06: return (m_acard_addr_offset[r_num] >> 8) & 0xff; - case 0x07: return (m_acard_addr_inc[r_num] >> 0) & 0xff; - case 0x08: return (m_acard_addr_inc[r_num] >> 8) & 0xff; - case 0x09: return m_acard_ctrl[r_num]; - default: return 0; - } -} - -WRITE8_MEMBER(pce_cd_device::acard_w) -{ - uint8_t w_num; - - if ((offset & 0x2e0) == 0x2e0) - { - switch (offset & 0x0f) - { - case 0: m_acard_shift = (data & 0xff) | (m_acard_shift & 0xffffff00); break; - case 1: m_acard_shift = (data << 8) | (m_acard_shift & 0xffff00ff); break; - case 2: m_acard_shift = (data << 16) | (m_acard_shift & 0xff00ffff); break; - case 3: m_acard_shift = (data << 24) | (m_acard_shift & 0x00ffffff); break; - case 4: - { - m_acard_shift_reg = data & 0x0f; - - if (m_acard_shift_reg != 0) - { - m_acard_shift = (m_acard_shift_reg < 8) ? - (m_acard_shift << m_acard_shift_reg) - : (m_acard_shift >> (16 - m_acard_shift_reg)); - } - } - break; - case 5: m_acard_latch = data; break; - } - } - else - { - w_num = (offset & 0x30) >> 4; - - switch (offset & 0x0f) - { - case 0x00: - case 0x01: - if (m_acard_ctrl[w_num] & 2) - m_acard_ram[(m_acard_base_addr[w_num] + m_acard_addr_offset[w_num]) & 0x1fffff] = data; - else - m_acard_ram[m_acard_base_addr[w_num] & 0x1FFFFF] = data; - - if (m_acard_ctrl[w_num] & 0x1) - { - if (m_acard_ctrl[w_num] & 0x10) - { - m_acard_base_addr[w_num] += m_acard_addr_inc[w_num]; - m_acard_base_addr[w_num] &= 0xffffff; - } - else - { - m_acard_addr_offset[w_num] += m_acard_addr_inc[w_num]; - } - } - - break; - - case 0x02: m_acard_base_addr[w_num] = (data & 0xff) | (m_acard_base_addr[w_num] & 0xffff00); break; - case 0x03: m_acard_base_addr[w_num] = (data << 8) | (m_acard_base_addr[w_num] & 0xff00ff); break; - case 0x04: m_acard_base_addr[w_num] = (data << 16) | (m_acard_base_addr[w_num] & 0x00ffff); break; - case 0x05: m_acard_addr_offset[w_num] = (data & 0xff) | (m_acard_addr_offset[w_num] & 0xff00); break; - case 0x06: - m_acard_addr_offset[w_num] = (data << 8) | (m_acard_addr_offset[w_num] & 0x00ff); - - if ((m_acard_ctrl[w_num] & 0x60) == 0x40) - { - m_acard_base_addr[w_num] += m_acard_addr_offset[w_num] + ((m_acard_ctrl[w_num] & 0x08) ? 0xff0000 : 0); - m_acard_base_addr[w_num] &= 0xffffff; - } - break; - case 0x07: m_acard_addr_inc[w_num] = (data & 0xff) | (m_acard_addr_inc[w_num] & 0xff00); break; - case 0x08: m_acard_addr_inc[w_num] = (data << 8) | (m_acard_addr_inc[w_num] & 0x00ff); break; - case 0x09: m_acard_ctrl[w_num] = data & 0x7f; break; - case 0x0a: - if ((m_acard_ctrl[w_num] & 0x60) == 0x60) - { - m_acard_base_addr[w_num] += m_acard_addr_offset[w_num]; - m_acard_base_addr[w_num] &= 0xffffff; - } - break; - } - } -} |