summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/ldv1000hle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/ldv1000hle.cpp')
-rw-r--r--src/devices/machine/ldv1000hle.cpp1046
1 files changed, 1046 insertions, 0 deletions
diff --git a/src/devices/machine/ldv1000hle.cpp b/src/devices/machine/ldv1000hle.cpp
new file mode 100644
index 00000000000..8d67db64186
--- /dev/null
+++ b/src/devices/machine/ldv1000hle.cpp
@@ -0,0 +1,1046 @@
+// license:BSD-3-Clause
+// copyright-holders:Ryan Holtz
+/*************************************************************************
+
+ ldv1000hle.cpp
+
+ Pioneer LDV-1000 laserdisc player simulation.
+
+**************************************************************************
+
+ To do:
+
+ * On-screen display support
+ * Commands that Dragon's Lair doesn't use
+
+*************************************************************************/
+
+
+#include "emu.h"
+#include "ldv1000hle.h"
+
+
+#define LOG_COMMAND_BYTES (1U << 1)
+#define LOG_COMMANDS (1U << 2)
+#define LOG_REPLIES (1U << 3)
+#define LOG_SEARCHES (1U << 4)
+#define LOG_STOPS (1U << 5)
+#define LOG_SQUELCHES (1U << 6)
+#define LOG_FRAMES (1U << 7)
+#define LOG_ALL (LOG_COMMAND_BYTES | LOG_COMMANDS | LOG_REPLIES | LOG_SEARCHES | LOG_STOPS | LOG_SQUELCHES | LOG_FRAMES)
+
+#define VERBOSE (0)
+#include "logmacro.h"
+
+DEFINE_DEVICE_TYPE(PIONEER_LDV1000HLE, pioneer_ldv1000hle_device, "ldv1000hle", "Pioneer LDV-1000 HLE")
+
+
+//-------------------------------------------------
+// pioneer_ldv1000hle_device - constructor
+//-------------------------------------------------
+
+pioneer_ldv1000hle_device::pioneer_ldv1000hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : parallel_laserdisc_device(mconfig, PIONEER_LDV1000HLE, tag, owner, clock)
+ , m_vbi_fetch(nullptr)
+ , m_stop_timer(nullptr)
+ , m_park_strobe_timer(nullptr)
+ , m_assert_status_strobe_timer(nullptr)
+ , m_deassert_status_strobe_timer(nullptr)
+ , m_assert_command_strobe_timer(nullptr)
+ , m_deassert_command_strobe_timer(nullptr)
+ , m_cmd_length(0)
+ , m_status(STATUS_PARK | STATUS_READY)
+ , m_pre_stop_status(0)
+ , m_mode(MODE_PARK)
+ , m_curr_frame(0)
+ , m_search_frame(~0U)
+ , m_stop_frame(~0U)
+ , m_cmd_number_length(0)
+ , m_curr_register(0)
+ , m_scan_speed(60)
+ , m_scan_speed_accum(0)
+ , m_play_speed(0.0)
+ , m_status_strobe(true)
+ , m_command_strobe(true)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device initialization
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::device_start()
+{
+ // pass through to the parent
+ parallel_laserdisc_device::device_start();
+
+ // allocate timers
+ m_vbi_fetch = timer_alloc(FUNC(pioneer_ldv1000hle_device::process_vbi_data), this);
+ m_stop_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::resume_from_stop), this);
+ m_park_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::park_strobe_tick), this);
+ m_assert_status_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::assert_status_strobe), this);
+ m_deassert_status_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::deassert_status_strobe), this);
+ m_assert_command_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::assert_command_strobe), this);
+ m_deassert_command_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::deassert_command_strobe), this);
+
+ // register state saving
+ save_item(NAME(m_cmd_buffer));
+ save_item(NAME(m_cmd_length));
+ save_item(NAME(m_status));
+ save_item(NAME(m_pre_stop_status));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_curr_frame));
+ save_item(NAME(m_search_frame));
+ save_item(NAME(m_stop_frame));
+ save_item(NAME(m_cmd_number));
+ save_item(NAME(m_cmd_number_length));
+ save_item(NAME(m_user_ram));
+ save_item(NAME(m_curr_register));
+ save_item(NAME(m_scan_speed));
+ save_item(NAME(m_scan_speed_accum));
+ save_item(NAME(m_play_speed));
+ save_item(NAME(m_audio_enable));
+ save_item(NAME(m_status_strobe));
+ save_item(NAME(m_command_strobe));
+}
+
+
+//-------------------------------------------------
+// device_reset - device reset
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::device_reset()
+{
+ // pass through to the parent
+ parallel_laserdisc_device::device_reset();
+
+ // reset our state
+ m_vbi_fetch->adjust(attotime::never);
+
+ std::fill_n(m_cmd_buffer, std::size(m_cmd_buffer), 0);
+ m_cmd_length = 0;
+ m_status = STATUS_PARK | STATUS_READY;
+ m_pre_stop_status = 0;
+ m_mode = MODE_PARK;
+ m_curr_frame = 0;
+ m_search_frame = ~0U;
+ m_stop_frame = ~0U;
+ m_cmd_number_length = 0;
+ m_curr_register = 0;
+ m_scan_speed = 0;
+ m_scan_speed_accum = 0;
+ m_audio_enable[0] = true;
+ m_audio_enable[1] = true;
+ m_status_strobe = true;
+ m_command_strobe = true;
+
+ set_video_squelch(false);
+
+ m_park_strobe_timer->adjust(attotime::from_msec(21), 0, attotime::from_msec(21));
+}
+
+
+//-------------------------------------------------
+// data_w - handle a new data byte
+// received over the parallel link
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::data_w(u8 data)
+{
+ LOGMASKED(LOG_COMMAND_BYTES, "data_w: Command byte added: %02x\n", data);
+ if (m_cmd_length < std::size(m_cmd_buffer))
+ {
+ m_cmd_buffer[m_cmd_length] = data;
+ m_cmd_length++;
+ }
+
+ if (is_command_byte(data))
+ {
+ LOGMASKED(LOG_COMMAND_BYTES, "data_w: %02x is a command byte, processing command buffer\n", data);
+ process_command_buffer();
+ }
+ else
+ {
+ LOGMASKED(LOG_COMMAND_BYTES, "data_w: %02x is not a command byte, leaving in the queue for now\n", data);
+ }
+}
+
+
+//-------------------------------------------------
+// data_r - returns the current status byte or
+// reply data when necessary
+//-------------------------------------------------
+
+u8 pioneer_ldv1000hle_device::data_r()
+{
+ u8 data = m_status;
+ LOGMASKED(LOG_REPLIES, "Sending reply %02x\n", data);
+ return data;
+}
+
+
+//-------------------------------------------------
+// enter_w - used to request service by a host
+// application, not currently implemented
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::enter_w(int state)
+{
+}
+
+
+//-------------------------------------------------
+// player_vsync - VSYNC callback, called at the
+// start of the blanking period
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::player_vsync(const vbi_metadata &vbi, int fieldnum, const attotime &curtime)
+{
+ // set a timer to fetch the VBI data when it is ready
+ if (m_mode != MODE_PARK)
+ {
+ m_vbi_fetch->adjust(screen().time_until_pos(19*2), fieldnum);
+ }
+}
+
+
+//-------------------------------------------------
+// player_update - update callback, called on
+// the first visible line of the frame
+//-------------------------------------------------
+
+s32 pioneer_ldv1000hle_device::player_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime)
+{
+ if (m_mode != MODE_PARK)
+ {
+ m_assert_status_strobe_timer->adjust(attotime::from_usec(500));
+ }
+
+ if (!fieldnum)
+ return 0;
+
+ if (m_mode == MODE_SCAN_FORWARD || m_mode == MODE_SCAN_REVERSE)
+ {
+ m_scan_speed_accum += m_scan_speed;
+ int elapsed_tracks = m_scan_speed_accum / 60;
+ m_scan_speed_accum -= elapsed_tracks * 60;
+ if (m_mode == MODE_SCAN_REVERSE)
+ elapsed_tracks *= -1;
+
+ if (m_stop_frame != ~0U)
+ {
+ const int next_frame = (int)m_curr_frame + elapsed_tracks;
+ if (next_frame >= m_stop_frame)
+ {
+ // If we've landed on (or exceeded) our desired frame and are in a SKIP FORWARD command, resume from the desired frame.
+ elapsed_tracks = (int)m_stop_frame - (int)m_curr_frame;
+ m_stop_frame = ~0U;
+ set_playing(STATUS_FORWARD, m_play_speed);
+ }
+ }
+ return elapsed_tracks;
+ }
+
+ if (m_mode == MODE_PLAY || m_mode == MODE_SEARCH)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// process_vbi_data - process VBI data and
+// act on search/play seeking
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::process_vbi_data)
+{
+ if (param == 0)
+ {
+ update_video_enable();
+ update_audio_enable();
+ }
+
+ uint32_t line = get_field_code(LASERDISC_CODE_LINE1718, false);
+ if ((line & 0xf00000) == 0xf00000 || line == VBI_CODE_LEADIN || line == VBI_CODE_LEADOUT)
+ {
+ uint32_t old_frame = m_curr_frame;
+ if (line == VBI_CODE_LEADIN)
+ m_curr_frame = 0;
+ else if (line == VBI_CODE_LEADOUT)
+ m_curr_frame = 54000;
+ else
+ m_curr_frame = bcd_to_literal(line & 0x7ffff);
+
+ LOGMASKED(LOG_FRAMES, "Current frame is %d (VBI 16: %06x, VBI 17: %06x, VBI 18: %06x, VBI 1718: %06x\n", m_curr_frame,
+ get_field_code(LASERDISC_CODE_LINE16, false),
+ get_field_code(LASERDISC_CODE_LINE17, false),
+ get_field_code(LASERDISC_CODE_LINE18, false),
+ line);
+
+ if (m_mode != MODE_STOP)
+ {
+ if (m_stop_frame != ~0U && m_search_frame == ~0U)
+ {
+ s32 old_delta = (s32)m_stop_frame - (s32)old_frame;
+ s32 curr_delta = (s32)m_stop_frame - (s32)m_curr_frame;
+ LOGMASKED(LOG_STOPS, "%s: Stop frame is currently %d, old frame is %d, current frame is %d, old delta %d, curr delta %d\n", machine().describe_context(), m_stop_frame, old_frame, m_curr_frame, old_delta, curr_delta);
+ if (curr_delta == 0 || (old_delta < 0) != (curr_delta < 0))
+ {
+ m_stop_frame = ~0U;
+ LOGMASKED(LOG_STOPS, "%s: Stop frame: Zero delta, entering stop mode\n", machine().describe_context());
+ set_stopped((m_status & STATUS_READY) | STATUS_STOP);
+ }
+ }
+
+ if (m_search_frame != ~0U)
+ {
+ s32 delta = (s32)m_search_frame - (s32)m_curr_frame;
+ LOGMASKED(LOG_SEARCHES, "%s: Searching from current frame %d with delta %d\n", machine().describe_context(), m_curr_frame, delta);
+ if (delta == 0)
+ {
+ // We've found our frame, enter play, pause or still mode.
+ m_search_frame = ~0U;
+ LOGMASKED(LOG_SEARCHES, "%s: Search Mark: Zero delta, entering still mode\n", machine().describe_context());
+ set_stopped(STATUS_SEARCH_FINISH);
+ }
+ else if (delta <= 2 && delta > 0)
+ {
+ LOGMASKED(LOG_SEARCHES, "%s: Positive near delta, letting disc run to current\n", machine().describe_context());
+ // We're approaching our frame, let it run up.
+ }
+ else
+ {
+ if (delta < 0)
+ {
+ advance_slider(std::min(-2, delta / 2));
+ }
+ else
+ {
+ advance_slider(std::max(1, delta / 2));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// resume_from_stop - resume from a previous
+// STOP command
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::resume_from_stop)
+{
+ set_playing(m_pre_stop_status, m_play_speed);
+}
+
+
+//-------------------------------------------------
+// park_strobe_tick - used to trigger periodic
+// status strobes when the player is parked
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::park_strobe_tick)
+{
+ m_status_strobe = false;
+ m_deassert_status_strobe_timer->adjust(attotime::from_usec(26));
+ m_assert_command_strobe_timer->adjust(attotime::from_usec(54));
+}
+
+
+//-------------------------------------------------
+// assert_status_strobe - assert the status
+// strobe signal to a host application at the
+// appropriate time, and prepare for command
+// strobe
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::assert_status_strobe)
+{
+ m_status_strobe = false;
+ m_deassert_status_strobe_timer->adjust(attotime::from_usec(26));
+ m_assert_command_strobe_timer->adjust(attotime::from_usec(54));
+}
+
+
+//-------------------------------------------------
+// deassert_status_strobe
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::deassert_status_strobe)
+{
+ m_status_strobe = true;
+}
+
+
+//-------------------------------------------------
+// assert_command_strobe
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::assert_command_strobe)
+{
+ m_command_strobe = false;
+ m_deassert_command_strobe_timer->adjust(attotime::from_usec(25));
+}
+
+
+//-------------------------------------------------
+// deassert_command_strobe
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::deassert_command_strobe)
+{
+ m_command_strobe = true;
+}
+
+
+//-------------------------------------------------
+// process_command_buffer - process a command
+// line sent from the host
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::process_command_buffer()
+{
+ size_t cmd_index = 0;
+ while (cmd_index < m_cmd_length)
+ {
+ process_command(cmd_index);
+ cmd_index++;
+ }
+
+ m_cmd_length = 0;
+}
+
+
+//-------------------------------------------------
+// is_command_byte - returns whether or not a
+// given byte is a recognized command
+//-------------------------------------------------
+
+bool pioneer_ldv1000hle_device::is_command_byte(const u8 data)
+{
+ switch (data)
+ {
+ case CMD_CLEAR:
+ case CMD_0:
+ case CMD_1:
+ case CMD_2:
+ case CMD_3:
+ case CMD_4:
+ case CMD_5:
+ case CMD_6:
+ case CMD_7:
+ case CMD_8:
+ case CMD_9:
+ case CMD_STORE:
+ case CMD_RECALL:
+ case CMD_DISPLAY:
+ case CMD_AUDIO1:
+ case CMD_AUDIO2:
+ case CMD_PLAY:
+ case CMD_STOP:
+ case CMD_AUTOSTOP:
+ case CMD_SEARCH:
+ case CMD_SCAN_FWD:
+ case CMD_SCAN_REV:
+ case CMD_STEP_FWD:
+ case CMD_STEP_REV:
+ case CMD_REJECT:
+ case CMD_NO_ENTRY:
+ case CMD_LOAD:
+ case CMD_DISPLAY_DISABLE:
+ case CMD_DISPLAY_ENABLE:
+ case CMD_GET_FRAME_NUM:
+ case CMD_GET_2ND_DISPLAY:
+ case CMD_GET_1ST_DISPLAY:
+ case CMD_TRANSFER_MEMORY:
+ case CMD_FWD_X0:
+ case CMD_FWD_X1_4:
+ case CMD_FWD_X1_2:
+ case CMD_FWD_X1:
+ case CMD_FWD_X2:
+ case CMD_FWD_X3:
+ case CMD_FWD_X4:
+ case CMD_FWD_X5:
+ case CMD_SKIP_FWD_10:
+ case CMD_SKIP_FWD_20:
+ case CMD_SKIP_FWD_30:
+ case CMD_SKIP_FWD_40:
+ case CMD_SKIP_FWD_50:
+ case CMD_SKIP_FWD_60:
+ case CMD_SKIP_FWD_70:
+ case CMD_SKIP_FWD_80:
+ case CMD_SKIP_FWD_90:
+ case CMD_SKIP_FWD_100:
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// is_command_number - returns whether or not a
+// given byte is a number-command
+//-------------------------------------------------
+
+bool pioneer_ldv1000hle_device::is_command_number(const u8 data)
+{
+ switch (data)
+ {
+ case CMD_0:
+ case CMD_1:
+ case CMD_2:
+ case CMD_3:
+ case CMD_4:
+ case CMD_5:
+ case CMD_6:
+ case CMD_7:
+ case CMD_8:
+ case CMD_9:
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// process_command - processes a single command
+// from the command buffer
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::process_command(size_t cmd_index)
+{
+ const u8 command = m_cmd_buffer[cmd_index];
+ LOGMASKED(LOG_COMMAND_BYTES, "process_command: Command byte %02x with status %02x\n", command, m_status);
+ if (BIT(m_status, 7))
+ {
+ m_status &= ~STATUS_READY;
+
+ switch (command)
+ {
+ case CMD_CLEAR:
+ LOGMASKED(LOG_COMMANDS, "process_command: Clear\n");
+ break;
+
+ case CMD_0:
+ case CMD_1:
+ case CMD_2:
+ case CMD_3:
+ case CMD_4:
+ case CMD_5:
+ case CMD_6:
+ case CMD_7:
+ case CMD_8:
+ case CMD_9:
+ m_cmd_number[m_cmd_number_length] = cmd_to_number(command);
+ LOGMASKED(LOG_COMMANDS, "process_command: %d (number length now %d)\n", m_cmd_number[m_cmd_number_length], m_cmd_number_length);
+ m_cmd_number_length++;
+ break;
+
+ case CMD_STORE:
+ if (m_cmd_number_length == 0)
+ {
+ LOGMASKED(LOG_COMMANDS, "process_command: Store (no value specified, storing current frame in current register)\n");
+ m_user_ram[m_curr_register] = literal_to_bcd(m_curr_frame);
+ }
+ else
+ {
+ m_user_ram[m_curr_register] = cmd_number_to_bcd();
+ LOGMASKED(LOG_COMMANDS, "process_command: Store %05d\n", m_user_ram[m_curr_register]);
+ }
+ break;
+
+ case CMD_RECALL:
+ LOGMASKED(LOG_COMMANDS, "process_command: Recall (not yet implemented)\n");
+ break;
+
+ case CMD_DISPLAY:
+ LOGMASKED(LOG_COMMANDS, "process_command: Display %d (not yet implemented)\n", cmd_number_to_bcd());
+ break;
+
+ case CMD_AUDIO1:
+ if (m_cmd_number_length == 0)
+ {
+ m_audio_enable[0] = !m_audio_enable[0];
+ LOGMASKED(LOG_COMMANDS, "process_command: Audio1, toggling audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]);
+ }
+ else
+ {
+ const u32 cmd_number = cmd_number_to_bcd();
+ m_audio_enable[0] = BIT(cmd_number, 0);
+ LOGMASKED(LOG_COMMANDS, "process_command: Audio1, setting audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]);
+ }
+ break;
+
+ case CMD_AUDIO2:
+ if (m_cmd_number_length == 0)
+ {
+ m_audio_enable[1] = !m_audio_enable[1];
+ LOGMASKED(LOG_COMMANDS, "process_command: Audio2, toggling audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]);
+ }
+ else
+ {
+ const u32 cmd_number = cmd_number_to_bcd();
+ m_audio_enable[1] = BIT(cmd_number, 0);
+ LOGMASKED(LOG_COMMANDS, "process_command: Audio2, setting audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]);
+ }
+ break;
+
+ case CMD_PLAY:
+ LOGMASKED(LOG_COMMANDS, "process_command: Play\n");
+ cmd_play();
+ break;
+
+ case CMD_STOP:
+ cmd_stop();
+ break;
+
+ case CMD_AUTOSTOP:
+ {
+ const u32 value = bcd_to_literal(cmd_number_to_bcd());
+ LOGMASKED(LOG_COMMANDS, "process_command: Autostop (at frame %d)\n", m_stop_frame);
+ if (value < m_curr_frame)
+ {
+ // Autostop with a frame number less than the current one will cause a search.
+ LOGMASKED(LOG_COMMANDS, "process_command: Requested autostop frame is less than current frame (%d), performing a search instead.\n", m_curr_frame);
+ cmd_search(value);
+ break;
+ }
+ m_status = STATUS_AUTOSTOP;
+ break;
+ }
+
+ case CMD_SEARCH:
+ {
+ const u32 value = bcd_to_literal(cmd_number_to_bcd());
+ LOGMASKED(LOG_COMMANDS, "process_command: Search (to frame %d)\n", value);
+ cmd_search(value);
+ break;
+ }
+
+ case CMD_SCAN_FWD:
+ case CMD_SCAN_REV:
+ LOGMASKED(LOG_COMMANDS, "process_command: Scan %s\n", command == CMD_SCAN_FWD ? "Forward" : "Reverse");
+ set_mode(command == CMD_SCAN_FWD ? MODE_SCAN_FORWARD : MODE_SCAN_REVERSE);
+ m_status = STATUS_SCAN;
+ m_scan_speed = 4000; // "The two SCAN commands move the player's optical head at the rate of approximately 2000 frames per second in the direction specified"
+ m_scan_speed_accum = 0;
+ m_stop_frame = ~0U;
+ m_search_frame = ~0U;
+ break;
+
+ case CMD_STEP_FWD:
+ LOGMASKED(LOG_COMMANDS, "process_command: Step Forward\n");
+ cmd_step(1);
+ break;
+
+ case CMD_STEP_REV:
+ LOGMASKED(LOG_COMMANDS, "process_command: Step Reverse\n");
+ cmd_step(-1);
+ break;
+
+ case CMD_REJECT:
+ LOGMASKED(LOG_COMMANDS, "process_command: Reject\n");
+ set_mode(MODE_PARK);
+ m_status = STATUS_PARK;
+ m_stop_frame = ~0U;
+ m_search_frame = ~0U;
+ break;
+
+ case CMD_NO_ENTRY:
+ LOGMASKED(LOG_COMMANDS, "process_command: No Entry\n");
+ m_status |= STATUS_READY;
+ break;
+
+ case CMD_LOAD:
+ LOGMASKED(LOG_COMMANDS, "process_command: Load (not yet implemented)\n");
+ break;
+
+ case CMD_DISPLAY_DISABLE:
+ LOGMASKED(LOG_COMMANDS, "process_command: Display Disable (not yet implemented)\n");
+ break;
+
+ case CMD_DISPLAY_ENABLE:
+ LOGMASKED(LOG_COMMANDS, "process_command: Display Enable (not yet implemented)\n");
+ break;
+
+ case CMD_GET_FRAME_NUM:
+ LOGMASKED(LOG_COMMANDS, "process_command: Get Frame Number (not yet implemented)\n");
+ break;
+
+ case CMD_GET_2ND_DISPLAY:
+ LOGMASKED(LOG_COMMANDS, "process_command: Get 2nd Display (not yet implemented)\n");
+ break;
+
+ case CMD_GET_1ST_DISPLAY:
+ LOGMASKED(LOG_COMMANDS, "process_command: Get 1st Display (not yet implemented)\n");
+ break;
+
+ case CMD_TRANSFER_MEMORY:
+ LOGMASKED(LOG_COMMANDS, "process_command: Transfer Memory (not yet implemented)\n");
+ break;
+
+ case CMD_FWD_X0:
+ cmd_play_forward(0.0);
+ break;
+
+ case CMD_FWD_X1_4:
+ cmd_play_forward(0.25);
+ break;
+
+ case CMD_FWD_X1_2:
+ cmd_play_forward(0.5);
+ break;
+
+ case CMD_FWD_X1:
+ cmd_play_forward(1.0);
+ break;
+
+ case CMD_FWD_X2:
+ cmd_play_forward(2.0);
+ break;
+
+ case CMD_FWD_X3:
+ cmd_play_forward(3.0);
+ break;
+
+ case CMD_FWD_X4:
+ cmd_play_forward(4.0);
+ break;
+
+ case CMD_FWD_X5:
+ cmd_play_forward(5.0);
+ break;
+
+ case CMD_SKIP_FWD_10:
+ cmd_skip_forward(10);
+ break;
+
+ case CMD_SKIP_FWD_20:
+ cmd_skip_forward(20);
+ break;
+
+ case CMD_SKIP_FWD_30:
+ cmd_skip_forward(30);
+ break;
+
+ case CMD_SKIP_FWD_40:
+ cmd_skip_forward(40);
+ break;
+
+ case CMD_SKIP_FWD_50:
+ cmd_skip_forward(50);
+ break;
+
+ case CMD_SKIP_FWD_60:
+ cmd_skip_forward(60);
+ break;
+
+ case CMD_SKIP_FWD_70:
+ cmd_skip_forward(70);
+ break;
+
+ case CMD_SKIP_FWD_80:
+ cmd_skip_forward(80);
+ break;
+
+ case CMD_SKIP_FWD_90:
+ cmd_skip_forward(90);
+ break;
+
+ case CMD_SKIP_FWD_100:
+ cmd_skip_forward(100);
+ break;
+ }
+
+ if (!is_command_number(command))
+ {
+ m_cmd_number_length = 0;
+ }
+
+ LOGMASKED(LOG_COMMAND_BYTES, "process_command: Status is now %02x\n", m_status);
+ }
+ else if (command == CMD_NO_ENTRY)
+ {
+ m_status |= STATUS_READY;
+ LOGMASKED(LOG_COMMAND_BYTES, "process_command: Received no-entry command while busy, setting ready, status is now %02x\n", m_status);
+ }
+ else
+ {
+ m_status &= ~STATUS_READY;
+ LOGMASKED(LOG_COMMAND_BYTES, "process_command: Received command %02x while busy, clearing ready, status is now %02x\n", command, m_status);
+ }
+}
+
+
+//-------------------------------------------------
+// bcd_to_literal - converts a BCD value used in
+// commands a direct numeric value
+//-------------------------------------------------
+
+u32 pioneer_ldv1000hle_device::bcd_to_literal(u32 bcd)
+{
+ u32 value = 0;
+ u32 shift = 28;
+ u32 multiplier = 10000000;
+ for (u32 i = 0; i < 8; i++)
+ {
+ u32 digit = (bcd >> shift) & 0xf;
+ bcd &= ~(0xf << shift);
+
+ value += digit * multiplier;
+
+ multiplier /= 10;
+ shift -= 4;
+ }
+ return value;
+}
+
+
+//-------------------------------------------------
+// literal_to_bcd - converts a literal value
+// into a BCD representation for commands
+//-------------------------------------------------
+
+u32 pioneer_ldv1000hle_device::literal_to_bcd(u32 value)
+{
+ LOGMASKED(LOG_COMMANDS, "literal_to_bcd: Converting %08x to BCD\n", value);
+ u32 bcd_value = 0;
+ u32 shift = 28;
+ u32 multiplier = 10000000;
+ for (u32 i = 0; i < 8; i++)
+ {
+ u32 digit = value / multiplier;
+ bcd_value |= digit << shift;
+
+ value -= digit * multiplier;
+ multiplier /= 10;
+ shift -= 4;
+ }
+ LOGMASKED(LOG_COMMANDS, "literal_to_bcd: Result: %08x\n", bcd_value);
+ return bcd_value;
+}
+
+
+//-------------------------------------------------
+// literal_to_bcd - converts a literal value
+// into a BCD representation for commands
+//-------------------------------------------------
+
+u32 pioneer_ldv1000hle_device::cmd_number_to_bcd()
+{
+ if (m_cmd_number_length == 0)
+ {
+ LOGMASKED(LOG_COMMANDS, "cmd_number_to_bcd: No command number length, returning 0\n");
+ return 0;
+ }
+ u32 bcd_value = 0;
+ size_t shift = 0;
+ for (int i = (int)m_cmd_number_length - 1; i >= 0; i--)
+ {
+ bcd_value |= m_cmd_number[i] << shift;
+ shift += 4;
+ }
+ LOGMASKED(LOG_COMMANDS, "cmd_number_to_bcd: %05x\n", bcd_value);
+ return bcd_value;
+}
+
+
+//-------------------------------------------------
+// set_mode - set the current high-level player
+// mode, updating any enables as necessary
+//-------------------------------------------------
+
+u8 pioneer_ldv1000hle_device::cmd_to_number(const u8 cmd)
+{
+ switch (cmd)
+ {
+ case CMD_0: return 0;
+ case CMD_1: return 1;
+ case CMD_2: return 2;
+ case CMD_3: return 3;
+ case CMD_4: return 4;
+ case CMD_5: return 5;
+ case CMD_6: return 6;
+ case CMD_7: return 7;
+ case CMD_8: return 8;
+ case CMD_9: return 9;
+ }
+ return 0;
+}
+
+
+//-------------------------------------------------
+// set_mode - set the current high-level player
+// mode, updating any enables as necessary
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::set_mode(const u8 mode)
+{
+ if (m_mode == mode)
+ {
+ return;
+ }
+
+ m_mode = mode;
+ if (mode != MODE_PARK)
+ {
+ m_park_strobe_timer->adjust(attotime::never);
+ }
+ else
+ {
+ m_park_strobe_timer->adjust(attotime::from_msec(21), 0, attotime::from_msec(21));
+ }
+}
+
+
+//-------------------------------------------------
+// set_playing - general-purpose function for
+// setting a forward play mode
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::set_playing(const u8 new_status, const double fields_per_vsync)
+{
+ set_mode(MODE_PLAY);
+ m_status = new_status;
+ m_play_speed = fields_per_vsync;
+ m_search_frame = ~0U;
+ m_stop_frame = ~0U;
+}
+
+
+//-------------------------------------------------
+// set_playing - general-purpose function for
+// setting a forward play mode
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::set_stopped(const u8 new_status)
+{
+ set_mode(MODE_STOP);
+ m_status = new_status;
+}
+
+
+//-------------------------------------------------
+// cmd_play - play forward at 1x speed
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_play()
+{
+ LOGMASKED(LOG_COMMANDS, "process_command: Play\n");
+ set_slider_speed(0);
+ set_playing(STATUS_PLAY, 1.0);
+}
+
+
+//-------------------------------------------------
+// cmd_stop - stop/pause/freeze-frame
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_stop()
+{
+ if (m_cmd_number_length > 0)
+ {
+ const u32 command_value = cmd_number_to_bcd();
+ m_stop_timer->adjust(attotime::from_msec(100 * command_value));
+ m_pre_stop_status = m_status;
+ LOGMASKED(LOG_COMMANDS, "process_command: Stop (for %d.%d seconds)\n", command_value / 10, command_value % 10);
+ }
+ else
+ {
+ LOGMASKED(LOG_COMMANDS, "process_command: Stop\n");
+ }
+ set_stopped(STATUS_STOP);
+}
+
+
+//-------------------------------------------------
+// cmd_step - step one frame forward or backward
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_step(const int direction)
+{
+ advance_slider(direction);
+ set_stopped(STATUS_STOP);
+ m_stop_frame = ~0U;
+ m_search_frame = ~0U;
+}
+
+
+//-------------------------------------------------
+// cmd_play_forward - play forward at a specific
+// multiplier
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_play_forward(const double fields_per_vsync)
+{
+ LOGMASKED(LOG_COMMANDS, "process_command: Forward x%f\n", fields_per_vsync);
+ set_slider_speed(fields_per_vsync);
+ set_playing(STATUS_FORWARD, fields_per_vsync);
+}
+
+
+//-------------------------------------------------
+// cmd_search - begin a search operation to a
+// specific frame
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_search(const u32 frame)
+{
+ set_mode(MODE_SEARCH);
+ m_status = STATUS_SEARCH;
+ m_search_frame = frame;
+ m_stop_frame = ~0U;
+}
+
+
+//-------------------------------------------------
+// cmd_skip_forward - skip forward a specific
+// number of frames while in forward-play mode
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::cmd_skip_forward(const s32 amount)
+{
+ LOGMASKED(LOG_COMMANDS, "Command: Skip Forward %d\n", amount);
+ set_mode(MODE_SEARCH);
+ m_search_frame = ~0U;
+ m_stop_frame = m_curr_frame + amount;
+ m_scan_speed = amount;
+}
+
+
+//-------------------------------------------------
+// update_video_enable - set video enable state
+// on the base device based on our video switch
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::update_video_enable()
+{
+ LOGMASKED(LOG_SQUELCHES, "%s: Updating video enable (mode %d), video %s enabled\n", machine().describe_context(), m_mode, m_mode != MODE_PARK ? "is" : "is not");
+ video_enable(m_mode == MODE_PLAY || m_mode == MODE_STOP);
+}
+
+
+//-------------------------------------------------
+// update_audio_enable - set audio enable state
+// depending on our current mode or channel mute
+//-------------------------------------------------
+
+void pioneer_ldv1000hle_device::update_audio_enable()
+{
+ if (m_mode == MODE_PLAY && m_play_speed == 1.0)
+ {
+ LOGMASKED(LOG_SQUELCHES, "%s: Updating audio enable (playing at 1x, channels %d/%d)\n", machine().describe_context(), m_audio_enable[0], m_audio_enable[1]);
+ set_audio_squelch(!m_audio_enable[0], !m_audio_enable[1]);
+ }
+ else
+ {
+ LOGMASKED(LOG_SQUELCHES, "%s: Updating audio enable (muted, mode %d, speed %f)\n", machine().describe_context(), m_mode, m_play_speed);
+ set_audio_squelch(true, true);
+ }
+}