summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/ldp1450hle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/ldp1450hle.cpp')
-rw-r--r--src/devices/machine/ldp1450hle.cpp944
1 files changed, 944 insertions, 0 deletions
diff --git a/src/devices/machine/ldp1450hle.cpp b/src/devices/machine/ldp1450hle.cpp
new file mode 100644
index 00000000000..ad48d4379c0
--- /dev/null
+++ b/src/devices/machine/ldp1450hle.cpp
@@ -0,0 +1,944 @@
+// license:BSD-3-Clause
+
+/*************************************************************************
+
+ ldp1450hle.cpp
+
+ HLE for the LDP 1000 series, such as LDP-1450
+ NTSC for now, PAL (P suffix) would set base speed to 50, at the least
+
+
+**************************************************************************
+
+ To do:
+
+ * On-screen display support (we just popmessage the data)
+ * Better CLV support
+ * Chapter-search support
+ * Repeat support (we only store the command)
+ * Commands that Time Traveler doesn't use:
+ - Scan Forward/Reverse
+ - Multitrack-Jump Forward/Reverse
+ - Clear
+ - Leadout Symbol
+ - OSD
+ - Status Requests
+ - UI text display (Nova games, DL2 need this)
+ * Repeat behaviour for reverse play should restart in reverse
+ * Delay timing of queue is a guess based on LDP1000A guide
+ * Not all features are fully hooked up
+ * Still step back and forth in Time Traveler glitches
+ - (level select doesn't stay in place)
+*************************************************************************/
+
+#include "emu.h"
+#include "ldp1450hle.h"
+
+
+#define LOG_COMMAND_BYTES (1U << 1)
+#define LOG_COMMANDS (1U << 2)
+#define LOG_COMMAND_BUFFERS (1U << 3)
+#define LOG_REPLIES (1U << 4)
+#define LOG_REPLY_BYTES (1U << 5)
+#define LOG_SEARCHES (1U << 6)
+#define LOG_STOPS (1U << 7)
+#define LOG_SQUELCHES (1U << 8)
+#define LOG_FRAMES (1U << 9)
+#define LOG_ALL (LOG_COMMAND_BYTES | LOG_COMMANDS | LOG_COMMAND_BUFFERS | LOG_REPLY_BYTES | LOG_SEARCHES | LOG_STOPS | LOG_SQUELCHES | LOG_FRAMES)
+
+
+#define VERBOSE 0
+
+#include "logmacro.h"
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+DEFINE_DEVICE_TYPE(SONY_LDP1450HLE, sony_ldp1450hle_device, "ldp1450hle", "Sony LDP-1450 HLE")
+
+
+//-------------------------------------------------
+// sony_ldp1450hle_device - constructor
+//-------------------------------------------------
+
+sony_ldp1450hle_device::sony_ldp1450hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : laserdisc_device(mconfig, SONY_LDP1450HLE, tag, owner, clock)
+ , device_serial_interface(mconfig, *this)
+ , m_serial_tx(*this)
+ , m_vbi_fetch(nullptr)
+ , m_cmd_running(false)
+ , m_reply_write_index(0)
+ , m_reply_read_index(0)
+ , m_mode(MODE_PARK)
+ , m_chapter(0)
+ , m_time(0)
+ , m_search_chapter(~u32(0))
+ , m_search_frame(~u32(0))
+ , m_mark_chapter(~u32(0))
+ , m_mark_frame(~u32(0))
+ , m_video_switch(1)
+ , m_ch1_switch(false)
+ , m_ch2_switch(false)
+ , m_display_switch(0)
+ , m_address_flag(ADDRESS_FRAME)
+ , m_base_speed(60)
+ , m_speed(60)
+ , m_speed_accum(0)
+ , m_curr_frame(0)
+{
+}
+
+
+void sony_ldp1450hle_device::queue_reply(u8 reply, float delay)
+{
+ const u8 reply_buffer[5] = {reply, 0, 0, 0, 0};
+ queue_reply_buffer(reply_buffer, delay);
+}
+
+void sony_ldp1450hle_device::queue_reply_buffer(const u8 reply[], float delay)
+{
+ const u8 max_writable = u8(std::size(m_reply_buffer));
+ for (u8 i = 0; i < 5 && reply[i] != 0; i++)
+ {
+ m_reply_buffer[m_reply_write_index] = reply[i];
+ m_reply_write_index = (m_reply_write_index + 1) % max_writable;
+ }
+
+ m_queue_timer->adjust(attotime::from_nsec(delay * 1000000));
+}
+
+TIMER_CALLBACK_MEMBER(sony_ldp1450hle_device::process_queue)
+{
+ LOGMASKED(LOG_REPLY_BYTES, "Sending reply byte: %02x\n", (u8)m_reply_buffer[m_reply_read_index]);
+ transmit_register_setup(m_reply_buffer[m_reply_read_index]);
+}
+
+//-------------------------------------------------
+// bcd_to_literal - converts a BCD value used in
+// commands a direct numeric value
+//-------------------------------------------------
+
+u32 sony_ldp1450hle_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;
+}
+
+//-------------------------------------------------
+// process_command - processes a single command
+// from the command buffer
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::add_command_byte(u8 command)
+{
+ switch (m_submode)
+ {
+ case SUBMODE_USER_INDEX:
+ switch (command)
+ {
+ case 0x00:
+ m_submode = SUBMODE_USER_INDEX_MODE_1;
+ queue_reply(0x0a, 4.3);
+ break;
+ case 0x01:
+ m_submode = SUBMODE_USER_INDEX_STRING_1;
+ queue_reply(0x0a, 4.3);
+ break;
+ case 0x02:
+ m_submode = SUBMODE_USER_INDEX_WINDOW;
+ queue_reply(0x0a, 4.3);
+ break;
+ }
+ break;
+
+ case SUBMODE_USER_INDEX_MODE_1:
+ m_user_index_x = (command & 0x3f);
+ m_submode = SUBMODE_USER_INDEX_MODE_2;
+ queue_reply(0x0a, 4.3);
+ break;
+
+ case SUBMODE_USER_INDEX_MODE_2:
+ m_user_index_y = (command & 0x3f);
+ m_submode = SUBMODE_USER_INDEX_MODE_3;
+ queue_reply(0x0a, 4.3);
+ break;
+
+ case SUBMODE_USER_INDEX_MODE_3:
+ m_user_index_mode = command;
+ m_submode = SUBMODE_NORMAL;
+ queue_reply(0x0a, 4.3);
+ break;
+
+ case SUBMODE_USER_INDEX_STRING_1:
+ m_user_index_char_idx = (command & 0x1f);
+ m_submode = SUBMODE_USER_INDEX_STRING_2;
+ queue_reply(0x0a, 4.3);
+ break;
+
+ case SUBMODE_USER_INDEX_STRING_2:
+ m_user_index_chars[m_user_index_char_idx] = (char)(command & 0x5f);
+ if (command == 0x1a)
+ {
+ m_submode = SUBMODE_NORMAL;
+ }
+ else
+ {
+ m_user_index_char_idx++;
+ if (m_user_index_char_idx > 32)
+ {
+ m_user_index_char_idx = 0;
+ }
+ }
+ queue_reply(0x0a, 4.3);
+ break;
+
+ case SUBMODE_USER_INDEX_WINDOW:
+ m_user_index_window_idx = (command & 0x1f);
+ m_submode = SUBMODE_NORMAL;
+ queue_reply(0x0a, 4.3);
+ break;
+
+ default:
+ if (command >= 0x30 && command <=0x39)
+ {
+ if (m_mode == MODE_SEARCH_CMD || m_mode == MODE_REPEAT_CMD_MARK || m_mode == MODE_REPEAT_CMD_REPS || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)
+ {
+ // Reset flags
+ if (m_cmd_buffer == -2)
+ {
+ m_cmd_buffer = 0;
+ }
+
+ if (m_cmd_buffer != 0)
+ {
+ m_cmd_buffer *= 10;
+ }
+ m_cmd_buffer += (command - 0x30);
+ queue_reply(0x0a, 4.3);
+ }
+ else
+ {
+ queue_reply(0x0b, 4.3);
+ }
+ }
+ else
+ {
+ switch (command)
+ {
+ case CMD_ENTER:
+ if (m_mode == MODE_SEARCH_CMD)
+ {
+ begin_search(m_cmd_buffer);
+ m_mode = MODE_SEARCH;
+ }
+ else if (m_mode == MODE_REPEAT_CMD_MARK)
+ {
+ if (m_address_flag == ADDRESS_FRAME)
+ {
+ m_repeat_frame_end = m_cmd_buffer;
+ }
+ else if (m_address_flag == ADDRESS_CHAPTER)
+ {
+ m_repeat_chapter_end = m_cmd_buffer;
+ }
+ m_mode = MODE_REPEAT_CMD_REPS;
+ m_cmd_buffer = -2;
+ }
+ else if (m_mode == MODE_REPEAT_CMD_REPS)
+ {
+ // if no number, presume 1
+ if (m_cmd_buffer == -2)
+ {
+ m_cmd_buffer = 1;
+ }
+ else if (m_cmd_buffer == 0)
+ {
+ m_cmd_buffer = -1; // infinite
+ }
+ m_repeat_repetitions = m_cmd_buffer;
+ m_mode = MODE_PLAY;
+ m_cmd_buffer = 0;
+ update_video_enable();
+ update_audio_squelch();
+ }
+ else if (m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)
+ {
+ m_speed = m_base_speed / m_cmd_buffer;
+ update_video_enable();
+ update_audio_squelch();
+ }
+ queue_reply(0x0a, 1.3);
+ break;
+
+ case CMD_STEP_STILL:
+ m_mode = MODE_STILL;
+ set_audio_squelch(true, true);
+ m_mark_frame = ~u32(0);
+ advance_slider(1);
+ queue_reply(0x0a, 1.4);
+ break;
+
+ case CMD_STEP_STILL_REVERSE:
+ m_mode = MODE_STILL;
+ set_audio_squelch(true, true);
+ m_mark_frame = ~u32(0);
+ advance_slider(-1);
+ queue_reply(0x0a, 1.4);
+ break;
+
+ case CMD_STOP:
+ m_mode = MODE_PARK;
+ set_audio_squelch(true, true);
+ set_video_squelch(true);
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_PLAY:
+ m_speed_accum = 0;
+ m_speed = m_base_speed;
+ m_mode = MODE_PLAY;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_FAST_FORWARD:
+ m_speed_accum = 0;
+ m_speed = m_base_speed * 3;
+ m_mode = MODE_MS_FORWARD;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_SLOW_FORWARD:
+ m_speed_accum = 0;
+ m_speed = m_base_speed / 5;
+ m_mode = MODE_MS_FORWARD;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_STEP_FORWARD:
+ // Most ROM revisions start at a slow speed, and then update to correct one
+ m_speed = m_base_speed / 7;
+ m_mode = MODE_MS_FORWARD;
+ set_audio_squelch(true, true);
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_SCAN_FORWARD:
+ m_speed_accum = 0;
+ m_speed = m_base_speed * 100;
+ m_mode = MODE_MS_FORWARD;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_PLAY_REVERSE:
+ m_speed_accum = 0;
+ m_speed = m_base_speed;
+ m_mode = MODE_MS_REVERSE;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_FAST_REVERSE:
+ m_speed_accum = 0;
+ m_speed = m_base_speed * 3;
+ m_mode = MODE_MS_REVERSE;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_SLOW_REVERSE:
+ m_speed_accum = 0;
+ m_speed = m_base_speed / 5;
+ m_mode = MODE_MS_REVERSE;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_STEP_REVERSE:
+ m_speed = m_base_speed / 7;
+ m_mode = MODE_MS_REVERSE;
+ set_audio_squelch(true, true);
+ m_mark_frame = 0;
+ advance_slider(-1);
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_SCAN_REVERSE:
+ m_speed = m_base_speed / 7;
+ m_mode = MODE_MS_REVERSE;
+ update_audio_squelch();
+ update_video_enable();
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_STILL:
+ m_mode = MODE_STILL;
+ set_audio_squelch(true, true);
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_SEARCH:
+ m_mode = MODE_SEARCH_CMD;
+ m_cmd_buffer = 0;
+ m_repeat_frame_start = 0;
+ m_repeat_frame_end = 0;
+ m_repeat_chapter_start = 0;
+ m_repeat_chapter_end = 0;
+ m_repeat_repetitions = 0;
+ m_search_frame = ~u32(0);
+ update_audio_squelch();
+ queue_reply(0x0a, 9.0);
+ break;
+
+ case CMD_REPEAT:
+ m_mode = MODE_REPEAT_CMD_MARK;
+ m_cmd_buffer = 0;
+ if (m_address_flag == ADDRESS_FRAME)
+ {
+ m_repeat_frame_start = m_curr_frame;
+ }
+ else if (m_address_flag == ADDRESS_CHAPTER)
+ {
+ m_repeat_chapter_start = m_chapter;
+ }
+ m_repeat_frame_end = 0;
+ m_repeat_chapter_end = 0;
+ m_repeat_repetitions = 0;
+ update_video_enable();
+ update_audio_squelch();
+ queue_reply(0x0a, 8.0);
+ break;
+
+ case CMD_FRAME_SET:
+ m_address_flag = ADDRESS_FRAME;
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_CHAPTER_SET:
+ m_address_flag = ADDRESS_CHAPTER;
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_CLEAR:
+ m_cmd_buffer = 0;
+ queue_reply(0x0a, 8.0);
+ break;
+
+ case CMD_CLEAR_ALL:
+ m_cmd_buffer = 0;
+ m_search_frame = 0;
+ m_search_chapter = 0;
+ m_repeat_chapter_start = 0;
+ m_repeat_frame_start = 0;
+ m_repeat_chapter_end = 0;
+ m_repeat_frame_end = 0;
+ m_repeat_repetitions = 0;
+ m_speed = m_base_speed;
+ m_mode = MODE_STILL;
+ queue_reply(0x0a, 5.5);
+ break;
+
+ case CMD_CH1_ON:
+ m_ch1_switch=true;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_CH1_OFF:
+ m_ch1_switch=false;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_CH2_ON:
+ m_ch2_switch=true;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_CH2_OFF:
+ m_ch2_switch=false;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_AUDIO_ON:
+ m_ch1_switch=true;
+ m_ch2_switch=true;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_AUDIO_OFF:
+ m_ch1_switch=false;
+ m_ch2_switch=false;
+ update_audio_squelch();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_VIDEO_ON:
+ m_video_switch = 1;
+ update_video_enable();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_VIDEO_OFF:
+ m_video_switch = 0;
+ update_video_enable();
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_INDEX_ON:
+ m_display_switch = 1;
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_INDEX_OFF:
+ m_display_switch =0;
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_ADDR_INQ:
+ {
+ u8 frame_buffer[5];
+ u32 frame_val = m_curr_frame;
+ for (u8 i = 0; i < 5; i++)
+ {
+ frame_buffer[4 - i] = frame_val%10 + 0x30;
+ frame_val /= 10;
+ }
+ queue_reply_buffer(frame_buffer, 1.3);
+ }
+ break;
+
+ case CMD_STATUS_INQ:
+ {
+ u8 status_buffer[5] = { 0x80, 0x00, 0x10, 0x00, 0xff};
+
+ if (m_mode == MODE_PLAY || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)
+ {
+ status_buffer[4] = 0x01;
+ }
+ else if (m_mode == MODE_PAUSE || m_mode == MODE_STILL)
+ {
+ status_buffer[4] = 0x20;
+ }
+ queue_reply_buffer(status_buffer, 1.3);
+ }
+ break;
+
+ case CMD_USER_INDEX_CTRL:
+ m_submode = SUBMODE_USER_INDEX;
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_USER_INDEX_ON:
+ popmessage("X %x Y %x M%x T%s (Start %x)", m_user_index_x, m_user_index_y, m_user_index_mode, m_user_index_chars,m_user_index_window_idx);
+ queue_reply(0x0a, 0.4);
+ break;
+
+ case CMD_USER_INDEX_OFF:
+ queue_reply(0x0a, 0.4);
+ break;
+
+ default:
+ popmessage("no implementation cmd %x", command);
+ queue_reply(0x0b, 0.4);
+ break;
+ }
+ }
+ LOGMASKED(LOG_SEARCHES, "Command %x\n", command);
+ }
+}
+
+//-------------------------------------------------
+// begin_search - initiates a search operation
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::begin_search(u32 value)
+{
+ if (m_address_flag == ADDRESS_FRAME)
+ {
+ m_search_frame = value;
+ }
+ else if (m_address_flag == ADDRESS_CHAPTER)
+ {
+ m_search_chapter = value;
+ }
+
+ set_audio_squelch(true, true);
+
+ if (std::abs(int32_t(m_search_frame) - int32_t(m_curr_frame)) > 100)
+ {
+ video_enable(false);
+ set_audio_squelch(true, true);
+ }
+}
+
+
+//-------------------------------------------------
+// update_audio_squelch - set audio squelch state
+// on the base device based on our audio switches
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::update_audio_squelch()
+{
+ const bool squelch_both = (m_mode == MODE_STILL || m_mode == MODE_PAUSE || m_mode == MODE_SEARCH || m_mode == MODE_MS_REVERSE || m_mode == MODE_SEARCH_CMD || m_mode == MODE_SEARCH_REP );
+ const bool squelch_left = !(m_ch1_switch) || squelch_both;
+ const bool squelch_right = !(m_ch2_switch) || squelch_both;
+ set_audio_squelch(squelch_left, squelch_right);
+}
+
+//-------------------------------------------------
+// update_video_enable - set video enable state
+// on the base device based on our video switch
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::update_video_enable()
+{
+ const bool enable = m_video_switch == 1 && (m_mode == MODE_STILL || m_mode == MODE_PLAY || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE);
+ set_video_squelch(!enable);
+ video_enable(enable);
+}
+
+
+//-------------------------------------------------
+// device_start - device initialization
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::device_start()
+{
+ // pass through to the parent
+ laserdisc_device::device_start();
+
+ // allocate timers
+ m_vbi_fetch = timer_alloc(FUNC(sony_ldp1450hle_device::process_vbi_data), this);
+ m_queue_timer = timer_alloc(FUNC(sony_ldp1450hle_device::process_queue), this);
+
+ // register state saving
+ save_item(NAME(m_baud));
+ save_item(NAME(m_cmd_buffer));
+ save_item(NAME(m_cmd_running));
+ save_item(NAME(m_reply_buffer));
+ save_item(NAME(m_reply_write_index));
+ save_item(NAME(m_reply_read_index));
+ save_item(NAME(m_reply));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_chapter));
+ save_item(NAME(m_time));
+ save_item(NAME(m_search_chapter));
+ save_item(NAME(m_search_frame));
+ save_item(NAME(m_mark_chapter));
+ save_item(NAME(m_mark_frame));
+ save_item(NAME(m_video_switch));
+ save_item(NAME(m_ch1_switch));
+ save_item(NAME(m_ch2_switch));
+ save_item(NAME(m_display_switch));
+ save_item(NAME(m_address_flag));
+ save_item(NAME(m_base_speed));
+ save_item(NAME(m_speed));
+ save_item(NAME(m_speed_accum));
+ save_item(NAME(m_curr_frame));
+ save_item(NAME(m_frame));
+ save_item(NAME(m_repeat_chapter_start));
+ save_item(NAME(m_repeat_chapter_end));
+ save_item(NAME(m_repeat_frame_start));
+ save_item(NAME(m_repeat_frame_end));
+}
+
+
+//-------------------------------------------------
+// device_reset - device reset
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::device_reset()
+{
+ // pass through to the parent
+ laserdisc_device::device_reset();
+
+ // initialize diserial
+ set_tra_rate(attotime::from_hz(m_baud));
+ set_rcv_rate(attotime::from_hz(m_baud));
+ set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1);
+
+ // reset our state
+ m_vbi_fetch->adjust(attotime::never);
+ m_queue_timer->adjust(attotime::never);
+
+ m_cmd_running = false;
+ std::fill_n(m_reply_buffer, std::size(m_reply_buffer), 0);
+ m_reply_write_index = 0;
+ m_reply_read_index = 0;
+
+ m_mode = MODE_PARK;
+ m_chapter = 0;
+ m_time = 0;
+ m_cmd_buffer = ~u32(0);
+ m_search_chapter = ~u32(0);
+ m_search_frame = ~u32(0);
+ m_mark_chapter = ~u32(0);
+ m_mark_frame = ~u32(0);
+ m_repeat_chapter_start = ~u32(0);
+ m_repeat_chapter_end = ~u32(0);
+ m_repeat_frame_start = ~u32(0);
+ m_repeat_frame_end = ~u32(0);
+ m_video_switch = 1;
+ m_ch1_switch = false;
+ m_ch2_switch = false;
+ m_display_switch = 0;
+ m_address_flag = ADDRESS_FRAME;
+ m_base_speed = 60;
+ m_speed = m_base_speed;
+ m_speed_accum = 0;
+
+ video_enable(true);
+ set_audio_squelch(true, true);
+}
+
+
+//-------------------------------------------------
+// process_vbi_data - process VBI data and
+// act on search/play seeking
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(sony_ldp1450hle_device::process_vbi_data)
+{
+ u32 line = get_field_code(LASERDISC_CODE_LINE1718, false);
+ if ((line & 0xf80000) == 0xf80000 || line == VBI_CODE_LEADIN || line == VBI_CODE_LEADOUT)
+ {
+ u32 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_STILL && m_mode != MODE_PAUSE)
+ {
+
+ if (m_mark_frame != ~u32(0) && m_search_frame == ~u32(0))
+ {
+ int32_t old_delta = int32_t(m_mark_frame) - int32_t(old_frame);
+ int32_t curr_delta = int32_t(m_mark_frame) - int32_t(m_curr_frame);
+ LOGMASKED(LOG_STOPS, "Stop Mark is currently %d, old frame is %d, current frame is %d, old delta %d, curr delta %d\n", m_mark_frame, old_frame, m_curr_frame, old_delta, curr_delta);
+ if (curr_delta == 0 || (old_delta < 0) != (curr_delta < 0))
+ {
+ m_mark_frame = ~u32(0);
+ if (is_cav_disc())
+ {
+ m_mode = MODE_STILL;
+ update_video_enable();
+ }
+ else
+ {
+ m_mode = MODE_PAUSE;
+ video_enable(false);
+ }
+
+ set_audio_squelch(true, true);
+ }
+ }
+
+ if (m_search_frame != ~u32(0))
+ {
+ // TODO: Chapter-search support
+ int32_t delta = int32_t(m_search_frame) - int32_t(m_curr_frame);
+ if (delta == 0)
+ {
+ m_search_frame = ~u32(0);
+ if (is_cav_disc())
+ {
+ if (m_mode == MODE_SEARCH_REP)
+ {
+ m_mode = MODE_PLAY;
+ }
+ else
+ {
+ m_mode = MODE_STILL;
+ }
+ update_video_enable();
+ }
+ else
+ {
+ m_mode = MODE_PLAY;
+ update_video_enable();
+ }
+
+ queue_reply(0x01, 1.3); //completion
+ }
+ else if (delta <= 2 && delta > 0)
+ {
+ // 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));
+ }
+ }
+ }
+
+
+ const bool repeat_hit = ((m_repeat_frame_end != ~u32(0) && m_curr_frame >= m_repeat_frame_end) || (m_mode == MODE_MS_REVERSE && (m_repeat_frame_start != ~u32(0) && m_curr_frame <= m_repeat_frame_start) ));
+ if (m_repeat_repetitions != 0 && repeat_hit)
+ {
+
+ if (m_repeat_repetitions > 0)
+ {
+ m_repeat_repetitions -= 1;
+ }
+
+ const bool reverse = m_mode == MODE_MS_REVERSE;
+
+ m_mode = MODE_STILL;
+ update_video_enable();
+ update_audio_squelch();
+ if (m_repeat_repetitions != 0)
+ {
+ m_mode = MODE_SEARCH_REP;
+ begin_search(reverse ? m_repeat_frame_end : m_repeat_frame_start);
+ }
+ else
+ {
+ m_repeat_frame_end = ~u32(0);
+ m_repeat_frame_start = ~u32(0);
+ queue_reply(0x01, 1.3);
+ }
+ }
+
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// player_vsync - VSYNC callback, called at the
+// start of the blanking period
+//-------------------------------------------------
+
+void sony_ldp1450hle_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_DOOR_OPEN)
+ {
+ m_vbi_fetch->adjust(screen().time_until_pos(19*2));
+ }
+}
+
+
+//-------------------------------------------------
+// player_update - update callback, called on
+// the first visible line of the frame
+//-------------------------------------------------
+
+int32_t sony_ldp1450hle_device::player_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime)
+{
+ if (!fieldnum)
+ return 0;
+
+ if (m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)
+ {
+ m_speed_accum += m_speed;
+ int elapsed_tracks = m_speed_accum / m_base_speed;
+ m_speed_accum -= elapsed_tracks * m_base_speed;
+ if (m_mode == MODE_MS_REVERSE)
+ elapsed_tracks *= -1;
+
+ if (m_mark_frame != ~u32(0))
+ {
+ const int32_t jump_frame = int32_t(m_curr_frame) + elapsed_tracks;
+ const int32_t curr_delta = int32_t(m_mark_frame) - int32_t(m_curr_frame);
+ const int32_t next_delta = int32_t(m_mark_frame) - int32_t(jump_frame);
+ if ((curr_delta < 0) != (next_delta < 0))
+ {
+ elapsed_tracks = curr_delta;
+ }
+ }
+ return elapsed_tracks;
+ }
+
+
+ if (m_mode == MODE_PLAY || m_mode == MODE_SEARCH || m_mode == MODE_SEARCH_REP)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// rcv_complete - called by diserial when we
+// have received a complete byte
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::rcv_complete()
+{
+ receive_register_extract();
+ add_command_byte(get_received_char());
+}
+
+
+//-------------------------------------------------
+// tra_complete - called by diserial when we
+// have transmitted a complete byte
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::tra_complete()
+{
+ m_reply_read_index = (m_reply_read_index + 1) % u8(std::size(m_reply_buffer));
+ if (m_reply_read_index != m_reply_write_index)
+ {
+ const u8 data = u8(m_reply_buffer[m_reply_read_index]);
+ LOGMASKED(LOG_REPLY_BYTES, "Sending reply byte: %02x\n", data);
+ transmit_register_setup(data);
+ }
+}
+
+
+//-------------------------------------------------
+// tra_callback - called by diserial when we
+// transmit a single bit
+//-------------------------------------------------
+
+void sony_ldp1450hle_device::tra_callback()
+{
+ m_serial_tx(transmit_register_get_data_bit());
+}