diff --git a/src/devices/bus/amiga/cpuslot/a570.cpp b/src/devices/bus/amiga/cpuslot/a570.cpp index 7689125ee9c..4e134004580 100644 --- a/src/devices/bus/amiga/cpuslot/a570.cpp +++ b/src/devices/bus/amiga/cpuslot/a570.cpp @@ -12,14 +12,17 @@ - ROM label for the A690: "391298-01 V1.0 Copyright ©1991 CBM C480" - The ROM P/N 391298-01 seems to have been used for multiple versions - There are expansion slots for a 2 MB RAM expansion and a SCSI module + - Uses the CR-512-B drive from MKE (Matsushita Kotobuki Electronics) + - An FPGA is used in place of many discrete logic chips of the CDTV TODO: - - DMAC/CD-ROM drive hookup (needs DMAC rev 2) + - Volume control (LC7883M) ***************************************************************************/ #include "emu.h" #include "a570.h" +#include "speaker.h" #define VERBOSE (LOG_GENERAL) @@ -37,7 +40,10 @@ namespace bus::amiga::cpuslot { a570_device::a570_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, AMIGA_CPUSLOT_A570, tag, owner, clock), device_amiga_cpuslot_interface(mconfig, *this), + m_irq(*this, "irq"), m_dmac(*this, "dmac"), + m_tpi(*this, "tpi"), + m_drive(*this, "drive"), m_config(*this, "config") { } @@ -94,12 +100,39 @@ const tiny_rom_entry *a570_device::device_rom_region() const void a570_device::device_add_mconfig(machine_config &config) { + INPUT_MERGER_ANY_HIGH(config, m_irq); + m_irq->output_handler().set([this] (int state) { m_host->int2_w(state); }); + AMIGA_DMAC_REV2(config, m_dmac, 28.37516_MHz_XTAL / 4); // 7M m_dmac->cfgout_cb().set([this] (int state) { m_host->cfgout_w(state); }); - m_dmac->int_cb().set([this] (int state) { m_host->int2_w(state); }); + m_dmac->int_cb().set(m_irq, FUNC(input_merger_any_high_device::in_w<0>)); + m_dmac->csx0_read_cb().set(m_drive, FUNC(cr511b_device::read)); + m_dmac->csx0_write_cb().set(m_drive, FUNC(cr511b_device::write)); + m_dmac->csx0_a4_read_cb().set(m_tpi, FUNC(tpi6525_device::read)); + m_dmac->csx0_a4_write_cb().set(m_tpi, FUNC(tpi6525_device::write)); + m_dmac->xdack_read_cb().set(m_drive, FUNC(cr511b_device::read)); AT28C16(config, "nvram0", 0); AT28C16(config, "nvram1", 0); + + TPI6525(config, m_tpi, 0); + m_tpi->out_irq_cb().set(m_irq, FUNC(input_merger_any_high_device::in_w<1>)); + m_tpi->out_pb_cb().set(FUNC(a570_device::tpi_portb_w)); + + CR511B(config, m_drive, 0); + m_drive->add_route(0, "lspeaker", 1.0); + m_drive->add_route(1, "rspeaker", 1.0); + m_drive->scor_cb().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert(); + m_drive->stch_cb().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert(); + m_drive->sten_cb().set(m_tpi, FUNC(tpi6525_device::i3_w)); + m_drive->sten_cb().append(FUNC(a570_device::sten_w)); + m_drive->drq_cb().set(m_tpi, FUNC(tpi6525_device::i4_w)); + m_drive->drq_cb().append(FUNC(a570_device::drq_w)); + + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + + // TODO: Add stereo input for Amiga sound } @@ -118,6 +151,33 @@ void a570_device::device_start() // register for save states save_pointer(NAME(m_ram), 0x200000/2); + save_item(NAME(m_sten)); +} + +void a570_device::sten_w(int state) +{ + m_sten = bool(state); +} + +void a570_device::drq_w(int state) +{ + if (m_sten) + m_dmac->xdreq_w(state); +} + +void a570_device::tpi_portb_w(uint8_t data) +{ + // 7------- daclch (lc7883m) + // -6------ dacst (lc7883m) + // --5----- dacatt (lc7883m) + // ---4---- weprom + // ----3--- dten (drive) + // -----2-- xaen (drive) + // ------1- enable (drive) + // -------0 cmd (drive) + + m_drive->enable_w(BIT(data, 1)); + m_drive->cmd_w(BIT(data, 0)); } // the dmac handles this diff --git a/src/devices/bus/amiga/cpuslot/a570.h b/src/devices/bus/amiga/cpuslot/a570.h index 09ce1a18844..63d97b7703f 100644 --- a/src/devices/bus/amiga/cpuslot/a570.h +++ b/src/devices/bus/amiga/cpuslot/a570.h @@ -14,8 +14,12 @@ #pragma once #include "cpuslot.h" +#include "machine/6525tpi.h" #include "machine/at28c16.h" +#include "machine/cr511b.h" #include "machine/dmac.h" +#include "machine/input_merger.h" + namespace bus::amiga::cpuslot { @@ -38,9 +42,19 @@ protected: private: void map(address_map &map) ATTR_COLD; + void sten_w(int state); + void drq_w(int state); + + void tpi_portb_w(uint8_t data); + + required_device m_irq; required_device m_dmac; + required_device m_tpi; + required_device m_drive; required_ioport m_config; + bool m_sten; + std::unique_ptr m_ram; }; diff --git a/src/devices/machine/cr511b.cpp b/src/devices/machine/cr511b.cpp index 3ac800c1db7..dc3fa967ba5 100644 --- a/src/devices/machine/cr511b.cpp +++ b/src/devices/machine/cr511b.cpp @@ -1,97 +1,764 @@ -// license:BSD-3-Clause -// copyright-holders:Dirk Best +// license: BSD-3-Clause +// copyright-holders: Dirk Best /*************************************************************************** CR-511-B CD-ROM drive CD-ROM drive with a custom MKE/Panasonic interface as used in the - Commodore CDTV and early SoundBlaster cards. + Commodore CDTV. Similar to the interface on early SoundBlaster cards. + + Hardware: + - MN188161REB1 + - M50423FP + - 4464S-08LL + - LC8951 + + TODO: + - Subcode P-W data + - Timing for status data or status change ***************************************************************************/ #include "emu.h" #include "cr511b.h" +#define LOG_CMD (1 << 1) +#define LOG_PARAM (1 << 2) +#define LOG_DATA (1 << 3) +#define LOG_SUBQ (1 << 4) +#define LOG_SUBQ2 (1 << 5) // log subq data to popmessage + +#define VERBOSE (LOG_GENERAL | LOG_CMD | LOG_PARAM) + +#define LOGPARAM LOGMASKED(LOG_PARAM, "-> Param: %02x %02x %02x %02x %02x %02x\n", \ + m_input_fifo[1], m_input_fifo[2], m_input_fifo[3], \ + m_input_fifo[4], m_input_fifo[5], m_input_fifo[6]) + +#include "logmacro.h" + //************************************************************************** -// DEVICE DEFINITIONS +// TYPE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(CR511B, cr511b_device, "cr511b", "CR-511-B CD-ROM drive") -//------------------------------------------------- -// device_add_mconfig - add device configuration -//------------------------------------------------- +cr511b_device::cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + cdrom_image_device(mconfig, CR511B, tag, owner, clock), + device_mixer_interface(mconfig, *this, 2), + m_cdda(*this, "cdda"), + m_stch_cb(*this), + m_sten_cb(*this), + m_drq_cb(*this), + m_dten_cb(*this), + m_scor_cb(*this), + m_input_fifo_pos(0), + m_output_fifo_pos(0), + m_output_fifo_length(0), + m_status(0), + m_enabled(false), + m_cmd(false), + m_status_ready(false), + m_data_ready(false) +{ + set_interface("cdrom"); +} + + +//************************************************************************** +// MACHINE DEFINITIONS +//************************************************************************** void cr511b_device::device_add_mconfig(machine_config &config) { - CDROM(config, m_cdrom).set_interface("cdrom"); CDDA(config, m_cdda); - m_cdda->add_route(0, ":lspeaker", 1.0); - m_cdda->add_route(1, ":rspeaker", 1.0); - m_cdda->set_cdrom_tag("cdrom"); + m_cdda->add_route(0, DEVICE_SELF, 1.0, AUTO_ALLOC_INPUT, 0); + m_cdda->add_route(1, DEVICE_SELF, 1.0, AUTO_ALLOC_INPUT, 1); + m_cdda->set_cdrom_tag(*this); + m_cdda->audio_end_cb().set(FUNC(cr511b_device::audio_end_cb)); } //************************************************************************** -// LIVE DEVICE +// MACHINE EMULATION //************************************************************************** -//------------------------------------------------- -// cr511b_device - constructor -//------------------------------------------------- - -cr511b_device::cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, CR511B, tag, owner, clock), - m_cdrom(*this, "cdrom"), - m_cdda(*this, "cdda"), - m_stch_handler(*this), - m_sten_handler(*this), - m_drq_handler(*this), - m_dten_handler(*this), - m_scor_handler(*this), - m_xaen_handler(*this), - //m_motor(false), - m_enabled(-1), - m_cmd(-1) -{ -} - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - void cr511b_device::device_start() { -} + cdrom_image_device::device_start(); -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- + m_frame_timer = timer_alloc(FUNC(cr511b_device::frame_cb), this); + m_stch_timer = timer_alloc(FUNC(cr511b_device::stch), this); + m_sten_timer = timer_alloc(FUNC(cr511b_device::sten), this); + + std::fill(std::begin(m_input_fifo), std::end(m_input_fifo), 0x00); + std::fill(std::begin(m_output_fifo), std::end(m_output_fifo), 0x00); + + // register for save states + save_item(NAME(m_input_fifo)); + save_item(NAME(m_input_fifo_pos)); + save_item(NAME(m_output_fifo)); + save_item(NAME(m_output_fifo_pos)); + save_item(NAME(m_output_fifo_length)); + save_item(NAME(m_status)); + save_item(NAME(m_sector_size)); + save_item(NAME(m_transfer_lba)); + save_item(NAME(m_transfer_sectors)); + save_item(NAME(m_transfer_length)); + save_item(NAME(m_transfer_buffer)); + save_item(NAME(m_transfer_buffer_pos)); + save_item(NAME(m_enabled)); + save_item(NAME(m_cmd)); + save_item(NAME(m_status_ready)); + save_item(NAME(m_data_ready)); +} void cr511b_device::device_reset() { + cdrom_image_device::device_reset(); + + m_input_fifo_pos = 0; + m_output_fifo_pos = 0; + m_output_fifo_length = 0; + + m_status_ready = false; + m_data_ready = false; + + m_status = STATUS_READY; + + if (exists()) + m_status |= STATUS_MEDIA; + + m_sten_cb(1); + m_stch_cb(0); } -//************************************************************************** -// IMPLEMENTATION -//************************************************************************** +std::pair cr511b_device::call_load() +{ + auto ret = cdrom_image_device::call_load(); + + if (!ret.first) + status_change(m_status | STATUS_MEDIA); + + return ret; +} + +void cr511b_device::call_unload() +{ + status_change(STATUS_READY); + + cdrom_image_device::call_unload(); +} + +uint32_t cr511b_device::lba_to_msf(int32_t lba) +{ + uint32_t msf = 0; + + lba += 2 * 75; // lba 0 is equivalent to msf 00:02:00 + + msf |= ((lba / (60 * 75)) & 0xff) << 16; + msf |= (((lba / 75) % 60) & 0xff) << 8; + msf |= ((lba % 75) & 0xff) << 0; + + return msf; +} + +int32_t cr511b_device::msf_to_lba(uint32_t msf) +{ + uint32_t lba = 0; + + lba += ((msf >> 16) & 0xff) * 60 * 75; + lba += ((msf >> 8) & 0xff) * 75; + lba += ((msf >> 0) & 0xff); + + lba -= 2 * 75; // msf 00:02:00 is equivalent to lba 0 + + return lba; +} + +int cr511b_device::size_to_track_type() +{ + switch (m_sector_size) + { + case 2048: return cdrom_file::CD_TRACK_MODE1; + } + + // have only seen 2048 so far + fatalerror("Unknown sector mode: %d\n", m_sector_size); +} + +TIMER_CALLBACK_MEMBER(cr511b_device::frame_cb) +{ + if (m_transfer_sectors > 0) + { + // old data hasn't been read completely yet + if (m_data_ready) + return; + + LOGMASKED(LOG_DATA, "Reading sector: %d\n", m_transfer_lba); + + read_data(m_transfer_lba, m_transfer_buffer, size_to_track_type()); + + // prepare for next sector + m_transfer_lba++; + m_transfer_sectors--; + m_transfer_buffer_pos = 0; + + // signal that we have data + m_data_ready = true; + m_drq_cb(1); + } + else if (m_status & STATUS_PLAYING) + { + // TODO: subcode handling + m_scor_cb(0); + m_scor_cb(1); + } +} + +TIMER_CALLBACK_MEMBER(cr511b_device::stch) +{ + m_stch_cb(1); + m_stch_cb(0); +} + +void cr511b_device::status_change(uint8_t status) +{ + if (m_status != status) + { + m_status = status; + + if (m_status & STATUS_MOTOR) + m_frame_timer->adjust(attotime::from_hz(75), 0, attotime::from_hz(75)); + else + m_frame_timer->adjust(attotime::never); + + m_stch_timer->adjust(attotime::from_usec(64 * 3)); // TODO + } +} + +TIMER_CALLBACK_MEMBER(cr511b_device::sten) +{ + m_status_ready = true; + + m_sten_cb(0); + m_sten_cb(1); +} + +void cr511b_device::status_enable(uint8_t output_length) +{ + m_input_fifo_pos = 0; + m_output_fifo_pos = 0; + m_output_fifo_length = output_length; + + // do we have status data to send? + if (m_output_fifo_length > 0) + { + if (m_input_fifo[0] != 0x87 || (VERBOSE & LOG_SUBQ)) + LOGMASKED(LOG_CMD, "-> Output: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", m_output_fifo[0], m_output_fifo[1], m_output_fifo[2], m_output_fifo[3], m_output_fifo[4], m_output_fifo[5], m_output_fifo[6], m_output_fifo[7], m_output_fifo[8], m_output_fifo[9], m_output_fifo[10], m_output_fifo[11]); + + m_sten_timer->adjust(attotime::from_usec(64 * 4)); // TODO + } +} + +void cr511b_device::audio_end_cb(int state) +{ + if (!state) + return; + + LOGMASKED(LOG_CMD, "Playing audio finished\n", state); + + status_change(m_status & ~STATUS_PLAYING); +} uint8_t cr511b_device::read() { - return 0xff; + uint8_t data = 0xff; + + if (!m_enabled) + { + LOG("Read while not enabled!\n"); + return data; + } + + if (m_cmd) + { + // command mode + if (m_status_ready) + { + data = m_output_fifo[m_output_fifo_pos]; + + // clear old data once read + m_output_fifo[m_output_fifo_pos++] = 0x00; + + LOGMASKED(LOG_DATA, "Data from drive: %02x (%d of %d)\n", data, m_output_fifo_pos, m_output_fifo_length); + + // more data? + if (m_output_fifo_pos < m_output_fifo_length) + { + m_sten_cb(0); + m_sten_cb(1); + } + else + { + m_status_ready = false; + } + } + } + else + { + // data mode + if (m_data_ready) + { + data = m_transfer_buffer[m_transfer_buffer_pos]; + m_transfer_length--; + + LOGMASKED(LOG_DATA, "Data = %02x, pos %d, length %d\n", data, m_transfer_buffer_pos, m_transfer_length); + + // finished transferring this sector? + if (++m_transfer_buffer_pos == m_sector_size) + { + m_data_ready = false; + m_drq_cb(0); + + if (m_transfer_sectors == 0) + { + LOGMASKED(LOG_DATA, "Read done\n"); + status_change(m_status | STATUS_SUCCESS); + } + } + } + } + + return data; } void cr511b_device::write(uint8_t data) { -} + // verify that we're enabled and are in command mode + if (!m_enabled || !m_cmd) + { + LOG("Invalid write: %02x (enabled: %d, cmd: %d)\n", data, m_enabled, m_cmd); + return; + } -void cr511b_device::enable_w(int state) -{ - m_enabled = state; + m_input_fifo[m_input_fifo_pos++] = data; + + switch (m_input_fifo[0]) + { + case 0x01: if (m_input_fifo_pos == 7) cmd_seek(); break; + case 0x02: if (m_input_fifo_pos == 7) cmd_read(); break; + case 0x04: if (m_input_fifo_pos == 7) cmd_motor_on(); break; + case 0x05: if (m_input_fifo_pos == 7) cmd_motor_off(); break; + case 0x09: if (m_input_fifo_pos == 7) cmd_play_lba(); break; + case 0x0a: if (m_input_fifo_pos == 7) cmd_play_msf(); break; + case 0x0b: if (m_input_fifo_pos == 7) cmd_play_track(); break; + case 0x81: if (m_input_fifo_pos == 1) cmd_read_status(); break; + case 0x82: if (m_input_fifo_pos == 7) cmd_read_error(); break; + case 0x84: if (m_input_fifo_pos == 7) cmd_set_mode(); break; + case 0x87: if (m_input_fifo_pos == 7) cmd_read_subq(); break; + case 0x89: if (m_input_fifo_pos == 7) cmd_read_disc_info(); break; + case 0x8a: if (m_input_fifo_pos == 7) cmd_read_toc(); break; + case 0x8b: if (m_input_fifo_pos == 7) cmd_pause(); break; + case 0xa3: if (m_input_fifo_pos == 7) cmd_front_panel(); break; + + default: + LOG("Unknown command: %02x\n", m_input_fifo[0]); + status_enable(0); + break; + } } void cr511b_device::cmd_w(int state) { - m_cmd = state; + m_cmd = !bool(state); // active low +} + +void cr511b_device::enable_w(int state) +{ + m_enabled = !bool(state); // active low +} + +void cr511b_device::cmd_seek() +{ + LOGMASKED(LOG_CMD, "Command: Seek\n"); + LOGPARAM; + + // TODO: Does this enable STATUS_SUCCESS? + + status_change(m_status | STATUS_MOTOR); + status_enable(0); +} + +void cr511b_device::cmd_read() +{ + LOGMASKED(LOG_CMD, "Command: Read\n"); + LOGPARAM; + + m_transfer_lba = (m_input_fifo[1] << 16) | (m_input_fifo[2] << 8) | (m_input_fifo[3] << 0); + m_transfer_sectors = (m_input_fifo[4] << 8) | (m_input_fifo[5] << 0); + m_transfer_length = m_transfer_sectors * m_sector_size; + + LOGMASKED(LOG_CMD, "-> LBA %d, sectors %d\n", m_transfer_lba, m_transfer_sectors); + + m_cdda->stop_audio(); + + uint8_t status = m_status; + + status &= ~STATUS_PLAYING; + status |= STATUS_MOTOR; + + status_change(status); + status_enable(0); +} + +void cr511b_device::cmd_motor_on() +{ + LOGMASKED(LOG_CMD, "Command: Motor On\n"); + LOGPARAM; + + // TODO: Does this enable STATUS_SUCCESS? + + status_change(m_status | STATUS_MOTOR); + status_enable(0); +} + +void cr511b_device::cmd_motor_off() +{ + LOGMASKED(LOG_CMD, "Command: Motor Off\n"); + LOGPARAM; + + // TODO: Does this enable STATUS_SUCCESS? + + status_change(m_status & ~STATUS_MOTOR); + status_enable(0); +} + +void cr511b_device::cmd_play_lba() +{ + LOGMASKED(LOG_CMD, "Command: Play LBA\n"); + LOGPARAM; + + // haven't found anything that uses it yet + fatalerror("Play LBA: Not implemented\n"); +} + +void cr511b_device::cmd_play_msf() +{ + LOGMASKED(LOG_CMD, "Command: Play MSF\n"); + LOGPARAM; + + uint32_t start = (m_input_fifo[1] << 16) | (m_input_fifo[2] << 8) | (m_input_fifo[3] << 0); + uint32_t end = (m_input_fifo[4] << 16) | (m_input_fifo[5] << 8) | (m_input_fifo[6] << 0); + + int32_t start_lba = msf_to_lba(start); + int32_t end_lba = msf_to_lba(end); + + // play to the end of the disc? + if (end == 0xffffff) + end_lba = get_track_start(0xaa) - 1; + + if (start == 0 && end == 0) + { + LOGMASKED(LOG_CMD, "Stop audio\n"); + + uint8_t status = m_status; + + if (m_cdda->audio_active()) + status |= STATUS_SUCCESS; + + m_cdda->stop_audio(); + + status &= ~STATUS_PLAYING; + status &= ~STATUS_MOTOR; + + status_change(status); + } + else if (start_lba < end_lba) + { + LOGMASKED(LOG_CMD, "Playing audio %02d:%02d.%02d to %02d:%02d.%02d (LBA %d to %d)\n", + m_input_fifo[1], m_input_fifo[2], m_input_fifo[3], + m_input_fifo[4], m_input_fifo[5], m_input_fifo[6], start_lba, end_lba); + + m_cdda->start_audio(start_lba, end_lba - start_lba); + + uint8_t status = m_status; + + status |= STATUS_PLAYING; + status |= STATUS_MOTOR; + + status_change(status); + } + else + { + LOGMASKED(LOG_CMD, "Invalid range %d to %d!\n", start_lba, end_lba); + status_change(m_status | STATUS_ERROR); + } + + status_enable(0); +} + +void cr511b_device::cmd_play_track() +{ + LOGMASKED(LOG_CMD, "Command: Play Track\n"); + LOGPARAM; + + uint8_t start_track = m_input_fifo[1]; + uint8_t start_index = m_input_fifo[2]; // TODO + uint8_t end_track = m_input_fifo[3]; + uint8_t end_index = m_input_fifo[4]; // TODO + + uint32_t start_lba = get_track_start(start_track - 1); + uint32_t end_lba = get_track_start(end_track - 1) - 1; + + LOGMASKED(LOG_CMD, "Playing audio track %d-%d to %d-%d (LBA %d to %d)\n", start_track, start_index, end_track, end_index, start_lba, end_lba); + + m_cdda->start_audio(start_lba, end_lba - start_lba); + + status_change(m_status | STATUS_PLAYING | STATUS_MOTOR); + status_enable(0); +} + +void cr511b_device::cmd_read_status() +{ + LOGMASKED(LOG_CMD, "Command: Read Status\n"); + + m_status &= ~STATUS_SUCCESS; + + m_output_fifo[0] = m_status; + + status_enable(1); +} + +void cr511b_device::cmd_read_error() +{ + LOGMASKED(LOG_CMD, "Command: Read Error\n"); + LOGPARAM; + + m_status &= ~STATUS_ERROR; + m_status |= STATUS_READY; + m_status |= STATUS_SUCCESS; + + m_output_fifo[2] |= (m_status & 0x10); + + status_enable(6); +} + +void cr511b_device::cmd_version() +{ + LOGMASKED(LOG_CMD, "Command: Version\n"); + LOGPARAM; + + // haven't found anything that uses it yet + fatalerror("Version: Not implemented\n"); +} + +void cr511b_device::cmd_set_mode() +{ + LOGMASKED(LOG_CMD, "Command: Set Mode\n"); + LOGPARAM; + + // 01: unknown (value seen: 02) + // 02: sector size hi + // 03: sector size lo + // 04: unknown (value seen 00) + // 05: unknown (value seen 0f) + // 06: unknown (value seen 00) + + m_sector_size = (m_input_fifo[2] << 8) | m_input_fifo[3]; + + LOGMASKED(LOG_CMD, "Sector size = %d\n", m_sector_size); + + m_status |= STATUS_SUCCESS; // ? + + status_enable(0); +} + +void cr511b_device::cmd_read_subq() +{ + LOGMASKED(LOG_SUBQ, "Command: Read SubQ\n"); + if (VERBOSE & LOG_SUBQ) + LOGPARAM; + + // 01: bit 1 - msf or lba + // 02: unknown + // 03: unknown + // 04: unknown + // 05: unknown + // 06: unknown + + bool msf = bool(BIT(m_input_fifo[1], 1)); + + if (m_cdda->audio_active()) + { + uint32_t lba = m_cdda->get_audio_lba(); + uint8_t track = get_track(lba); + + uint32_t disc_pos = lba; + uint32_t track_pos = lba - get_track_start(track); + + if (msf) + { + disc_pos = lba_to_msf(disc_pos); + track_pos = lba_to_msf(track_pos - 150); + + if (VERBOSE & LOG_SUBQ2) + popmessage("Playing track %d at %02d:%02d.%02d (disc %02d:%02d.%02d)", track + 1, + track_pos >> 16 & 0xff, track_pos >> 8 & 0xff, track_pos & 0xff, + disc_pos >> 16 & 0xff, disc_pos >> 8 & 0xff, disc_pos & 0xff); + } + else + { + if (VERBOSE & LOG_SUBQ2) + popmessage("Playing track %d at %d (disc %d)", track, track_pos, disc_pos); + } + + m_output_fifo[1] = get_adr_control(track); + m_output_fifo[2] = track + 1; + m_output_fifo[3] = 0x01; // TODO: index + + m_output_fifo[4] = disc_pos >> 24; + m_output_fifo[5] = disc_pos >> 16; + m_output_fifo[6] = disc_pos >> 8; + m_output_fifo[7] = disc_pos >> 0; + m_output_fifo[8] = track_pos >> 24; + m_output_fifo[9] = track_pos >> 16; + m_output_fifo[10] = track_pos >> 8; + m_output_fifo[11] = track_pos >> 0; + m_output_fifo[12] = 0; // TODO: upc flag + + if (m_cdda->audio_paused()) + m_output_fifo[0] = AUDIO_STATUS_PAUSED; + else + m_output_fifo[0] = AUDIO_STATUS_PLAY; + } + else if (m_cdda->audio_ended()) + m_output_fifo[0] = AUDIO_STATUS_COMPLETED; + else + m_output_fifo[0] = AUDIO_STATUS_NO_STATUS; + + LOGMASKED(LOG_SUBQ, "-> Audio status = %02x\n", m_output_fifo[0]); + + status_enable(13); +} + +void cr511b_device::cmd_read_disc_info() +{ + LOGMASKED(LOG_CMD, "Command: Read Disc Info\n"); + LOGPARAM; + + uint8_t last_track = get_last_track(); + uint32_t last_lba = get_track_start(0xaa); + uint32_t last_msf = lba_to_msf(last_lba); + + m_output_fifo[0] = 1; // first track + m_output_fifo[1] = last_track; + m_output_fifo[2] = last_msf >> 16; + m_output_fifo[3] = last_msf >> 8; + m_output_fifo[4] = last_msf >> 0; + + status_change(m_status | STATUS_MOTOR | STATUS_SUCCESS); + status_enable(5); +} + +void cr511b_device::cmd_read_toc() +{ + LOGMASKED(LOG_CMD, "Command: Read TOC\n"); + LOGPARAM; + + // 01: bit 1 - msf or lba + // 02: track + // 03: unused? + // 04: unused? + // 05: unused? + // 06: unused? + + bool msf = bool(BIT(m_input_fifo[1], 1)); + uint8_t track = m_input_fifo[2]; + + uint8_t status = m_status; + + status |= STATUS_MOTOR; + + if (track > get_last_track()) + { + LOGMASKED(LOG_CMD, "Invalid track requested: %d\n", track); + + status |= STATUS_ERROR; + status_enable(0); + } + else if (track == 0) + { + uint32_t track_start = get_track_start(0xaa); + + LOGMASKED(LOG_CMD, "Track 0 requested, lead out start %d\n", track_start); + + if (msf) + track_start = lba_to_msf(track_start); + + m_output_fifo[1] = get_adr_control(0xaa); + m_output_fifo[2] = 1; // first track + m_output_fifo[3] = get_last_track(); + m_output_fifo[4] = track_start >> 24; + m_output_fifo[5] = track_start >> 16; + m_output_fifo[6] = track_start >> 8; + m_output_fifo[7] = track_start >> 0; + + status_enable(8); + } + else + { + uint32_t track_start = get_track_start(track - 1); + + LOGMASKED(LOG_CMD, "Track %d requested, start %d\n", track, track_start); + + if (msf) + track_start = lba_to_msf(track_start); + + m_output_fifo[1] = get_adr_control(track - 1); + m_output_fifo[2] = track; + m_output_fifo[3] = 0; + m_output_fifo[4] = track_start >> 24; + m_output_fifo[5] = track_start >> 16; + m_output_fifo[6] = track_start >> 8; + m_output_fifo[7] = track_start >> 0; + + status_enable(8); + } + + status_change(status); +} + +void cr511b_device::cmd_pause() +{ + LOGMASKED(LOG_CMD, "Command: Pause\n"); + LOGPARAM; + + // 01: 00 = pause audio, other values? + // 02: unused? + // 03: unused? + // 04: unused? + // 05: unused? + // 06: unused? + + m_cdda->pause_audio(m_input_fifo[1] == 0x00); + + status_enable(0); +} + +void cr511b_device::cmd_front_panel() +{ + LOGMASKED(LOG_CMD, "Command: Front Panel\n"); + LOGPARAM; + + // enables direct control of the drive by the front panel buttons: stop, play/pause, ff, rew + + // 01: enable/disable + // 02: unused? + // 03: unused? + // 04: unused? + // 05: unused? + // 06: unused? + + status_enable(0); } diff --git a/src/devices/machine/cr511b.h b/src/devices/machine/cr511b.h index 0575562b2d4..e773c8b1f82 100644 --- a/src/devices/machine/cr511b.h +++ b/src/devices/machine/cr511b.h @@ -1,11 +1,11 @@ -// license:BSD-3-Clause -// copyright-holders:Dirk Best +// license: BSD-3-Clause +// copyright-holders: Dirk Best /*************************************************************************** CR-511-B CD-ROM drive CD-ROM drive with a custom MKE/Panasonic interface as used in the - Commodore CDTV and early SoundBlaster cards. + Commodore CDTV. Similar to the interface on early SoundBlaster cards. 1 _RESET 2 GND 3 EFFK 4 SCCK @@ -38,65 +38,122 @@ #include "imagedev/cdromimg.h" #include "sound/cdda.h" -class cr511b_device : public device_t + +class cr511b_device : public cdrom_image_device, public device_mixer_interface { public: - // construction/destruction cr511b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // callbacks - auto stch_handler() { return m_stch_handler.bind(); } - auto sten_handler() { return m_sten_handler.bind(); } - auto drq_handler() { return m_drq_handler.bind(); } - auto dten_handler() { return m_dten_handler.bind(); } - auto scor_handler() { return m_scor_handler.bind(); } - auto xaen_handler() { return m_xaen_handler.bind(); } + auto stch_cb() { return m_stch_cb.bind(); } + auto sten_cb() { return m_sten_cb.bind(); } + auto drq_cb() { return m_drq_cb.bind(); } + auto dten_cb() { return m_dten_cb.bind(); } + auto scor_cb() { return m_scor_cb.bind(); } uint8_t read(); void write(uint8_t data); - void enable_w(int state); void cmd_w(int state); + void enable_w(int state); protected: - // device-level overrides + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; virtual void device_start() override ATTR_COLD; virtual void device_reset() override ATTR_COLD; - virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + // device_image_interface implementation + virtual std::pair call_load() override; + virtual void call_unload() override; private: - enum - { - STATUS_DOOR_CLOSED = 0x80, - STATUS_MEDIA = 0x40, - STATUS_MOTOR = 0x20, - STATUS_ERROR = 0x10, - STATUS_SUCCESS = 0x08, - STATUS_PLAYING = 0x04, - STATUS_DOOR_LOCKED = 0x02, - STATUS_READY = 0x01 - }; + uint32_t lba_to_msf(int32_t lba); + int32_t msf_to_lba(uint32_t msf); + int size_to_track_type(); + + TIMER_CALLBACK_MEMBER(frame_cb); + + TIMER_CALLBACK_MEMBER(stch); + void status_change(uint8_t status); + + TIMER_CALLBACK_MEMBER(sten); + void status_enable(uint8_t output_length); + + void audio_end_cb(int state); + + // commands + void cmd_seek(); + void cmd_read(); + void cmd_motor_on(); + void cmd_motor_off(); + void cmd_play_lba(); + void cmd_play_msf(); + void cmd_play_track(); + void cmd_read_status(); + void cmd_read_error(); + void cmd_version(); + void cmd_set_mode(); + void cmd_read_subq(); + void cmd_read_disc_info(); + void cmd_read_toc(); + void cmd_pause(); + void cmd_front_panel(); + + // drive status + static constexpr uint8_t STATUS_DOOR_CLOSED = 0x80; // unverified, not used + static constexpr uint8_t STATUS_MEDIA = 0x40; + static constexpr uint8_t STATUS_MOTOR = 0x20; + static constexpr uint8_t STATUS_ERROR = 0x10; + static constexpr uint8_t STATUS_SUCCESS = 0x08; // last command has successfully executed + static constexpr uint8_t STATUS_PLAYING = 0x04; + static constexpr uint8_t STATUS_DOOR_LOCKED = 0x02; // unverified, not used + static constexpr uint8_t STATUS_READY = 0x01; + + // audio status + static constexpr uint8_t AUDIO_STATUS_INVALID = 0x00; + static constexpr uint8_t AUDIO_STATUS_PLAY = 0x11; + static constexpr uint8_t AUDIO_STATUS_PAUSED = 0x12; + static constexpr uint8_t AUDIO_STATUS_COMPLETED = 0x13; + static constexpr uint8_t AUDIO_STATUS_ERROR = 0x14; + static constexpr uint8_t AUDIO_STATUS_NO_STATUS = 0x15; - required_device m_cdrom; required_device m_cdda; - devcb_write_line m_stch_handler; - devcb_write_line m_sten_handler; - devcb_write_line m_drq_handler; - devcb_write_line m_dten_handler; - devcb_write_line m_scor_handler; - devcb_write_line m_xaen_handler; + devcb_write_line m_stch_cb; + devcb_write_line m_sten_cb; + devcb_write_line m_drq_cb; + devcb_write_line m_dten_cb; + devcb_write_line m_scor_cb; - //bool m_motor; + emu_timer *m_frame_timer; + emu_timer *m_stch_timer; + emu_timer *m_sten_timer; - // state of lines - int m_enabled; - int m_cmd; + uint8_t m_input_fifo[16]; + uint8_t m_input_fifo_pos; - // data transfer - //uint8_t m_sector_buffer[CD_MAX_SECTOR_DATA]; + uint8_t m_output_fifo[16]; + uint8_t m_output_fifo_pos; + uint8_t m_output_fifo_length; + + uint8_t m_status; + uint16_t m_sector_size; + + uint32_t m_transfer_lba; + uint16_t m_transfer_sectors; + uint32_t m_transfer_length; + uint8_t m_transfer_buffer[2352]; + uint16_t m_transfer_buffer_pos; + + // external lines + bool m_enabled; + bool m_cmd; + + bool m_status_ready; + bool m_data_ready; }; +// device type declaration DECLARE_DEVICE_TYPE(CR511B, cr511b_device) #endif // MAME_MACHINE_CR511B_H diff --git a/src/devices/machine/dmac.cpp b/src/devices/machine/dmac.cpp index 83c51fd215b..c4abb65fb34 100644 --- a/src/devices/machine/dmac.cpp +++ b/src/devices/machine/dmac.cpp @@ -14,7 +14,7 @@ - SCSI - Support newer variant - DAWR - - Data corruption when installing WB31 + - Rev 1: Data corruption when installing WB31 - FIFO? ***************************************************************************/ @@ -47,6 +47,8 @@ amiga_dmac_device::amiga_dmac_device(const machine_config &mconfig, device_type m_css_write_cb(*this), m_csx0_read_cb(*this, 0), m_csx0_write_cb(*this), + m_csx0_a4_read_cb(*this, 0), + m_csx0_a4_write_cb(*this), m_csx1_read_cb(*this, 0), m_csx1_write_cb(*this), m_sdack_read_cb(*this, 0), @@ -99,6 +101,7 @@ void amiga_dmac_device::map(address_map &map) map(0x008e, 0x008f).w(FUNC(amiga_dmac_device::dawr_w)); map(0x0090, 0x0093).rw(FUNC(amiga_dmac_device::css_r), FUNC(amiga_dmac_device::css_w)).umask16(0x00ff); map(0x00a0, 0x00a7).rw(FUNC(amiga_dmac_device::csx0_r), FUNC(amiga_dmac_device::csx0_w)).umask16(0x00ff); + map(0x00b0, 0x00bf).rw(FUNC(amiga_dmac_device::csx0_a4_r), FUNC(amiga_dmac_device::csx0_a4_w)).umask16(0x00ff); // a4 + csx0 map(0x00c0, 0x00c7).rw(FUNC(amiga_dmac_device::csx1_r), FUNC(amiga_dmac_device::csx1_w)).umask16(0x00ff); map(0x00e0, 0x00e1).rw(FUNC(amiga_dmac_device::st_dma_r), FUNC(amiga_dmac_device::st_dma_w)); map(0x00e2, 0x00e3).rw(FUNC(amiga_dmac_device::sp_dma_r), FUNC(amiga_dmac_device::sp_dma_w)); @@ -192,7 +195,7 @@ TIMER_CALLBACK_MEMBER(amiga_dmac_device::update_dma) m_acr++; - if (m_rev1 && (m_cntr & CNTR_TCEN)) + if (m_cntr & CNTR_TCEN) { // we count words if ((m_acr & 1) == 0) @@ -201,6 +204,8 @@ TIMER_CALLBACK_MEMBER(amiga_dmac_device::update_dma) { LOGMASKED(LOG_DMA, "Terminal count\n"); + stop_dma(); + m_istr |= ISTR_E_INT; update_interrupts(); } @@ -254,22 +259,16 @@ void amiga_dmac_device::wtc_hi_w(offs_t offset, uint16_t data, uint16_t mem_mask { LOGMASKED(LOG_REGS, "wtc_hi_w: %04x & %04x\n", data, mem_mask); - if (m_rev1) - { - m_wtc &= (~(uint32_t) mem_mask) << 16 | 0x0000ffff; - m_wtc |= ((uint32_t) data & mem_mask) << 16; - } + m_wtc &= (~(uint32_t) mem_mask) << 16 | 0x0000ffff; + m_wtc |= ((uint32_t) data & mem_mask) << 16; } void amiga_dmac_device::wtc_lo_w(offs_t offset, uint16_t data, uint16_t mem_mask) { LOGMASKED(LOG_REGS, "wtc_lo_w: %04x & %04x\n", data, mem_mask); - if (m_rev1) - { - m_wtc &= 0xffff0000 & (~mem_mask); - m_wtc |= data & mem_mask; - } + m_wtc &= 0xffff0000 & (~mem_mask); + m_wtc |= data & mem_mask; } void amiga_dmac_device::acr_hi_w(offs_t offset, uint16_t data, uint16_t mem_mask) @@ -298,6 +297,8 @@ uint8_t amiga_dmac_device::css_r(offs_t offset) { return m_css_read_cb(offset); void amiga_dmac_device::css_w(offs_t offset, uint8_t data) { m_css_write_cb(offset, data); } uint8_t amiga_dmac_device::csx0_r(offs_t offset) { return m_csx0_read_cb(offset); } void amiga_dmac_device::csx0_w(offs_t offset, uint8_t data) { m_csx0_write_cb(offset, data); } +uint8_t amiga_dmac_device::csx0_a4_r(offs_t offset) { return m_csx0_a4_read_cb(offset); } +void amiga_dmac_device::csx0_a4_w(offs_t offset, uint8_t data) { m_csx0_a4_write_cb(offset, data); } uint8_t amiga_dmac_device::csx1_r(offs_t offset) { return m_csx1_read_cb(offset); } void amiga_dmac_device::csx1_w(offs_t offset, uint8_t data) { m_csx1_write_cb(offset, data); } diff --git a/src/devices/machine/dmac.h b/src/devices/machine/dmac.h index 3faa6e4d1f0..0bd2d1f7caa 100644 --- a/src/devices/machine/dmac.h +++ b/src/devices/machine/dmac.h @@ -26,6 +26,8 @@ public: auto css_write_cb() { return m_css_write_cb.bind(); } auto csx0_read_cb() { return m_csx0_read_cb.bind(); } auto csx0_write_cb() { return m_csx0_write_cb.bind(); } + auto csx0_a4_read_cb() { return m_csx0_a4_read_cb.bind(); } + auto csx0_a4_write_cb() { return m_csx0_a4_write_cb.bind(); } auto csx1_read_cb() { return m_csx1_read_cb.bind(); } auto csx1_write_cb() { return m_csx1_write_cb.bind(); } auto sdack_read_cb() { return m_sdack_read_cb.bind(); } @@ -77,6 +79,8 @@ private: void css_w(offs_t offset, uint8_t data); uint8_t csx0_r(offs_t offset); void csx0_w(offs_t offset, uint8_t data); + uint8_t csx0_a4_r(offs_t offset); + void csx0_a4_w(offs_t offset, uint8_t data); uint8_t csx1_r(offs_t offset); void csx1_w(offs_t offset, uint8_t data); @@ -120,6 +124,8 @@ private: devcb_write8 m_css_write_cb; devcb_read8 m_csx0_read_cb; devcb_write8 m_csx0_write_cb; + devcb_read8 m_csx0_a4_read_cb; + devcb_write8 m_csx0_a4_write_cb; devcb_read8 m_csx1_read_cb; devcb_write8 m_csx1_write_cb; devcb_read8 m_sdack_read_cb; @@ -165,7 +171,7 @@ public: amiga_dmac_rev2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; -// device type definition +// device type declaration DECLARE_DEVICE_TYPE(AMIGA_DMAC_REV1, amiga_dmac_rev1_device) DECLARE_DEVICE_TYPE(AMIGA_DMAC_REV2, amiga_dmac_rev2_device) diff --git a/src/mame/amiga/amiga.cpp b/src/mame/amiga/amiga.cpp index b6e757b9168..ac82186d1bf 100644 --- a/src/mame/amiga/amiga.cpp +++ b/src/mame/amiga/amiga.cpp @@ -354,13 +354,6 @@ public: u16 clock_r(offs_t offset); void clock_w(offs_t offset, u16 data); - uint8_t dmac_scsi_data_read(offs_t offset); - void dmac_scsi_data_write(offs_t offset, uint8_t data); - void dmac_int_w(int state); - - void tpi_port_b_write(uint8_t data); - void tpi_int_w(int state); - void cdtv(machine_config &config); void cdtvn(machine_config &config); void cdtv_mem(address_map &map) ATTR_COLD; @@ -369,21 +362,30 @@ public: protected: // driver_device overrides virtual void machine_start() override ATTR_COLD; + virtual void machine_reset() override ATTR_COLD; // amiga_state overrides virtual bool int2_pending() override; virtual bool int6_pending() override; private: - // devices + void dmac_int_w(int state); + + void tpi_portb_w(uint8_t data); + void tpi_int_w(int state); + + void sten_w(int state); + void drq_w(int state); + required_device m_rtc; - required_device m_dmac; + required_device m_dmac; required_device m_tpi; required_device m_cdrom; // internal state int m_dmac_irq; int m_tpi_irq; + bool m_sten; }; class a3000_state : public amiga_state @@ -643,43 +645,6 @@ void a500p_state::clock_w(offs_t offset, u16 data) } -//************************************************************************** -// CD-ROM CONTROLLER -//************************************************************************** - -uint8_t cdtv_state::dmac_scsi_data_read(offs_t offset) -{ - if (offset >= 0xb0 && offset <= 0xbf) - return m_tpi->read(offset); - - return 0xff; -} - -void cdtv_state::dmac_scsi_data_write(offs_t offset, uint8_t data) -{ - if (offset >= 0xb0 && offset <= 0xbf) - m_tpi->write(offset, data); -} - -void cdtv_state::dmac_int_w(int state) -{ - m_dmac_irq = state; - update_int2(); -} - -void cdtv_state::tpi_port_b_write(uint8_t data) -{ - m_cdrom->cmd_w(BIT(data, 0)); - m_cdrom->enable_w(BIT(data, 1)); -} - -void cdtv_state::tpi_int_w(int state) -{ - m_tpi_irq = state; - update_int2(); -} - - //************************************************************************** // DRIVER INIT //************************************************************************** @@ -949,6 +914,15 @@ void cdtv_state::machine_start() m_dmac->ramsz_w(0); } +void cdtv_state::machine_reset() +{ + amiga_state::machine_reset(); + + // start autoconfig + m_dmac->configin_w(0); + m_dmac->configin_w(1); +} + bool cdtv_state::int2_pending() { return m_cia_0_irq || m_dmac_irq || m_tpi_irq; @@ -959,6 +933,35 @@ bool cdtv_state::int6_pending() return m_cia_1_irq; } +void cdtv_state::dmac_int_w(int state) +{ + m_dmac_irq = state; + update_int2(); +} + +void cdtv_state::tpi_portb_w(uint8_t data) +{ + m_cdrom->enable_w(BIT(data, 1)); + m_cdrom->cmd_w(BIT(data, 0)); +} + +void cdtv_state::tpi_int_w(int state) +{ + m_tpi_irq = state; + update_int2(); +} + +void cdtv_state::sten_w(int state) +{ + m_sten = bool(state); +} + +void cdtv_state::drq_w(int state) +{ + if (m_sten) + m_dmac->xdreq_w(state); +} + u32 a3000_state::scsi_r(offs_t offset, u32 mem_mask) { u32 data = 0xffffffff; @@ -1961,31 +1964,30 @@ void cdtv_state::cdtv(machine_config &config) // 256kb memory card NVRAM(config, "memcard", nvram_device::DEFAULT_ALL_0); - // real-time clock MSM6242(config, m_rtc, XTAL(32'768)); - // cd-rom controller - AMIGA_DMAC_REV1(config, m_dmac, amiga_state::CLK_7M_PAL); - m_dmac->css_read_cb().set(FUNC(cdtv_state::dmac_scsi_data_read)); - m_dmac->css_write_cb().set(FUNC(cdtv_state::dmac_scsi_data_write)); + AMIGA_DMAC_REV2(config, m_dmac, amiga_state::CLK_7M_PAL); + m_dmac->int_cb().set(FUNC(cdtv_state::dmac_int_w)); m_dmac->csx0_read_cb().set(m_cdrom, FUNC(cr511b_device::read)); m_dmac->csx0_write_cb().set(m_cdrom, FUNC(cr511b_device::write)); - m_dmac->int_cb().set(FUNC(cdtv_state::dmac_int_w)); + m_dmac->csx0_a4_read_cb().set(m_tpi, FUNC(tpi6525_device::read)); + m_dmac->csx0_a4_write_cb().set(m_tpi, FUNC(tpi6525_device::write)); + m_dmac->xdack_read_cb().set(m_cdrom, FUNC(cr511b_device::read)); TPI6525(config, m_tpi, 0); m_tpi->out_irq_cb().set(FUNC(cdtv_state::tpi_int_w)); - m_tpi->out_pb_cb().set(FUNC(cdtv_state::tpi_port_b_write)); + m_tpi->out_pb_cb().set(FUNC(cdtv_state::tpi_portb_w)); - // cd-rom CR511B(config, m_cdrom, 0); - m_cdrom->scor_handler().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert(); - m_cdrom->stch_handler().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert(); - m_cdrom->sten_handler().set(m_tpi, FUNC(tpi6525_device::i3_w)); - m_cdrom->xaen_handler().set(m_tpi, FUNC(tpi6525_device::pb2_w)); - m_cdrom->drq_handler().set(m_dmac, FUNC(amiga_dmac_device::xdreq_w)); - m_cdrom->dten_handler().set(m_dmac, FUNC(amiga_dmac_device::xdreq_w)); + m_cdrom->add_route(0, "lspeaker", 1.0); + m_cdrom->add_route(1, "rspeaker", 1.0); + m_cdrom->scor_cb().set(m_tpi, FUNC(tpi6525_device::i1_w)).invert(); + m_cdrom->stch_cb().set(m_tpi, FUNC(tpi6525_device::i2_w)).invert(); + m_cdrom->sten_cb().set(m_tpi, FUNC(tpi6525_device::i3_w)); + m_cdrom->sten_cb().append(FUNC(cdtv_state::sten_w)); + m_cdrom->drq_cb().set(m_tpi, FUNC(tpi6525_device::i4_w)); + m_cdrom->drq_cb().append(FUNC(cdtv_state::drq_w)); - // software SOFTWARE_LIST(config, "cd_list").set_original("cdtv"); }