summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/cr511b.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/cr511b.cpp')
-rw-r--r--src/devices/machine/cr511b.cpp766
1 files changed, 707 insertions, 59 deletions
diff --git a/src/devices/machine/cr511b.cpp b/src/devices/machine/cr511b.cpp
index 6a4309f12c9..7f52ced1e20 100644
--- a/src/devices/machine/cr511b.cpp
+++ b/src/devices/machine/cr511b.cpp
@@ -1,116 +1,764 @@
-// license:GPL-2.0+
-// 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),
+ 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->add_route(0, DEVICE_SELF, 1.0, 0);
+ m_cdda->add_route(1, DEVICE_SELF, 1.0, 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
-//-------------------------------------------------
+void cr511b_device::device_start()
+{
+ cdrom_image_device::device_start();
-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_frame_timer(nullptr),
- //m_motor(false),
- m_enabled(-1),
- m_cmd(-1)
+ 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);
}
-//-------------------------------------------------
-// device_start - device-specific startup
-//-------------------------------------------------
+std::pair<std::error_condition, std::string> cr511b_device::call_load()
+{
+ auto ret = cdrom_image_device::call_load();
+
+ if (!ret.first)
+ status_change(m_status | STATUS_MEDIA);
-void cr511b_device::device_start()
+ return ret;
+}
+
+void cr511b_device::call_unload()
{
- // resolve callbacks
- m_stch_handler.resolve_safe();
- m_sten_handler.resolve_safe();
- m_drq_handler.resolve_safe();
- m_dten_handler.resolve_safe();
- m_scor_handler.resolve_safe();
- m_xaen_handler.resolve_safe();
+ status_change(STATUS_READY);
- m_frame_timer = timer_alloc(0, nullptr);
- m_frame_timer->adjust(attotime::never);
+ cdrom_image_device::call_unload();
}
-//-------------------------------------------------
-// device_reset - device-specific reset
-//-------------------------------------------------
+uint32_t cr511b_device::lba_to_msf(int32_t lba)
+{
+ uint32_t msf = 0;
-void cr511b_device::device_reset()
+ 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;
}
-//-------------------------------------------------
-// device_timer - device-specific timer events
-//-------------------------------------------------
+int cr511b_device::size_to_track_type()
+{
+ switch (m_sector_size)
+ {
+ case 2048: return cdrom_file::CD_TRACK_MODE1;
+ }
-void cr511b_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+ // 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);
+}
-//**************************************************************************
-// IMPLEMENTATION
-//**************************************************************************
+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()
+{
+ 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;
+ }
+
+ 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 = !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();
-READ8_MEMBER( cr511b_device::read )
+ uint8_t status = m_status;
+
+ status &= ~STATUS_PLAYING;
+ status |= STATUS_MOTOR;
+
+ status_change(status);
+ status_enable(0);
+}
+
+void cr511b_device::cmd_motor_on()
{
- return 0xff;
+ LOGMASKED(LOG_CMD, "Command: Motor On\n");
+ LOGPARAM;
+
+ // TODO: Does this enable STATUS_SUCCESS?
+
+ status_change(m_status | STATUS_MOTOR);
+ status_enable(0);
}
-WRITE8_MEMBER ( cr511b_device::write )
+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);
}
-WRITE_LINE_MEMBER( cr511b_device::enable_w )
+void cr511b_device::cmd_play_lba()
{
- m_enabled = state;
+ LOGMASKED(LOG_CMD, "Command: Play LBA\n");
+ LOGPARAM;
+
+ // haven't found anything that uses it yet
+ fatalerror("Play LBA: Not implemented\n");
}
-WRITE_LINE_MEMBER( cr511b_device::cmd_w )
+void cr511b_device::cmd_play_msf()
{
- m_cmd = state;
+ 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);
}