From 06e60e848fde3f2c0a08f664bddb3b98f0937336 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Sat, 26 Mar 2022 14:41:51 +0100 Subject: Time Traveler (set 1) - promoted to working (#9468) [Ryan Holtz] * machine/laserdsc.cpp: Added functionality to retrieve if a disc is CAV. * machine/ldv4200hle.cpp: Added high-level Pioneer LD-V4200 player emulation. Machines promoted to working ---------------------------- Time Traveler (set 1) [Ryan Holtz, Matt O, ld-decode Team] --- scripts/src/machine.lua | 12 + scripts/target/mame/arcade.lua | 1 + src/devices/machine/laserdsc.cpp | 20 + src/devices/machine/laserdsc.h | 2 + src/devices/machine/ldv4200hle.cpp | 948 +++++++++++++++++++++++++++++++++++++ src/devices/machine/ldv4200hle.h | 217 +++++++++ src/mame/drivers/timetrv.cpp | 319 +++++++++---- src/mame/layout/timetrv.lay | 65 +++ 8 files changed, 1503 insertions(+), 81 deletions(-) create mode 100644 src/devices/machine/ldv4200hle.cpp create mode 100644 src/devices/machine/ldv4200hle.h create mode 100644 src/mame/layout/timetrv.lay diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index f4f825e64b9..06d11c4d388 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1974,6 +1974,18 @@ if (MACHINES["LDV1000"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/ldv4200hle.h,MACHINES["LDV4200HLE"] = true +--------------------------------------------------- + +if (MACHINES["LDV4200HLE"]~=null) then + files { + MAME_DIR .. "src/devices/machine/ldv4200hle.cpp", + MAME_DIR .. "src/devices/machine/ldv4200hle.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/ldp1000.h,MACHINES["LDP1000"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index fdc2b97fb27..108228f30b1 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -542,6 +542,7 @@ MACHINES["LC89510"] = true MACHINES["LDPR8210"] = true MACHINES["LDSTUB"] = true MACHINES["LDV1000"] = true +MACHINES["LDV4200HLE"] = true MACHINES["LDP1000"] = true MACHINES["LDP1450"] = true MACHINES["LDVP931"] = true diff --git a/src/devices/machine/laserdsc.cpp b/src/devices/machine/laserdsc.cpp index 4d04ea68505..475014f4f83 100644 --- a/src/devices/machine/laserdsc.cpp +++ b/src/devices/machine/laserdsc.cpp @@ -69,6 +69,7 @@ laserdisc_device::laserdisc_device(const machine_config &mconfig, device_type ty m_overclip(0, -1, 0, -1), m_overupdate_rgb32(*this), m_disc(nullptr), + m_is_cav_disc(false), m_width(0), m_height(0), m_fps_times_1million(0), @@ -398,7 +399,9 @@ void laserdisc_device::set_slider_speed(int32_t tracks_per_vsync) // negative values store negative times else + { m_attospertrack = -(vsyncperiod / -tracks_per_vsync).as_attoseconds(); + } if (LOG_SLIDER) printf("Slider speed = %d\n", tracks_per_vsync); @@ -685,6 +688,23 @@ void laserdisc_device::init_disc() err = m_disc->read_metadata(AV_LD_METADATA_TAG, 0, m_vbidata); if (err || (m_vbidata.size() != totalhunks * VBI_PACKED_BYTES)) throw emu_fatalerror("Precomputed VBI metadata missing or incorrect size"); + + m_is_cav_disc = false; + vbi_metadata vbidata_even = { 0 }; + vbi_metadata_unpack(&vbidata_even, nullptr, &m_vbidata[m_chdtracks * VBI_PACKED_BYTES]); + if ((vbidata_even.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + { + m_is_cav_disc = true; + } + else + { + vbi_metadata vbidata_odd = { 0 }; + vbi_metadata_unpack(&vbidata_odd, nullptr, &m_vbidata[(m_chdtracks + 1) * VBI_PACKED_BYTES]); + if ((vbidata_odd.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + { + m_is_cav_disc = true; + } + } } m_maxtrack = std::max(m_maxtrack, VIRTUAL_LEAD_IN_TRACKS + VIRTUAL_LEAD_OUT_TRACKS + m_chdtracks); } diff --git a/src/devices/machine/laserdsc.h b/src/devices/machine/laserdsc.h index b706afb5816..9466a869505 100644 --- a/src/devices/machine/laserdsc.h +++ b/src/devices/machine/laserdsc.h @@ -233,6 +233,7 @@ protected: int32_t generic_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime, player_state_info &curstate); // general helpers + bool is_cav_disc() const { return m_is_cav_disc; } bool is_start_of_frame(const vbi_metadata &vbi); int frame_from_metadata(const vbi_metadata &metadata); int chapter_from_metadata(const vbi_metadata &metadata); @@ -277,6 +278,7 @@ private: // disc parameters chd_file * m_disc; // handle to the disc itself std::vector m_vbidata; // pointer to precomputed VBI data + bool m_is_cav_disc; // precomputed check if the mounted disc is CAV int m_width; // width of video int m_height; // height of video uint32_t m_fps_times_1million; // frame rate of video diff --git a/src/devices/machine/ldv4200hle.cpp b/src/devices/machine/ldv4200hle.cpp new file mode 100644 index 00000000000..c86b3e4cee7 --- /dev/null +++ b/src/devices/machine/ldv4200hle.cpp @@ -0,0 +1,948 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/************************************************************************* + + ldv4200hle.cpp + + Pioneer LD-V4200 laserdisc player simulation. + +************************************************************************** + + To do: + + * On-screen display support + * Better CLV support + * Chapter-search support + * Commands that Time Traveler doesn't use: + - Door Open/Close + - Reject + - Pause/Still + - Scan Forward/Reverse + - Multitrack-Jump Forward/Reverse + - Clear + - Leadout Symbol + - Key Lock and on-screen functions + - Status Requests + - Registers A-D + +*************************************************************************/ + + +#include "emu.h" +#include "ldv4200hle.h" + + +#define LOG_COMMAND_BYTES (1 << 1U) +#define LOG_COMMANDS (1 << 2U) +#define LOG_COMMAND_BUFFERS (1 << 3U) +#define LOG_REPLIES (1 << 4U) +#define LOG_REPLY_BYTES (1 << 5U) +#define LOG_SEARCHES (1 << 6U) +#define LOG_STOPS (1 << 7U) +#define LOG_SQUELCHES (1 << 8U) +#define LOG_FRAMES (1 << 9U) +#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(PIONEER_LDV4200HLE, pioneer_ldv4200hle_device, "ldv4200hle", "Pioneer LD-V4200 HLE") + + + +//************************************************************************** +// PIONEER LD-V4200 HLE IMPLEMENTATION +//************************************************************************** + +//------------------------------------------------- +// pioneer_ldv4200hle_device - constructor +//------------------------------------------------- + +pioneer_ldv4200hle_device::pioneer_ldv4200hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : laserdisc_device(mconfig, PIONEER_LDV4200HLE, tag, owner, clock) + , device_serial_interface(mconfig, *this) + , m_serial_tx(*this) + , m_vbi_fetch(nullptr) + , m_cmd_length(0) + , 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_frame(0) + , m_search_chapter(~0U) + , m_search_frame(~0U) + , m_mark_chapter(~0U) + , m_mark_frame(~0U) + , m_key_lock(0) + , m_video_switch(1) + , m_audio_switch(0) + , m_display_switch(0) + , m_address_flag(ADDRESS_FRAME) + , m_speed(60) + , m_speed_accum(0) + , m_comm_ctrl(3) + , m_reg_a(3) + , m_reg_b(0) + , m_reg_c(0) + , m_reg_d(0) + , m_aux_port(3) + , m_curr_frame(0) +{ +} + + +//------------------------------------------------- +// add_command_byte - handle a new data byte +// received over the serial link +//------------------------------------------------- + +void pioneer_ldv4200hle_device::add_command_byte(uint8_t data) +{ + // Space and L/F codes are ignored in command sequences, per LD-V4400 Level I & III User's Manual, pg. 4-8 + if (data == 0x20 || data == 0x0a) + return; + + LOGMASKED(LOG_COMMAND_BYTES, "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 (data == 0x0d) + { + normalize_command_buffer(); + process_command_buffer(); + m_cmd_length = 0; + } +} + + +//------------------------------------------------- +// queue_reply - queues a reply string to send +// back to the host +//------------------------------------------------- + +void pioneer_ldv4200hle_device::queue_reply(const char *reply) +{ + char print_buf[128]; + + uint8_t max_writable = (uint8_t)std::size(m_reply_buffer); + for (uint8_t i = 0; i < max_writable && reply[i] != 0; i++) + { + m_reply_buffer[m_reply_write_index] = reply[i]; + m_reply_write_index = (m_reply_write_index + 1) % max_writable; + print_buf[i] = (reply[i] == '\x0d' ? 0 : reply[i]); + } + LOGMASKED(LOG_REPLIES, "Sending reply: %s\n", print_buf); + + if (!m_replying) + { + m_replying = true; + LOGMASKED(LOG_REPLY_BYTES, "Sending reply byte: %02x\n", (uint8_t)m_reply_buffer[m_reply_read_index]); + transmit_register_setup(m_reply_buffer[m_reply_read_index]); + } +} + + +//------------------------------------------------- +// queue_error - queues an error-code string to +// send back to the host +//------------------------------------------------- + +void pioneer_ldv4200hle_device::queue_error(error_code err) +{ + char buf[5] = { 'E', '0', '0', '\x0d', '\0' }; + buf[1] += err / 10; + buf[2] += err % 10; + queue_reply(buf); +} + + +//------------------------------------------------- +// normalize_command_buffer - ensure any alphabet +// characters in the command buffer are +// upper-cased for matching purposes. +//------------------------------------------------- + +void pioneer_ldv4200hle_device::normalize_command_buffer() +{ + char print_buf[64]; + for (uint8_t i = 0; i < m_cmd_length; i++) + { + if (m_cmd_buffer[i] >= 'a' && m_cmd_buffer[i] <= 'z') + { + m_cmd_buffer[i] &= ~0x20; + } + print_buf[i] = (char)m_cmd_buffer[i]; + } + print_buf[m_cmd_length - 1] = '\0'; + LOGMASKED(LOG_COMMAND_BUFFERS, "Command Buffer: %02d:%02d:%02d: %s\n", (int)(machine().time().seconds() / 60), (int)(machine().time().seconds()) % 60, (machine().time() * 100).seconds() % 100, print_buf); +} + + +//------------------------------------------------- +// process_command_buffer - process a command +// line sent from the host +//------------------------------------------------- + +void pioneer_ldv4200hle_device::process_command_buffer() +{ + if (m_cmd_length <= 1) + return; + + error_code err = ERR_NONE; + uint8_t cmd_index = 0; + bool send_reply = true; + while (cmd_index < m_cmd_length && err == ERR_NONE) + { + if (cmd_index == (m_cmd_length - 1) && m_cmd_buffer[cmd_index] == 0x0d) + { + break; + } + + uint32_t value = ~0U; + if (is_number(m_cmd_buffer[cmd_index])) + { + cmd_index += parse_numeric_value(cmd_index, value, err); + } + if (err == ERR_NONE) + { + cmd_index += process_command(cmd_index, value, err); + if (m_cmd_running) + { + send_reply = false; + } + } + } + + if (send_reply) + { + if (err == ERR_NONE) + { + queue_reply("R\x0d"); + } + else + { + queue_error(err); + } + } +} + + +//------------------------------------------------- +// bcd_to_literal - converts a BCD value used in +// commands a direct numeric value +//------------------------------------------------- + +uint32_t pioneer_ldv4200hle_device::bcd_to_literal(uint32_t bcd) +{ + uint32_t value = 0; + uint32_t shift = 28; + uint32_t multiplier = 10000000; + for (uint32_t i = 0; i < 8; i++) + { + uint32_t digit = (bcd >> shift) & 0xf; + bcd &= ~(0xf << shift); + + value += digit * multiplier; + + multiplier /= 10; + shift -= 4; + } + return value; +} + + +//------------------------------------------------- +// is_number - indicates if a given character is +// a numeric value +//------------------------------------------------- + +bool pioneer_ldv4200hle_device::is_number(char value) +{ + return value >= '0' && value <= '9'; +} + + +//------------------------------------------------- +// parse_numeric_value - parses a numeric value +// from the command buffer +//------------------------------------------------- + +uint8_t pioneer_ldv4200hle_device::parse_numeric_value(uint8_t cmd_index, uint32_t &value, error_code &err) +{ + static const uint8_t MAX_NUMBER_LENGTH = 7; + uint8_t number_length = 0; + value = 0; + while (number_length < MAX_NUMBER_LENGTH && is_number(m_cmd_buffer[cmd_index]) && cmd_index < m_cmd_length) + { + value *= 10; + value += m_cmd_buffer[cmd_index] - '0'; + cmd_index++; + number_length++; + } + + if (cmd_index == m_cmd_length) + { + err = ERR_COMMUNICATION; + return number_length; + } + + if (number_length == MAX_NUMBER_LENGTH && is_number(m_cmd_buffer[cmd_index])) + { + err = ERR_MISSING_ARGUMENT; + return number_length; + } + + return number_length; +} + + +//------------------------------------------------- +// process_command - processes a single command +// from the command buffer +//------------------------------------------------- + +uint8_t pioneer_ldv4200hle_device::process_command(uint8_t cmd_index, uint32_t value, error_code &err) +{ + const uint8_t remaining_bytes = m_cmd_length - cmd_index; + if (remaining_bytes == 1 && m_cmd_buffer[cmd_index] == 0x0d) + { + // Done processing + return remaining_bytes; + } + else if (remaining_bytes < 3) + { + // Not enough data in the buffer to form a valid command + err = ERR_COMMUNICATION; + return remaining_bytes; + } + + uint16_t command = (m_cmd_buffer[cmd_index] << 8) | m_cmd_buffer[cmd_index + 1]; + switch (command) + { + case CMD_DOOR_OPEN: + LOGMASKED(LOG_COMMANDS, "%s: Command: Door Open\n", machine().describe_context()); + break; + case CMD_DOOR_CLOSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Door Close\n", machine().describe_context()); + break; + case CMD_REJECT: + LOGMASKED(LOG_COMMANDS, "%s: Command: Reject\n", machine().describe_context()); + break; + case CMD_START: + LOGMASKED(LOG_COMMANDS | LOG_SQUELCHES, "%s: Command: Start (squelching audio, unsquelching + disabling video)\n", machine().describe_context()); + m_mode = MODE_PAUSE; + video_enable(false); + set_video_squelch(false); + set_audio_squelch(true, true); + break; + case CMD_PLAY: + LOGMASKED(LOG_COMMANDS, "%s: Command: Play [%d] (cancelling search)\n", machine().describe_context(), value == ~0U ? 0 : value); + m_speed_accum = 0; + m_mode = MODE_PLAY; + update_audio_squelch(); + update_video_enable(); + if (value != ~0U) + { + LOGMASKED(LOG_COMMANDS, "%s: Setting stop frame\n", machine().describe_context()); + m_mark_frame = value + 1; + m_cmd_running = true; + } + m_search_frame = ~0U; + m_search_chapter = ~0U; + break; + case CMD_PAUSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Pause\n", machine().describe_context()); + m_mode = MODE_PAUSE; + video_enable(false); + set_audio_squelch(true, true); + break; + case CMD_STILL: + LOGMASKED(LOG_COMMANDS, "%s: Command: Still\n", machine().describe_context()); + m_mode = MODE_STILL; + set_audio_squelch(true, true); + break; + case CMD_STEP_FORWARD: + LOGMASKED(LOG_COMMANDS, "%s: Command: Step Forward\n", machine().describe_context()); + m_mode = MODE_STILL; + set_audio_squelch(true, true); + m_mark_frame = ~0U; + advance_slider(1); + break; + case CMD_STEP_REVERSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Step Reverse\n", machine().describe_context()); + m_mode = MODE_STILL; + set_audio_squelch(true, true); + m_mark_frame = ~0U; + advance_slider(-1); + break; + case CMD_SCAN_FORWARD: + LOGMASKED(LOG_COMMANDS, "%s: Command: Scan Forward\n", machine().describe_context()); + break; + case CMD_SCAN_REVERSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Scan Reverse\n", machine().describe_context()); + break; + case CMD_MULTISPEED_FORWARD: + LOGMASKED(LOG_COMMANDS, "%s: Command: Multi-Speed Forward (%d) (cancelling search)\n", machine().describe_context(), value == ~0U ? 0 : value); + m_mode = MODE_MS_FORWARD; + m_search_frame = ~0U; + m_search_chapter = ~0U; + if (value != ~0U) + { + LOGMASKED(LOG_COMMANDS, "%s: Setting stop frame\n", machine().describe_context()); + m_mark_frame = value + 1; + m_cmd_running = true; + } + break; + case CMD_MULTISPEED_REVERSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Multi-Speed Reverse (%d) (cancelling search)\n", machine().describe_context(), value == ~0U ? 0 : value); + m_mode = MODE_MS_REVERSE; + m_search_frame = ~0U; + m_search_chapter = ~0U; + if (value != ~0U) + { + LOGMASKED(LOG_COMMANDS, "%s: Setting stop frame\n", machine().describe_context()); + m_mark_frame = value + 1; + m_cmd_running = true; + } + break; + case CMD_SPEED_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Speed Set %05d\n", machine().describe_context(), value); + if (is_cav_disc()) + { + m_speed = value; + if (m_speed == 0) + { + m_speed = 1; + } + else if (m_speed > 255) + { + m_speed = 255; + } + } + else + { + err = ERR_NOT_AVAILABLE; + } + break; + case CMD_SEARCH: + LOGMASKED(LOG_COMMANDS, "%s: Command: Search %d\n", machine().describe_context(), value); + begin_search(value); + m_cmd_running = true; + m_mode = MODE_SEARCH; + break; + case CMD_MULTITRACK_FORWARD: + LOGMASKED(LOG_COMMANDS, "%s: Command: Multi-Track Jump Forward %05d\n", machine().describe_context(), value); + break; + case CMD_MULTITRACK_REVERSE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Multi-Track Jump Reverse %05d\n", machine().describe_context(), value); + break; + case CMD_STOP_MARKER: + LOGMASKED(LOG_COMMANDS, "%s: Command: Stop Marker %d\n", machine().describe_context(), value); + if (m_address_flag == ADDRESS_FRAME) + { + m_mark_frame = value + 1; + } + else if (m_address_flag == ADDRESS_CHAPTER) + { + m_mark_chapter = value; + } + break; + case CMD_FRAME_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Frame Set\n", machine().describe_context()); + m_address_flag = ADDRESS_FRAME; + break; + case CMD_TIME_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Time Set\n", machine().describe_context()); + if (!is_cav_disc()) + { + m_address_flag = ADDRESS_TIME; + } + else + { + err = ERR_NOT_AVAILABLE; + } + break; + case CMD_CHAPTER_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Chapter Set\n", machine().describe_context()); + m_address_flag = ADDRESS_CHAPTER; + break; + case CMD_CLEAR: + LOGMASKED(LOG_COMMANDS, "%s: Command: Clear\n", machine().describe_context()); + break; + case CMD_LEADOUT_SYMBOL: + LOGMASKED(LOG_COMMANDS, "%s: Command: Lead-Out Symbol\n", machine().describe_context()); + break; + case CMD_AUDIO_CTRL: + m_audio_switch = value; + LOGMASKED(LOG_COMMANDS, "%s: Command: Audio Control %05d\n", machine().describe_context(), value); + update_audio_squelch(); + break; + case CMD_VIDEO_CTRL: + LOGMASKED(LOG_COMMANDS, "%s: Command: Video Control %05d\n", machine().describe_context(), value); + m_video_switch = value; + update_video_enable(); + break; + case CMD_KEY_LOCK: + LOGMASKED(LOG_COMMANDS, "%s: Command: Key Lock %05d\n", machine().describe_context(), value); + break; + case CMD_DISPLAY_CONTROL: + LOGMASKED(LOG_COMMANDS, "%s: Command: Display Control %05d\n", machine().describe_context(), value); + break; + case CMD_CLEAR_SCREEN: + LOGMASKED(LOG_COMMANDS, "%s: Command: Clear Screen\n", machine().describe_context()); + break; + case CMD_PRINT_CHAR: + LOGMASKED(LOG_COMMANDS, "%s: Command: Print Character %05d\n", machine().describe_context(), value); + break; + case CMD_REQ_FRAME_NUMBER: + LOGMASKED(LOG_COMMANDS, "%s: Command: Frame Number Request\n", machine().describe_context()); + break; + case CMD_REQ_TIME_CODE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Time Code Request\n", machine().describe_context()); + break; + case CMD_REQ_CHAPTER_NUMBER: + LOGMASKED(LOG_COMMANDS, "%s: Command: Chapter Number Request\n", machine().describe_context()); + break; + case CMD_REQ_PLAYER_MODE: + LOGMASKED(LOG_COMMANDS, "%s: Command: Player Active Mode Request\n", machine().describe_context()); + break; + case CMD_REQ_DISC_STATUS: + LOGMASKED(LOG_COMMANDS, "%s: Command: Disc Status Request\n", machine().describe_context()); + break; + case CMD_REQ_LDP_MODEL: + LOGMASKED(LOG_COMMANDS, "%s: Command: LDP Model Name Request\n", machine().describe_context()); + break; + case CMD_REQ_PIONEER_DISC_ID: + LOGMASKED(LOG_COMMANDS, "%s: Command: Pioneer User's Code Request (Disc ID)\n", machine().describe_context()); + break; + case CMD_REQ_STANDARD_DISC_ID: + LOGMASKED(LOG_COMMANDS, "%s: Command: Standard User's Code Request (Disc ID)\n", machine().describe_context()); + break; + case CMD_REQ_TV_SYSTEM: + LOGMASKED(LOG_COMMANDS, "%s: Command: Television System Request\n", machine().describe_context()); + break; + case CMD_COMMUNICATION_CTRL: + LOGMASKED(LOG_COMMANDS, "%s: Command: Communication Control %05d\n", machine().describe_context(), value); + break; + case CMD_REQ_CCR_MODE: + LOGMASKED(LOG_COMMANDS, "%s: Command: CCR Mode Request\n", machine().describe_context()); + break; + case CMD_REGISTER_A_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register A Set (Display) %d\n", machine().describe_context(), value); + break; + case CMD_REGISTER_B_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register B Set (Squelch Control) %d\n", machine().describe_context(), value); + break; + case CMD_REGISTER_C_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register C Set (Miscellaneous) %d\n", machine().describe_context(), value); + break; + case CMD_REGISTER_D_SET: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register D Set (RS-232) %d\n", machine().describe_context(), value); + break; + case CMD_REQ_REGISTER_A: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register A Request (Display)\n", machine().describe_context()); + break; + case CMD_REQ_REGISTER_B: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register B Request (Squelch Control)\n", machine().describe_context()); + break; + case CMD_REQ_REGISTER_C: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register C Request (Miscellaneous)\n", machine().describe_context()); + break; + case CMD_REQ_REGISTER_D: + LOGMASKED(LOG_COMMANDS, "%s: Command: Register D Request (RS-232)\n", machine().describe_context()); + break; + case CMD_REQ_INPUT_UNIT: + LOGMASKED(LOG_COMMANDS, "%s: Command: Input Unit Request\n", machine().describe_context()); + break; + case CMD_INPUT_NUMBER_WAIT: + LOGMASKED(LOG_COMMANDS, "%s: Command: Input Number Wait\n", machine().describe_context()); + break; + default: + LOGMASKED(LOG_COMMANDS, "%s: Command: Unknown (%c%c)\n", machine().describe_context(), m_cmd_buffer[cmd_index], m_cmd_buffer[cmd_index + 1]); + err = ERR_NOT_AVAILABLE; + break; + } + + return 2; +} + + +//------------------------------------------------- +// begin_search - initiates a search operation +//------------------------------------------------- + +void pioneer_ldv4200hle_device::begin_search(uint32_t value) +{ + if (m_address_flag == ADDRESS_FRAME) + { + m_search_frame = value + 1; + LOGMASKED(LOG_SEARCHES, "%s: Beginning search from frame address %d\n", machine().describe_context(), value); + } + else if (m_address_flag == ADDRESS_CHAPTER) + { + m_search_chapter = value; + LOGMASKED(LOG_SEARCHES, "%s: Beginning search from chapter address %d\n", machine().describe_context(), value); + } + + set_audio_squelch(true, true); + + if (std::abs((int32_t)m_search_frame - (int32_t)m_curr_frame) > 100) + { + LOGMASKED(LOG_SEARCHES | LOG_SQUELCHES, "%s: Search distance is outside +/- 100 frames, squelching audio+video\n", machine().describe_context()); + video_enable(false); + set_audio_squelch(true, true); + } + else + { + LOGMASKED(LOG_SEARCHES | LOG_SQUELCHES, "%s: Search distance is within +/- 100 frames, squelching audio and doing live search\n", machine().describe_context()); + } +} + + +//------------------------------------------------- +// update_audio_squelch - set audio squelch state +// on the base device based on our audio switch +//------------------------------------------------- + +void pioneer_ldv4200hle_device::update_audio_squelch() +{ + const bool squelch_both = (m_mode == MODE_STILL || m_mode == MODE_PAUSE || m_mode == MODE_SEARCH); + const bool squelch_left = !(m_audio_switch == 1 || m_audio_switch == 3) || squelch_both; + const bool squelch_right = !(m_audio_switch == 2 || m_audio_switch == 3) || squelch_both; + set_audio_squelch(squelch_left, squelch_right); + LOGMASKED(LOG_SQUELCHES, "%s: Updating audio squelch (L:%d, R:%d)\n", machine().describe_context(), squelch_left, squelch_right); +} + + +//------------------------------------------------- +// update_video_enable - set video enable state +// on the base device based on our video switch +//------------------------------------------------- + +void pioneer_ldv4200hle_device::update_video_enable() +{ + video_enable(m_video_switch == 1 && (m_mode == MODE_STILL || m_mode == MODE_PLAY || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)); + LOGMASKED(LOG_SQUELCHES, "%s: Updating video enable (Switch:%d, Mode:%d)\n", machine().describe_context(), m_video_switch, m_mode); +} + + +//------------------------------------------------- +// device_start - device initialization +//------------------------------------------------- + +void pioneer_ldv4200hle_device::device_start() +{ + // pass through to the parent + laserdisc_device::device_start(); + + m_serial_tx.resolve_safe(); + + // allocate timers + m_vbi_fetch = timer_alloc(TID_VBI_DATA_FETCH); + + // register state saving + save_item(NAME(m_cmd_buffer)); + save_item(NAME(m_cmd_length)); + 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_replying)); + save_item(NAME(m_mode)); + save_item(NAME(m_chapter)); + save_item(NAME(m_time)); + save_item(NAME(m_frame)); + 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_key_lock)); + save_item(NAME(m_video_switch)); + save_item(NAME(m_audio_switch)); + save_item(NAME(m_display_switch)); + save_item(NAME(m_address_flag)); + save_item(NAME(m_speed)); + save_item(NAME(m_speed_accum)); + save_item(NAME(m_comm_ctrl)); + save_item(NAME(m_reg_a)); + save_item(NAME(m_reg_b)); + save_item(NAME(m_reg_c)); + save_item(NAME(m_reg_d)); + save_item(NAME(m_aux_port)); + save_item(NAME(m_curr_frame)); +} + + +//------------------------------------------------- +// device_reset - device reset +//------------------------------------------------- + +void pioneer_ldv4200hle_device::device_reset() +{ + // pass through to the parent + laserdisc_device::device_reset(); + + // initialize diserial + set_tra_rate(attotime::from_hz(4800)); + set_rcv_rate(attotime::from_hz(4800)); + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + + // reset our state + m_vbi_fetch->adjust(attotime::never); + + std::fill_n(m_cmd_buffer, 0, std::size(m_cmd_buffer)); + m_cmd_length = 0; + m_cmd_running = false; + std::fill_n(m_reply_buffer, 0, std::size(m_reply_buffer)); + m_reply_write_index = 0; + m_reply_read_index = 0; + m_replying = false; + m_mode = MODE_PARK; + m_chapter = 0; + m_time = 0; + m_frame = 0; + m_search_chapter = ~0U; + m_search_frame = ~0U; + m_mark_chapter = ~0U; + m_mark_frame = ~0U; + m_key_lock = 0; + m_video_switch = 1; + m_audio_switch = 0; + m_display_switch = 0; + m_address_flag = ADDRESS_FRAME; + m_speed = 60; + m_speed_accum = 0; + m_comm_ctrl = 3; + m_reg_a = 3; + m_reg_b = 0; + m_reg_c = 0; + m_reg_d = 0; + m_aux_port = 3; + m_curr_frame = 0; + + video_enable(false); + set_audio_squelch(true, true); +} + + +//------------------------------------------------- +// device_timer - handle timers set by this +// device +//------------------------------------------------- + +void pioneer_ldv4200hle_device::device_timer(emu_timer &timer, device_timer_id id, int param) +{ + switch (id) + { + case TID_VBI_DATA_FETCH: + { + uint32_t line = get_field_code(LASERDISC_CODE_LINE1718, false); + if ((line & 0xf80000) == 0xf80000 || 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_STILL && m_mode != MODE_PAUSE) + { + if (m_mark_frame != ~0U && m_search_frame == ~0U) + { + int32_t old_delta = (int32_t)old_frame - (int32_t)m_mark_frame; + int32_t curr_delta = (int32_t)m_curr_frame - (int32_t)m_mark_frame; + LOGMASKED(LOG_STOPS, "%s: Stop Mark is currently %d, old frame is %d, current frame is %d, old delta %d, curr delta %d\n", machine().describe_context(), m_mark_frame, old_frame, m_curr_frame, old_delta, curr_delta); + if (curr_delta == 0 || std::signbit(old_delta) != std::signbit(curr_delta)) + { + m_mark_frame = ~0U; + if (is_cav_disc()) + { + LOGMASKED(LOG_STOPS | LOG_SQUELCHES, "%s: Stop Mark: Zero delta w/ CAV disc, entering still mode and squelching audio\n", machine().describe_context()); + m_mode = MODE_STILL; + update_video_enable(); + } + else + { + LOGMASKED(LOG_STOPS | LOG_SQUELCHES, "%s: Stop Mark: Zero delta w/ CLV disc, entering still mode and squelching video+audio\n", machine().describe_context()); + m_mode = MODE_PAUSE; + video_enable(false); + } + + set_audio_squelch(true, true); + + if (m_cmd_running) + { + LOGMASKED(LOG_SEARCHES | LOG_COMMANDS, "%s: Stop Mark: Command running, sending reply\n", machine().describe_context()); + m_cmd_running = false; + queue_reply("R\x0d"); + } + } + } + + if (m_search_frame != ~0U) + { + // TODO: Chapter-search support + int32_t delta = (int32_t)m_curr_frame - (int32_t)m_search_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; + if (is_cav_disc()) + { + LOGMASKED(LOG_SEARCHES | LOG_SQUELCHES, "%s: Search Mark: Zero delta w/ CAV disc, entering still mode and squelching audio\n", machine().describe_context()); + m_mode = MODE_STILL; + update_video_enable(); + } + else + { + LOGMASKED(LOG_SEARCHES | LOG_SQUELCHES, "%s: Search Mark: Zero delta w/ CLV disc, entering still mode and squelching video+audio\n", machine().describe_context()); + m_mode = MODE_PAUSE; + video_enable(false); + } + + set_audio_squelch(true, true); + + if (m_cmd_running) + { + LOGMASKED(LOG_SEARCHES | LOG_COMMANDS, "%s: Search Mark: Command running, sending reply\n", machine().describe_context()); + m_cmd_running = false; + queue_reply("R\x0d"); + } + } + else if (delta >= -2 && delta < 0) + { + LOGMASKED(LOG_SEARCHES, "%s: Negative-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::max(1, -delta / 2)); + } + else + { + advance_slider(std::min(-2, -delta / 2)); + } + } + } + } + } + break; + } + + // pass everything else onto the parent + default: + laserdisc_device::device_timer(timer, id, param); + break; + } +} + + +//------------------------------------------------- +// player_vsync - VSYNC callback, called at the +// start of the blanking period +//------------------------------------------------- + +void pioneer_ldv4200hle_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 pioneer_ldv4200hle_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 / 60; + m_speed_accum -= elapsed_tracks * 60; + if (m_mode == MODE_MS_REVERSE) + elapsed_tracks *= -1; + return elapsed_tracks; + } + + + if (m_mode == MODE_PLAY || m_mode == MODE_SEARCH) + { + return 1; + } + + return 0; +} + + +//------------------------------------------------- +// rcv_complete - called by diserial when we +// have received a complete byte +//------------------------------------------------- + +void pioneer_ldv4200hle_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 pioneer_ldv4200hle_device::tra_complete() +{ + m_reply_read_index = (m_reply_read_index + 1) % (uint8_t)std::size(m_reply_buffer); + if (m_reply_read_index != m_reply_write_index) + { + uint8_t data = (uint8_t)m_reply_buffer[m_reply_read_index]; + LOGMASKED(LOG_REPLY_BYTES, "Sending reply byte: %02x\n", data); + transmit_register_setup(data); + } + else + { + m_replying = false; + } +} + + +//------------------------------------------------- +// tra_callback - called by diserial when we +// transmit a single bit +//------------------------------------------------- + +void pioneer_ldv4200hle_device::tra_callback() +{ + m_serial_tx(transmit_register_get_data_bit()); +} diff --git a/src/devices/machine/ldv4200hle.h b/src/devices/machine/ldv4200hle.h new file mode 100644 index 00000000000..f57763698eb --- /dev/null +++ b/src/devices/machine/ldv4200hle.h @@ -0,0 +1,217 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/************************************************************************* + + ldv1000hle.h + + Pioneer LD-V4200 laserdisc player simulation. + +*************************************************************************/ + +#ifndef MAME_MACHINE_LDV4200_H +#define MAME_MACHINE_LDV4200_H + +#pragma once + +#include "laserdsc.h" +#include "diserial.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DECLARE_DEVICE_TYPE(PIONEER_LDV4200HLE, pioneer_ldv4200hle_device) + + + +//************************************************************************** +// GLOBAL HELPERS +//************************************************************************** + +// Note: This should be included within the class rather than the global namespace. +// However, doing so results in a "called in a constant expression before its definition is complete" error for the enum values. +static constexpr uint16_t make_ldv4000_command(const char *str) { return ((uint8_t)str[0] << 8) | (uint8_t)str[1]; } + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> pioneer_ldv4200hle_device + +class pioneer_ldv4200hle_device : public laserdisc_device, public device_serial_interface +{ +public: + // construction/destruction + pioneer_ldv4200hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + auto serial_tx() { return m_serial_tx.bind(); } + +protected: + // timer IDs + enum + { + TID_VBI_DATA_FETCH = TID_FIRST_PLAYER_TIMER + }; + + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param) override; + + // laserdisc overrides + virtual void player_vsync(const vbi_metadata &vbi, int fieldnum, const attotime &curtime) override; + virtual int32_t player_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime) override; + virtual void player_overlay(bitmap_yuy16 &bitmap) override { } + + // diserial overrides + virtual void rcv_complete() override; + virtual void tra_complete() override; + virtual void tra_callback() override; + +private: + enum player_command : uint16_t + { + CMD_DOOR_OPEN = make_ldv4000_command("OP"), + CMD_DOOR_CLOSE = make_ldv4000_command("CO"), + CMD_REJECT = make_ldv4000_command("RJ"), + CMD_START = make_ldv4000_command("SA"), + CMD_PLAY = make_ldv4000_command("PL"), + CMD_PAUSE = make_ldv4000_command("PA"), + CMD_STILL = make_ldv4000_command("ST"), + CMD_STEP_FORWARD = make_ldv4000_command("SF"), + CMD_STEP_REVERSE = make_ldv4000_command("SR"), + CMD_SCAN_FORWARD = make_ldv4000_command("NF"), + CMD_SCAN_REVERSE = make_ldv4000_command("NR"), + CMD_MULTISPEED_FORWARD = make_ldv4000_command("MF"), + CMD_MULTISPEED_REVERSE = make_ldv4000_command("MR"), + CMD_SPEED_SET = make_ldv4000_command("SP"), + CMD_SEARCH = make_ldv4000_command("SE"), + CMD_MULTITRACK_FORWARD = make_ldv4000_command("JF"), + CMD_MULTITRACK_REVERSE = make_ldv4000_command("JR"), + CMD_STOP_MARKER = make_ldv4000_command("SM"), + CMD_FRAME_SET = make_ldv4000_command("FR"), + CMD_TIME_SET = make_ldv4000_command("TM"), + CMD_CHAPTER_SET = make_ldv4000_command("CH"), + CMD_CLEAR = make_ldv4000_command("CL"), + CMD_LEADOUT_SYMBOL = make_ldv4000_command("LO"), + CMD_AUDIO_CTRL = make_ldv4000_command("AD"), + CMD_VIDEO_CTRL = make_ldv4000_command("VD"), + CMD_KEY_LOCK = make_ldv4000_command("KL"), + CMD_DISPLAY_CONTROL = make_ldv4000_command("DS"), + CMD_CLEAR_SCREEN = make_ldv4000_command("CS"), + CMD_PRINT_CHAR = make_ldv4000_command("PR"), + CMD_REQ_FRAME_NUMBER = make_ldv4000_command("?F"), + CMD_REQ_TIME_CODE = make_ldv4000_command("?T"), + CMD_REQ_CHAPTER_NUMBER = make_ldv4000_command("?C"), + CMD_REQ_PLAYER_MODE = make_ldv4000_command("?P"), + CMD_REQ_DISC_STATUS = make_ldv4000_command("?D"), + CMD_REQ_LDP_MODEL = make_ldv4000_command("?X"), + CMD_REQ_PIONEER_DISC_ID = make_ldv4000_command("?U"), + CMD_REQ_STANDARD_DISC_ID = make_ldv4000_command("$Y"), + CMD_REQ_TV_SYSTEM = make_ldv4000_command("?S"), + CMD_COMMUNICATION_CTRL = make_ldv4000_command("CM"), + CMD_REQ_CCR_MODE = make_ldv4000_command("?M"), + CMD_REGISTER_A_SET = make_ldv4000_command("RA"), + CMD_REGISTER_B_SET = make_ldv4000_command("RB"), + CMD_REGISTER_C_SET = make_ldv4000_command("RC"), + CMD_REGISTER_D_SET = make_ldv4000_command("RD"), + CMD_REQ_REGISTER_A = make_ldv4000_command("$A"), + CMD_REQ_REGISTER_B = make_ldv4000_command("$B"), + CMD_REQ_REGISTER_C = make_ldv4000_command("$C"), + CMD_REQ_REGISTER_D = make_ldv4000_command("$D"), + CMD_REQ_INPUT_UNIT = make_ldv4000_command("#I"), + CMD_INPUT_NUMBER_WAIT = make_ldv4000_command("?N") + }; + + enum player_mode : uint8_t + { + MODE_PARK, + MODE_DOOR_OPEN, + MODE_PAUSE, + MODE_PLAY, + MODE_MS_FORWARD, + MODE_MS_REVERSE, + MODE_SEARCH, + MODE_STILL + }; + + enum address_mode : uint8_t + { + ADDRESS_FRAME, + ADDRESS_TIME, + ADDRESS_CHAPTER + }; + + enum error_code : uint8_t + { + ERR_NONE = 0xff, + ERR_COMMUNICATION = 0, + ERR_NOT_AVAILABLE = 4, + ERR_MISSING_ARGUMENT = 6, + ERR_DISC_NOT_LOADED = 11, + ERR_SEARCH = 12, + ERR_DEFOCUS = 13, + ERR_PICTURE_STOP = 15, + ERR_OTHER_INPUT = 16, + ERR_PANIC = 99 + }; + + void add_command_byte(uint8_t data); + + void queue_reply(const char *reply); + void queue_error(error_code err); + + void normalize_command_buffer(); + void process_command_buffer(); + + static uint32_t bcd_to_literal(uint32_t bcd); + static bool is_number(char value); + uint8_t parse_numeric_value(uint8_t cmd_index, uint32_t &value, error_code &err); + uint8_t process_command(uint8_t cmd_index, uint32_t value, error_code &err); + + void begin_search(uint32_t value); + + void update_audio_squelch(); + void update_video_enable(); + + // internal state + devcb_write_line m_serial_tx; + emu_timer * m_vbi_fetch; + char m_cmd_buffer[21]; + uint8_t m_cmd_length; + bool m_cmd_running; + char m_reply_buffer[64]; + uint8_t m_reply_write_index; + uint8_t m_reply_read_index; + bool m_replying; + + uint8_t m_mode; // current player mode + uint32_t m_chapter; + uint32_t m_time; + uint32_t m_frame; // raw frame index (CAV mode) + uint32_t m_search_chapter; + uint32_t m_search_frame; + uint32_t m_mark_chapter; + uint32_t m_mark_frame; + + uint8_t m_key_lock; + uint8_t m_video_switch; + uint8_t m_audio_switch; + uint8_t m_display_switch; + uint8_t m_address_flag; + uint16_t m_speed; + uint32_t m_speed_accum; + uint8_t m_comm_ctrl; + uint8_t m_reg_a; + uint8_t m_reg_b; + uint8_t m_reg_c; + uint8_t m_reg_d; + uint8_t m_aux_port; + uint32_t m_curr_frame; +}; + +#endif // MAME_MACHINE_LDV4200_H diff --git a/src/mame/drivers/timetrv.cpp b/src/mame/drivers/timetrv.cpp index c05012458a7..a326c3d14d3 100644 --- a/src/mame/drivers/timetrv.cpp +++ b/src/mame/drivers/timetrv.cpp @@ -1,23 +1,20 @@ // license:BSD-3-Clause -// copyright-holders:Angelo Salese +// copyright-holders:Angelo Salese, Ryan Holtz /************************************************************************************************* (Hologram) Time Traveler (c) 1991 Virtual Image Productions / Sega -preliminary driver by Angelo Salese +Driver by Angelo Salese +LaserDisc and artwork hookup by Ryan Holtz TODO: -- unemulated Pioneer LDV-4200 and Sony LDP-1450 players, needs a dump of the BIOSes and proper - hook-up; -- ICM7243B 14-segment alphanumeric LED display driver +- Unemulated Sony LDP-1450 player, and Pioneer LD-V4200 is HLE; needs a dump of the BIOSes and + proper hook-up. +- Unknown how the bill validator is hooked up. ================================================================================================== -Time Traveler ROM image - -warren@dragons-lair-project.com -6/25/01 - +Time Traveler ROM image courtesy of Warren O of the Dragon's Lair Project, 25 Jun. 2001 ROM is a 27C020 (256kbit x 8 = 256 KB) ROM sticker says 6/18/91 @@ -31,79 +28,189 @@ CPU is an Intel 80188 #include "machine/eeprompar.h" #include "machine/i8255.h" #include "machine/ins8250.h" +#include "machine/ldv4200hle.h" #include "emupal.h" -#include "screen.h" +#include "speaker.h" + +#include "timetrv.lh" + +namespace { class timetrv_state : public driver_device { public: timetrv_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_led_vram_lo(*this, "led_vralo"), - m_led_vram_hi(*this, "led_vrahi"), - m_maincpu(*this, "maincpu") { } + : driver_device(mconfig, type, tag) + , m_led_vram_lo(*this, "led_vramlo") + , m_led_vram_hi(*this, "led_vramhi") + , m_maincpu(*this, "maincpu") + , m_uart(*this, "uart") + , m_laserdisc(*this, "laserdisc") + , m_digits(*this, "digit%u", 0U) + , m_decimals(*this, "decimal%u", 0U) + , m_cube_lamp(*this, "cube_lamp") + , m_player_lamps(*this, "player_lamp%u", 1U) + { } void timetrv(machine_config &config); private: - required_shared_ptr m_led_vram_lo; - required_shared_ptr m_led_vram_hi; - uint8_t test1_r(); - uint8_t test2_r(); - uint8_t in_r(); - virtual void video_start() override; - uint32_t screen_update_timetrv(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - required_device m_maincpu; - void timetrv_io(address_map &map); + virtual void machine_start() override; + void timetrv_map(address_map &map); -}; + void timetrv_io(address_map &map); + void ppi1_pc_w(uint8_t data); + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); -void timetrv_state::video_start() -{ -} + template void led_w(offs_t offset, uint8_t data); + + required_shared_ptr m_led_vram_lo; + required_shared_ptr m_led_vram_hi; -uint32_t timetrv_state::screen_update_timetrv(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) + required_device m_maincpu; + required_device m_uart; + required_device m_laserdisc; + output_finder<16> m_digits; + output_finder<16> m_decimals; + output_finder<> m_cube_lamp; + output_finder<2> m_player_lamps; +}; + +void timetrv_state::machine_start() { - popmessage("%s%s",reinterpret_cast(m_led_vram_lo.target()),reinterpret_cast(m_led_vram_hi.target())); - return 0; + m_digits.resolve(); + m_decimals.resolve(); + m_cube_lamp.resolve(); + m_player_lamps.resolve(); } -uint8_t timetrv_state::test1_r() +void timetrv_state::ppi1_pc_w(uint8_t data) { - return ioport("IN0")->read();//machine().rand(); + // Bit 3: 2P Start lamp + // Bit 4: 1P Start lamp + // Bit 5: Time Reversal Cube button-lamp + // Bit 6: Coin-up/start 'bip' noise + m_cube_lamp = BIT(data, 5); + m_player_lamps[0] = BIT(data, 4); + m_player_lamps[1] = BIT(data, 3); } -uint8_t timetrv_state::test2_r() +uint32_t timetrv_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { - /*bit 7,eeprom read bit*/ - return (ioport("IN1")->read() & 0x7f);//machine().rand(); + return 0; } - -uint8_t timetrv_state::in_r() +template +void timetrv_state::led_w(offs_t offset, uint8_t data) { - return 0xff; + /* + 000000000 + 5B 8 C1 + 5 B 8 C 1 + 5 B 8 C 1 + 6666 7777 + 4 A 9 D 2 + 4 A 9 D 2 + 4A 9 D2 + 333EEE333 + + 00: 0000 0001 1011 1011 0x01bb @ + 01: 0000 0000 1111 0111 0x00f7 A + 02: 0000 0011 1000 1111 0x038f B + 03: 0000 0000 0011 1001 0x0039 C + 04: 0000 0011 0000 1111 0x030f D + 05: 0000 0000 0111 1001 0x0079 E + 06: 0000 0000 0111 0001 0x0071 F + 07: 0000 0000 1011 1101 0x00bd G + 08: 0000 0000 1111 0110 0x00f6 H + 09: 0000 0011 0000 0000 0x0300 I + 0A: 0000 0000 0001 1110 0x001e J + 0B: 0011 0000 0111 0000 0x3070 K + 0C: 0000 0000 0011 1000 0x0038 L + 0D: 0001 1000 0011 0110 0x1836 M + 0E: 0010 1000 0011 0110 0x2836 N + 0F: 0000 0000 0011 1111 0x003f O + 10: 0000 0000 1111 0011 0x00f3 P + 11: 0010 0000 0011 1111 0x203f Q + 12: 0010 0000 1111 0011 0x20f3 R + 13: 0000 0000 1110 1101 0x00ed S + 14: 0000 0011 0000 0001 0x0301 T + 15: 0000 0000 0011 1110 0x003e U + 16: 0001 0100 0011 0000 0x1430 V + 17: 0010 0100 0011 0110 0x2436 W + 18: 0011 1100 0000 0000 0x3c00 X + 19: 0001 1010 0000 0000 0x1a00 Y + 1A: 0001 0100 0000 1001 0x1409 Z + 1B: 0011 0000 1000 0000 0x3080 [ + 1C: 0010 1000 0000 0000 0x2800 Backslash + 1D: 0000 1100 0100 0000 0x0c40 ] + 1E: 0001 0100 0000 0011 0x1403 Arrow + 1F: 0000 0000 0000 1000 0x0008 _ + 20: 0000 0000 0000 0000 0x0000 Space + 21: 0100 0001 0000 0000 0x4100 ! + 22: 0000 0000 0010 0010 0x0022 " + 23: 0000 0011 1100 1110 0x03ce # + 24: 0000 0011 1110 1101 0x03ed $ + 25: 0011 1100 1110 0100 0x3ce4 % + 26: 0011 1100 0000 1101 0x3c0d & + 27: 0000 0001 0000 0000 0x0100 ' + 28: 0011 0000 0000 0000 0x3000 ( + 29: 0000 1100 0000 0000 0x0c00 ) + 2A: 0011 1111 1100 0000 0x3fc0 * + 2B: 0000 0011 1100 0000 0x03c0 + + 2C: 0000 0100 0000 0000 0x0400 , + 2D: 0000 0000 1100 0000 0x00c0 - + 2E: 0100 0000 0000 0000 0x4000 . + 2F: 0001 0100 0000 0000 0x1400 / + 30: 0001 0100 0011 1111 0x143f 0 + 31: 0000 0011 0000 0000 0x0300 1 + 32: 0000 0000 1101 1011 0x00db 2 + 33: 0000 0000 1100 1111 0x00cf 3 + 34: 0000 0000 1110 0110 0x00e6 4 + 35: 0010 0000 0110 1001 0x2069 5 + 36: 0000 0000 1111 1101 0x00fd 6 + 37: 0000 0000 0000 0111 0x0007 7 + 38: 0000 0000 1111 1111 0x00ff 8 + 39: 0000 0000 1110 1111 0x00ef 9 + 3A: 0100 0000 0000 0000 0x4000 : + 3B: 0000 0100 0000 0000 0x0400 ; + 3C: 0000 0100 0000 1000 0x0408 < + 3D: 0000 0000 1100 1000 0x00c8 = + 3E: 0010 0000 0000 1000 0x2008 > + 3F: 0000 0010 1000 0011 0x0283 ? + */ + + static uint16_t const s_digit_data[0x40] = + { + 0x01bb, 0x00f7, 0x038f, 0x0039, 0x030f, 0x0079, 0x0071, 0x00bd, 0x00f6, 0x0300, 0x001e, 0x3070, 0x0038, 0x1836, 0x2836, 0x003f, + 0x00f3, 0x203f, 0x20f3, 0x00ed, 0x0301, 0x003e, 0x1430, 0x2436, 0x3c00, 0x1a00, 0x1409, 0x3080, 0x2800, 0x0c40, 0x1403, 0x0008, + 0x0000, 0x4100, 0x0022, 0x03ce, 0x03ed, 0x3ce4, 0x3c0d, 0x0100, 0x3000, 0x0c00, 0x3fc0, 0x03c0, 0x0400, 0x00c0, 0x4000, 0x1400, + 0x143f, 0x0300, 0x00db, 0x00cf, 0x00e6, 0x2069, 0x00fd, 0x0007, 0x00ff, 0x00ef, 0x4000, 0x0400, 0x0408, 0x00c8, 0x2008, 0x0283 + }; + + const uint16_t digit_data = s_digit_data[data & 0x3f]; + m_digits[Bank + offset] = digit_data & 0x3fff; + m_decimals[Bank + offset] = BIT(digit_data, 14); } void timetrv_state::timetrv_map(address_map &map) { - map(0x00000, 0x0ffff).ram(); //irq vectors + work ram + map(0x00000, 0x0ffff).ram(); // IRQ vectors + work RAM map(0x10000, 0x107ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write)); map(0xc0000, 0xfffff).rom(); } void timetrv_state::timetrv_io(address_map &map) { - map(0x0122, 0x0123).nopw(); //eeprom write bits map(0x1000, 0x1003).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x1080, 0x1083).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x1100, 0x1107).rw("uart", FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w)); - map(0x1180, 0x1187).ram().share("led_vralo");//led string,part 1 - map(0x1200, 0x1207).ram().share("led_vrahi");//led string,part 2 - map(0xff80, 0xffff).ram(); //am80188-em-like cpu internal regs? + map(0x1180, 0x1187).ram().w(FUNC(timetrv_state::led_w<0>)).share(m_led_vram_lo); + map(0x1200, 0x1207).ram().w(FUNC(timetrv_state::led_w<8>)).share(m_led_vram_hi); + map(0xff80, 0xffff).ram(); // CPU internal registers on 80188 } @@ -119,32 +226,77 @@ static INPUT_PORTS_START( timetrv ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_START("IN1") - PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) + PORT_BIT( 0x8f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Attack") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Reversal Cube") + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE ) + + PORT_START("DSW1") + PORT_DIPNAME( 0x03, 0x02, DEF_STR( Coinage ) ) PORT_DIPLOCATION("SW1:1,2") + PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) + PORT_DIPSETTING( 0x01, DEF_STR( 2C_1C ) ) + PORT_DIPSETTING( 0x02, DEF_STR( 3C_1C ) ) + PORT_DIPSETTING( 0x03, DEF_STR( 4C_1C ) ) + PORT_DIPNAME( 0x0c, 0x04, DEF_STR( Lives ) ) PORT_DIPLOCATION("SW1:3,4") + PORT_DIPSETTING( 0x00, "2" ) + PORT_DIPSETTING( 0x04, "3" ) + PORT_DIPSETTING( 0x08, "4" ) + PORT_DIPSETTING( 0x0c, "5" ) + PORT_DIPUNUSED_DIPLOC( 0xf0, 0x00, "SW1:5,6,7,8" ) + + PORT_START("DSW2") + PORT_DIPNAME( 0x01, 0x00, "Max Reversal Cubes" ) PORT_DIPLOCATION("SW2:1") + PORT_DIPSETTING( 0x00, "6" ) + PORT_DIPSETTING( 0x01, "36" ) + PORT_DIPNAME( 0x0e, 0x04, "Reversal Cube Cost" ) PORT_DIPLOCATION("SW2:2,3,4") + PORT_DIPSETTING( 0x00, "1 Coin / 1 Cube" ) + PORT_DIPSETTING( 0x02, "2 Coins / 1 Cube" ) + PORT_DIPSETTING( 0x04, "1 Coins / 2 Cubes" ) + PORT_DIPSETTING( 0x0a, "2 Coins / 3 Cubes" ) + PORT_DIPSETTING( 0x08, "1 Coin / 3 Cubes" ) + PORT_DIPSETTING( 0x0e, "1/2 Coins / 1/3 Cubes" ) + PORT_DIPSETTING( 0x0c, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x06, DEF_STR( Unknown ) ) + PORT_DIPNAME( 0x10, 0x00, DEF_STR( Level_Select ) ) PORT_DIPLOCATION("SW2:5") + PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x10, DEF_STR( On ) ) + PORT_DIPNAME( 0x60, 0x00, "Devil Behavior" ) PORT_DIPLOCATION("SW2:6,7") + PORT_DIPSETTING( 0x00, "Devil Can Take Lives" ) + PORT_DIPSETTING( 0x20, "Devil Never Takes Lives" ) + PORT_DIPSETTING( 0x40, "Devil Not In Game" ) + PORT_DIPSETTING( 0x60, DEF_STR( Unknown ) ) + PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW2:8" ) + + PORT_START("DSW3") + PORT_DIPNAME( 0x01, 0x01, DEF_STR( Free_Play ) ) PORT_DIPLOCATION("SW3:1") PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) - PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) ) - PORT_DIPSETTING( 0x04, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) - PORT_DIPSETTING( 0x08, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) - PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) - PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x40, 0x40, DEF_STR( Service_Mode ) ) - PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - // 0x80 eeprom read bit + PORT_DIPNAME( 0x02, 0x02, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("SW3:2") + PORT_DIPSETTING( 0x02, DEF_STR( On ) ) + PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) + PORT_DIPNAME( 0x0c, 0x0c, DEF_STR( Difficulty ) ) PORT_DIPLOCATION("SW3:4,3") + PORT_DIPSETTING( 0x0c, DEF_STR( Medium ) ) + PORT_DIPSETTING( 0x08, DEF_STR( Easy ) ) + PORT_DIPSETTING( 0x04, DEF_STR( Difficult ) ) + PORT_DIPSETTING( 0x0c, DEF_STR( Very_Difficult ) ) + PORT_DIPNAME( 0x10, 0x10, "LaserDisc Player Protocol" ) PORT_DIPLOCATION("SW3:5") + PORT_DIPSETTING( 0x10, "Pioneer LDV-4200" ) + PORT_DIPSETTING( 0x00, "Sony LDP-1450" ) + PORT_DIPNAME( 0xe0, 0x60, "Bill Multiplier" ) PORT_DIPLOCATION("SW3:6,7,8") + PORT_DIPSETTING( 0x00, "1" ) + PORT_DIPSETTING( 0x20, "2" ) + PORT_DIPSETTING( 0x60, "4" ) + PORT_DIPSETTING( 0xe0, "8" ) + PORT_DIPSETTING( 0x40, "10" ) + PORT_DIPSETTING( 0x80, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0xa0, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0xc0, DEF_STR( Unknown ) ) INPUT_PORTS_END void timetrv_state::timetrv(machine_config &config) { /* basic machine hardware */ - I80188(config, m_maincpu, 20000000); //??? + I80188(config, m_maincpu, 16_MHz_XTAL); // Confirmed from PCB layout diagram m_maincpu->set_addrmap(AS_PROGRAM, &timetrv_state::timetrv_map); m_maincpu->set_addrmap(AS_IO, &timetrv_state::timetrv_io); // interrupts are generated by internally-driven timers @@ -152,27 +304,28 @@ void timetrv_state::timetrv(machine_config &config) EEPROM_2816(config, "eeprom"); i8255_device &ppi1(I8255(config, "ppi1")); - ppi1.in_pa_callback().set(FUNC(timetrv_state::test1_r)); //inputs - ppi1.in_pb_callback().set(FUNC(timetrv_state::test2_r)); //eeprom read bit + inputs + ppi1.in_pa_callback().set_ioport("IN0"); + ppi1.in_pb_callback().set_ioport("IN1"); + ppi1.out_pc_callback().set(FUNC(timetrv_state::ppi1_pc_w)); i8255_device &ppi2(I8255(config, "ppi2")); - ppi2.in_pa_callback().set(FUNC(timetrv_state::in_r)); //dsw - ppi2.in_pb_callback().set(FUNC(timetrv_state::in_r)); //dsw - ppi2.in_pc_callback().set(FUNC(timetrv_state::in_r)); //dsw + ppi2.in_pa_callback().set_ioport("DSW1"); + ppi2.in_pb_callback().set_ioport("DSW2"); + ppi2.in_pc_callback().set_ioport("DSW3"); - NS16450(config, "uart", 1843200); // P82050 (serial interface for laserdisc) + NS16450(config, m_uart, 768000); // P82050 (serial interface for Laserdisc player) + m_uart->out_tx_callback().set(m_laserdisc, FUNC(pioneer_ldv4200hle_device::rx_w)); /* video hardware */ - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(60); - screen.set_vblank_time(ATTOSECONDS_IN_USEC(0)); - screen.set_size(512, 512); - screen.set_visarea(0*8, 512-1, 0*8, 512-1); - screen.set_screen_update(FUNC(timetrv_state::screen_update_timetrv)); - - PALETTE(config, "palette").set_entries(512); + PIONEER_LDV4200HLE(config, m_laserdisc, 0); + m_laserdisc->set_overlay(256, 256, FUNC(timetrv_state::screen_update)); + m_laserdisc->add_route(0, "mono", 0.4); + m_laserdisc->add_route(1, "mono", 0.4); + m_laserdisc->add_ntsc_screen(config, "screen"); + m_laserdisc->serial_tx().set(m_uart, FUNC(ns16450_device::rx_w)); /* sound hardware */ + SPEAKER(config, "mono").front_center(); } /*************************************************************************** @@ -186,7 +339,7 @@ ROM_START( timetrv ) ROM_LOAD( "tt061891.bin", 0xc0000, 0x40000, CRC(a3d44219) SHA1(7c5003b6d3df1e472db45abd725e7d3d43f0dfb4) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "timetrv", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "timetrv", 0, SHA1(8abb5e6aa58ab49477ef89f507264d35454f99d3) ) ROM_END ROM_START( timetrv2 ) @@ -194,8 +347,12 @@ ROM_START( timetrv2 ) ROM_LOAD( "epr-72491.u9", 0xc0000, 0x40000, CRC(c7998e2f) SHA1(26060653b2368f52c304e6433b4f447f99a36839) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "timetrv", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "timetrv", 0, SHA1(8abb5e6aa58ab49477ef89f507264d35454f99d3) BAD_DUMP ) ROM_END -GAME( 1991, timetrv, 0, timetrv, timetrv, timetrv_state, empty_init, ROT0, "Virtual Image Productions (Sega license)", "Time Traveler (set 1)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) -GAME( 1991, timetrv2, timetrv, timetrv, timetrv, timetrv_state, empty_init, ROT0, "Virtual Image Productions (Sega license)", "Time Traveler (set 2)", MACHINE_NO_SOUND | MACHINE_NOT_WORKING ) // Europe? +} // anonymous namespace + + + +GAMEL( 1991, timetrv, 0, timetrv, timetrv, timetrv_state, empty_init, ORIENTATION_FLIP_Y, "Virtual Image Productions (Sega license)", "Time Traveler (set 1)", MACHINE_IMPERFECT_SOUND, layout_timetrv ) +GAMEL( 1991, timetrv2, timetrv, timetrv, timetrv, timetrv_state, empty_init, ORIENTATION_FLIP_Y, "Virtual Image Productions (Sega license)", "Time Traveler (set 2)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND, layout_timetrv ) // Different frame indices; Europe? diff --git a/src/mame/layout/timetrv.lay b/src/mame/layout/timetrv.lay new file mode 100644 index 00000000000..cad88c53a3c --- /dev/null +++ b/src/mame/layout/timetrv.lay @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3