From c8c09a8c88ca41e8478ac22a0269fcdad43b5cc9 Mon Sep 17 00:00:00 2001 From: James Wallace Date: Wed, 26 Jun 2024 01:12:12 +0100 Subject: cops.cpp: Promote Revelations to working (#12485) * LDP1450: Added HLE of player and hookups to some games that use it * LDP1450: Tweaked timings * LDP1450 - added some more comms, and text overlay logging * cops.cpp: Fixed loose input that stopped Nova games from booting * cops.cpp: Add Revelations support * LDP1450: Added multibyte command support * Revelations: Add SHA1 for disc image (needs redump) --- scripts/src/machine.lua | 24 + src/devices/machine/ldp1450hle.cpp | 1006 ++++++++++++++++++++++++++++++++++++ src/devices/machine/ldp1450hle.h | 200 +++++++ src/devices/machine/r65c52.cpp | 987 +++++++++++++++++++++++++++++++++++ src/devices/machine/r65c52.h | 250 +++++++++ src/mame/atari/cops.cpp | 792 +++++++++------------------- src/mame/layout/cops.lay | 2 +- src/mame/layout/revlatns.lay | 136 +++++ src/mame/sega/timetrv.cpp | 2 +- 9 files changed, 2852 insertions(+), 547 deletions(-) create mode 100644 src/devices/machine/ldp1450hle.cpp create mode 100644 src/devices/machine/ldp1450hle.h create mode 100644 src/devices/machine/r65c52.cpp create mode 100644 src/devices/machine/r65c52.h create mode 100644 src/mame/layout/revlatns.lay diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index ace32673389..7814da83e12 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2082,6 +2082,18 @@ if (MACHINES["LDP1450"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/ldp1450hle.h,MACHINES["LDP1450HLE"] = true +--------------------------------------------------- + +if (MACHINES["LDP1450HLE"]~=null) then + files { + MAME_DIR .. "src/devices/machine/ldp1450hle.cpp", + MAME_DIR .. "src/devices/machine/ldp1450hle.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/ldvp931.h,MACHINES["LDVP931"] = true @@ -3035,6 +3047,18 @@ if (MACHINES["R10788"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/r65c52.h,MACHINES["R65C52"] = true +--------------------------------------------------- + +if (MACHINES["R65C52"]~=null) then + files { + MAME_DIR .. "src/devices/machine/r65c52.cpp", + MAME_DIR .. "src/devices/machine/r65c52.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/ra17xx.h,MACHINES["RA17XX"] = true diff --git a/src/devices/machine/ldp1450hle.cpp b/src/devices/machine/ldp1450hle.cpp new file mode 100644 index 00000000000..6b569c7a5ee --- /dev/null +++ b/src/devices/machine/ldp1450hle.cpp @@ -0,0 +1,1006 @@ +// 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) +{ + 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); + } + else + { + } +} + + +//------------------------------------------------- +// 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() +{ + set_video_squelch(!(m_video_switch == 1 && (m_mode == MODE_STILL || m_mode == MODE_PLAY || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE))); + video_enable(m_video_switch == 1 && (m_mode == MODE_STILL || m_mode == MODE_PLAY || m_mode == MODE_MS_FORWARD || m_mode == MODE_MS_REVERSE)); +} + + +//------------------------------------------------- +// 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)); + } + } + } + + + 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; + } + + 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; + if (reverse) + { + begin_search(m_repeat_frame_end); + } + else{ + begin_search(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)) + { + int32_t jump_frame = (int32_t)m_curr_frame + elapsed_tracks; + int32_t curr_delta = (int32_t)m_mark_frame - (int32_t)m_curr_frame; + 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) + { + 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()); +} diff --git a/src/devices/machine/ldp1450hle.h b/src/devices/machine/ldp1450hle.h new file mode 100644 index 00000000000..dd7d11239bd --- /dev/null +++ b/src/devices/machine/ldp1450hle.h @@ -0,0 +1,200 @@ +// license:BSD-3-Clause + +/************************************************************************* + + ldp1450hle.h + + +*************************************************************************/ + +#ifndef MAME_MACHINE_LDP1450_H +#define MAME_MACHINE_LDP1450_H + +#pragma once + +#include "laserdsc.h" +#include "diserial.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DECLARE_DEVICE_TYPE(SONY_LDP1450HLE, sony_ldp1450hle_device) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sony_ldp1450hle_device + +class sony_ldp1450hle_device : public laserdisc_device, public device_serial_interface +{ +public: + // construction/destruction + sony_ldp1450hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + auto serial_tx() { return m_serial_tx.bind(); } + + void set_baud(s32 clock) { m_baud = clock; } + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // laserdisc overrides + virtual void player_vsync(const vbi_metadata &vbi, int fieldnum, const attotime &curtime) override; + virtual s32 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; + + + TIMER_CALLBACK_MEMBER(process_vbi_data); + TIMER_CALLBACK_MEMBER(process_queue); + +private: + enum player_command : u16 + { + CMD_AUDIO_OFF =0x24, + CMD_AUDIO_ON =0x25, + CMD_VIDEO_OFF =0x26, + CMD_VIDEO_ON =0x27, + CMD_PSC_ON =0x28, + CMD_PSC_OFF =0x29, + CMD_EJECT =0x2a, + CMD_STEP_STILL =0x2b, + CMD_STEP_STILL_REVERSE =0x2c, + CMD_PLAY =0x3a, + CMD_FAST_FORWARD =0x3b, + CMD_SLOW_FORWARD =0x3c, + CMD_STEP_FORWARD =0x3d, + CMD_SCAN_FORWARD =0x3e, + CMD_STOP =0x3f, + CMD_ENTER =0x40, + CMD_CLEAR =0x41, + CMD_MENU =0x42, + CMD_SEARCH =0x43, + CMD_REPEAT =0x44, + CMD_CH1_ON =0x46, + CMD_CH1_OFF =0x47, + CMD_CH2_ON =0x48, + CMD_CH2_OFF =0x49, + CMD_PLAY_REVERSE =0x4a, + CMD_FAST_REVERSE =0x4b, + CMD_SLOW_REVERSE =0x4c, + CMD_STEP_REVERSE =0x4d, + CMD_SCAN_REVERSE =0x4e, + CMD_STILL =0x4f, + CMD_INDEX_ON =0x50, + CMD_INDEX_OFF =0x51, + CMD_FRAME_SET =0x55, + CMD_CLEAR_ALL =0x56, + CMD_ADDR_INQ =0x60, + CMD_STATUS_INQ =0x67, + CMD_CHAPTER_SET =0x69, + CMD_USER_INDEX_CTRL =0x80, + CMD_USER_INDEX_ON =0x81, + CMD_USER_INDEX_OFF =0x82, + }; + + enum player_mode : u8 + { + MODE_PARK, + MODE_DOOR_OPEN, + MODE_PAUSE, + MODE_PLAY, + MODE_MS_FORWARD, + MODE_MS_REVERSE, + MODE_SEARCH, + MODE_SEARCH_CMD, + MODE_SEARCH_REP, + MODE_SEARCH_CL, + MODE_REPEAT_CMD_MARK, + MODE_REPEAT_CMD_REPS, + MODE_STILL, + SUBMODE_NORMAL, + SUBMODE_USER_INDEX, + SUBMODE_USER_INDEX_MODE_1, + SUBMODE_USER_INDEX_MODE_2, + SUBMODE_USER_INDEX_MODE_3, + SUBMODE_USER_INDEX_STRING_1, + SUBMODE_USER_INDEX_STRING_2, + SUBMODE_USER_INDEX_WINDOW, + + }; + + enum address_mode : u8 + { + ADDRESS_FRAME, + ADDRESS_CHAPTER + }; + + + void queue_reply(u8 reply, float delay); + void queue_reply_buffer (const u8 reply[], float delay); + + static u32 bcd_to_literal(u32 bcd); + static bool is_number(char value); + void add_command_byte(u8 command); + + void begin_search(u32 value); + + void update_audio_squelch(); + void update_video_enable(); + + // internal state + devcb_write_line m_serial_tx; + emu_timer * m_vbi_fetch; + emu_timer * m_queue_timer; + bool m_cmd_running; + u8 m_reply_buffer[64]; + + u8 m_reply_write_index; + u8 m_reply_read_index; + u8 m_reply; + + u8 m_mode; // current player mode + u8 m_submode; + u32 m_baud; + u32 m_chapter; + u32 m_time; + u32 m_frame; // raw frame index (CAV mode) + u32 m_search_chapter; + u32 m_search_frame; + s32 m_cmd_buffer; + u32 m_mark_chapter; + u32 m_mark_frame; + u32 m_repeat_chapter_start; + u32 m_repeat_chapter_end; + u32 m_repeat_frame_start; + u32 m_repeat_frame_end; + u32 m_repeat_repetitions; + + u8 m_video_switch; + bool m_ch1_switch; + bool m_ch2_switch; + u8 m_display_switch; + u8 m_address_flag; + u16 m_base_speed; + u16 m_speed; + u32 m_speed_accum; + u32 m_curr_frame; + + u8 m_user_index_x; + u8 m_user_index_y; + u8 m_user_index_mode; + u8 m_user_index_char_idx; + u8 m_user_index_window_idx; + char m_user_index_chars[32]; + +}; + +#endif // MAME_MACHINE_LDP1450_H diff --git a/src/devices/machine/r65c52.cpp b/src/devices/machine/r65c52.cpp new file mode 100644 index 00000000000..76d16ff6bc5 --- /dev/null +++ b/src/devices/machine/r65c52.cpp @@ -0,0 +1,987 @@ +// license:BSD-3-Clause +// copyright-holders:jwallace +/********************************************************************** + + Rockwell 65C52 Dual Asynchronous Communication Interface Adapter + + A slightly tweaked combination of two 6551 ACIAs on a single chip + +**********************************************************************/ + +#include "emu.h" +#include "r65c52.h" + +#define VERBOSE 0 +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(R65C52, r65c52_device, "r65c52", "Rockwell 65C52 DACIA") + +r65c52_device::r65c52_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, R65C52, tag, owner, clock), + m_internal_clock1(*this, "clock1"), + m_internal_clock2(*this, "clock2"), + m_irq_handler(*this), + m_txd_handler(*this), + m_rxc_handler(*this), + m_rts_handler(*this), + m_dtr_handler(*this), + m_aux_ctrl{0, 0}, m_control{0, 0}, + m_compare{0, 0}, + m_format{0, 0}, m_status{0, 0}, + m_tdr{0, 0}, m_tdre{true, true}, + m_rdr{0, 0}, m_rdrf{false, false}, + m_ier{0, 0}, m_isr{0, 0}, + m_irq{0, 0}, + m_overrun{false, false}, + m_parity_err{false, false}, + m_parity_err_mode{0, 0}, + m_txd{0, 0}, + m_rxc(0), + m_rts{0, 0}, + m_dtr{0, 0}, + m_xtal(clock), + m_divide{0, 0}, + m_cts{0, 0}, + m_dsr{0, 0}, + m_dcd{0, 0}, + m_rxd{0, 0}, m_wordlength{0, 0}, m_stoplength{0, 0}, m_brk{0, 0}, m_echo_mode{0, 0}, m_parity{0, 0}, + m_rx_state{STATE_START, STATE_START}, + m_rx_clock{0, 0}, m_rx_bits{0, 0}, m_rx_shift{0, 0}, m_rx_parity{0, 0}, + m_rx_counter{0, 0}, + m_tx_state{STATE_START, STATE_START}, + m_tx_output{OUTPUT_MARK, OUTPUT_MARK}, + m_tx_clock{0, 0}, m_tx_bits{0, 0}, m_tx_shift{0, 0}, m_tx_parity{0, 0}, + m_tx_counter{0, 0} +{ +} + +const int r65c52_device::internal_divider[16] = + { + 4608, + 2096, + 1713, + 1536, + 768, + 384, + 192, + 128, + 96, + 64, + 48, + 32, + 24, + 12, + 6, + 1}; + +void r65c52_device::device_add_mconfig(machine_config &config) +{ + CLOCK(config, m_internal_clock1, 0); + CLOCK(config, m_internal_clock2, 0); + m_internal_clock1->signal_handler().set(FUNC(r65c52_device::internal_clock1)); + m_internal_clock2->signal_handler().set(FUNC(r65c52_device::internal_clock2)); +} + +void r65c52_device::device_start() +{ + // state saving + save_item(NAME(m_aux_ctrl)); + save_item(NAME(m_compare)); + save_item(NAME(m_control)); + save_item(NAME(m_format)); + save_item(NAME(m_status)); + save_item(NAME(m_tdr)); + save_item(NAME(m_tdre)); + save_item(NAME(m_rdr)); + save_item(NAME(m_rdrf)); + + save_item(NAME(m_ier)); + save_item(NAME(m_isr)); + save_item(NAME(m_irq)); + + save_item(NAME(m_overrun)); + save_item(NAME(m_parity_err)); + save_item(NAME(m_parity_err_mode)); + + save_item(NAME(m_txd)); + save_item(NAME(m_rxc)); + save_item(NAME(m_rts)); + save_item(NAME(m_dtr)); + + save_item(NAME(m_xtal)); + save_item(NAME(m_divide)); + save_item(NAME(m_cts)); + save_item(NAME(m_dsr)); + save_item(NAME(m_dcd)); + save_item(NAME(m_rxd)); + + save_item(NAME(m_wordlength)); + save_item(NAME(m_stoplength)); + save_item(NAME(m_brk)); + save_item(NAME(m_echo_mode)); + save_item(NAME(m_parity)); + + save_item(NAME(m_rx_state)); + save_item(NAME(m_rx_clock)); + save_item(NAME(m_rx_bits)); + save_item(NAME(m_rx_shift)); + save_item(NAME(m_rx_parity)); + save_item(NAME(m_rx_counter)); + + save_item(NAME(m_tx_state)); + save_item(NAME(m_tx_output)); + save_item(NAME(m_tx_clock)); + save_item(NAME(m_tx_bits)); + save_item(NAME(m_tx_shift)); + save_item(NAME(m_tx_parity)); + + save_item(NAME(m_tx_counter)); + + m_internal_clock1->set_unscaled_clock(m_xtal); + m_internal_clock2->set_unscaled_clock(m_xtal); + + set_xtal(m_xtal); +} + +void r65c52_device::device_reset() +{ + for (int i = 0; i < 2; i++) + { + m_tdre[i] = true; + m_rdrf[i] = true; + + m_status[i] = SR_FRAMING_ERROR; + + m_aux_ctrl[i] = 0; + m_compare[i] = 0; + m_rdr[i] = 0x00; + m_isr[i] = 0x80; + m_ier[i] = 0x80; + + m_rts[i] = 1; + m_dtr[i] = 1; + + output_rts(i, 1); + + output_dtr(i, 1); + + m_rx_state[i] = STATE_START; + m_rx_counter[i] = 0; + } +} + +u8 r65c52_device::stoplengthcounter(int idx) +{ + if (m_stoplength[idx] == 2) + { + if (m_wordlength[idx] == 5 && m_parity[idx] == PARITY_NONE) + { + return m_divide[idx] + (m_divide[idx] / 2); + } + + if (m_wordlength[idx] < 8 || m_parity[idx] == PARITY_NONE) + { + return m_divide[idx] * 2; + } + } + + return m_divide[idx]; +} + +void r65c52_device::output_irq(int idx, int irq) +{ + LOG("R65C52: %x IRQ %x \n", idx + 1, irq); + + if (m_irq[idx] != irq) + { + m_irq[idx] = irq; + + m_irq_handler[idx](!m_irq[idx]); + } +} + +void r65c52_device::output_txd(int idx, int txd) +{ + switch (m_tx_output[idx]) + { + case OUTPUT_MARK: + txd = 1; + break; + + case OUTPUT_BREAK: + txd = 0; + break; + } + + if (m_txd[idx] != txd) + { + m_txd[idx] = txd; + m_txd_handler[idx](m_txd[idx]); + } +} + +void r65c52_device::output_rts(int idx, int rts) +{ + LOG("R65C52: %x RTS %x \n", idx + 1, rts); + if (m_rts[idx] != rts) + { + m_rts[idx] = rts; + m_rts_handler[idx](m_rts[idx]); + if (rts) + { + m_status[idx] |= SR_RTS; + } + else + { + m_status[idx] &= ~SR_RTS; + } + } +} + +void r65c52_device::output_dtr(int idx, int dtr) +{ + LOG("R65C52: %x DTR %x \n", idx + 1, dtr); + if (m_dtr[idx] != dtr) + { + m_dtr[idx] = dtr; + m_dtr_handler[idx](m_dtr[idx]); + if (dtr) + { + m_status[idx] |= SR_DTR; + } + else + { + m_status[idx] &= ~SR_DTR; + } + } +} + +void r65c52_device::update_irq(int idx) +{ + bool irq = false; + LOG("R65C52: %x IER %x ISR %x\n", idx + 1, m_ier[idx], m_isr[idx]); + for (int i = 0; i < 8; i++) + { + if ((m_ier[idx] & (1 >> i)) && ((m_isr[idx] & (1 >> i)))) + { + irq = true; + } + } + + output_irq(idx, irq); +} + +void r65c52_device::update_divider(int idx) +{ + // bits 0-3 + double scale = internal_divider[(m_control[idx] >> 0) & 0xf]; + + if (m_xtal != 0) + { + m_divide[idx] = 16; + if (!m_dtr[idx] || m_rx_state[idx] != STATE_START) + { + scale = (double)1 / scale; + } + else + { + scale = 0; + } + + LOG("R65C52: %x CLOCK %d SCALE %f \n", idx + 1, m_xtal * scale, scale); + } + else + { + m_divide[idx] = scale * 16; + scale = 0; + } + + if (idx == 0) + { + m_internal_clock1->set_clock_scale(scale); + } + else + { + m_internal_clock2->set_clock_scale(scale); + } +} + +u8 r65c52_device::read_rdr(int idx) +{ + m_status[idx] &= ~(SR_BRK | SR_FRAMING_ERROR); + m_isr[idx] &= ~(IRQ_PAR | IRQ_FOB | IRQ_RDRF); + m_rdrf[idx] = false; + m_parity_err[idx] = false; + m_overrun[idx] = false; + update_irq(idx); + LOG("R65C52: %x RDR %x \n", idx + 1, m_rdr[idx]); + return m_rdr[idx]; +} + +u8 r65c52_device::read_status(int idx) +{ + LOG("R65C52: %x STATUS %x \n", idx + 1, m_status[idx]); + m_dtr[idx] = false; + m_rts[idx] = false; + return m_status[idx]; +} + +void r65c52_device::write_ier(int idx, u8 data) +{ + if (data & 0x80) + { + m_ier[idx] |= (data & 0x7f); + } + else + { + m_ier[idx] &= ~(data & 0x7f); + } + + LOG("R65C52: %x IER %x \n", idx + 1, m_ier[idx]); +} + +void r65c52_device::write_tdr(int idx, u8 data) +{ + m_tdr[idx] = data; + m_tdre[idx] = false; + m_isr[idx] &= ~IRQ_TDRE; + LOG("R65C52: %x TDR %x \n", idx + 1, m_tdr[idx]); +} + +void r65c52_device::write_control(int idx, u8 data) +{ + m_control[idx] = data; + + // bits 0-3 + update_divider(idx); + + // bit 4 + m_echo_mode[idx] = (m_control[idx] >> 4) & 1; + + // bit 5 + m_stoplength[idx] = 1 + ((m_control[idx] >> 5) & 1); + + LOG("R65C52: %x CTRL%X ECHO %x STOP%x\n", idx + 1, m_control[idx], m_echo_mode[idx], m_stoplength[idx]); +} + +void r65c52_device::write_format(int idx, u8 data) +{ + m_format[idx] = data; + + // bit 0 + output_rts(idx, ((m_format[idx] >> 0) & 1)); + + // bit 1 + output_dtr(idx, ((m_format[idx] >> 1) & 1)); + + // bit 2 + if (!((m_format[idx] >> 2) & 1)) + { + m_parity[idx] = PARITY_NONE; + } + else + { + // bits 3-4 + m_parity[idx] = (m_format[idx] >> 3) & 3; + } + + // bits 5-6 + m_wordlength[idx] = 5 + ((m_format[idx] >> 5) & 3); + + LOG("R65C52: %x FMT %x RTS %x DTR %x PARITY %x WORDLENGTH %x \n", idx + 1, m_format[idx], (m_format[idx] >> 0) & 1, (m_format[idx] >> 1) & 1, m_parity[idx], m_wordlength[idx]); + + update_divider(idx); +} + +void r65c52_device::write_aux_ctrl(int idx, u8 data) +{ + m_aux_ctrl[idx] = data; + + // bit 0 + m_parity_err_mode[idx] = (m_format[idx] >> 0) & 1; + + // bit 1 + m_brk[idx] = (m_format[idx] >> 1) & 1; + + LOG("R65C52: %x AUX CTRL %x \n", idx + 1, m_aux_ctrl[idx]); +} + +void r65c52_device::write_compare(int idx, u8 data) +{ + LOG("R65C52: %x COMPARE %x \n", idx + 1, data); + m_compare[idx] = data; +} + +u8 r65c52_device::read_isr(int idx) +{ + + if (m_status[idx] & SR_BRK || m_status[idx] & SR_FRAMING_ERROR || m_overrun[idx]) + { + m_isr[idx] |= IRQ_FOB; + } + + u8 isr = m_isr[idx]; + + if (isr != 0) + { + isr |= 0x80; + } + if (!m_echo_mode[idx]) + { + if (m_cts[idx]) + { + isr |= 0x80; + } + } + else + { + isr &= ~0x80; + } + + m_isr[idx] &= ~(IRQ_CTS | IRQ_DCD | IRQ_DSR | IRQ_FOB); + + LOG("R65C52: %x ISR %x \n", idx + 1, m_isr[idx]); + + return isr; +} + +void r65c52_device::map(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(r65c52_device::isr_0_r), FUNC(r65c52_device::ier_0_w)); + map(0x01, 0x01).rw(FUNC(r65c52_device::status_0_r), FUNC(r65c52_device::format_ctrl_0_w)); + map(0x02, 0x02).nopr().w(FUNC(r65c52_device::aux_compare_0_w)); + map(0x03, 0x03).rw(FUNC(r65c52_device::rdr_0_r), FUNC(r65c52_device::tdr_0_w)); + map(0x04, 0x04).rw(FUNC(r65c52_device::isr_1_r), FUNC(r65c52_device::ier_1_w)); + map(0x05, 0x05).rw(FUNC(r65c52_device::status_1_r), FUNC(r65c52_device::format_ctrl_1_w)); + map(0x06, 0x06).nopr().w(FUNC(r65c52_device::aux_compare_1_w)); + map(0x07, 0x07).rw(FUNC(r65c52_device::rdr_1_r), FUNC(r65c52_device::tdr_1_w)); +} + +void r65c52_device::format_ctrl_0_w(u8 data) +{ + if (data & 0x80) + { + write_format(0, data); + } + else + { + write_control(0, data); + } +} + +void r65c52_device::format_ctrl_1_w(u8 data) +{ + if (data & 0x80) + { + write_format(1, data); + } + else + { + write_control(1, data); + } +} + +void r65c52_device::aux_compare_0_w(u8 data) +{ + if (data & 0x40) + { + write_aux_ctrl(0, data); + } + else + { + write_compare(0, data); + } +} + +void r65c52_device::aux_compare_1_w(u8 data) +{ + if (data & 0x40) + { + write_aux_ctrl(1, data); + } + else + { + write_compare(1, data); + } +} + +void r65c52_device::set_xtal(u32 xtal) +{ + m_xtal = xtal; + + if (started()) + { + m_internal_clock1->set_unscaled_clock(m_xtal); + m_internal_clock2->set_unscaled_clock(m_xtal); + update_divider(0); + update_divider(1); + } +} + +void r65c52_device::internal_clock1(int state) +{ + transmitter_clock(0, state); + receiver_clock(0, state); +} + +void r65c52_device::internal_clock2(int state) +{ + transmitter_clock(1, state); + receiver_clock(1, state); +} + +void r65c52_device::write_rxc(int state) +{ + for (int i = 0; i < 2; i++) + { + receiver_clock(i, state); + } +} + +void r65c52_device::write_txc(int state) +{ + for (int i = 0; i < 2; i++) + { + transmitter_clock(i, state); + } +} + +void r65c52_device::write_rxd1(int state) +{ + m_rxd[0] = state; +} + +void r65c52_device::write_rxd2(int state) +{ + m_rxd[1] = state; +} + +void r65c52_device::write_cts1(int state) +{ + _write_cts(0, state); +} + +void r65c52_device::write_cts2(int state) +{ + _write_cts(1, state); +} + +void r65c52_device::_write_cts(int idx, int state) +{ + if (m_cts[idx] != state) + { + m_cts[idx] = state; + m_isr[idx] |= IRQ_CTS; + + if (m_cts[idx]) + { + m_status[idx] |= SR_CTS; + + if (m_tx_output[idx] == OUTPUT_TXD) + { + m_tx_output[idx] = OUTPUT_MARK; + output_txd(idx, 1); + } + } + else + { + m_status[idx] &= ~SR_CTS; + } + update_irq(idx); + } + LOG("R65C52: %x CTS STATUS %x \n", idx + 1, m_status[idx]); +} + +void r65c52_device::write_dsr1(int state) +{ + _write_dsr(0, state); +} + +void r65c52_device::write_dsr2(int state) +{ + _write_dsr(1, state); +} + +void r65c52_device::_write_dsr(int idx, int state) +{ + if (m_dsr[idx] != state) + { + m_dsr[idx] = state; + m_isr[idx] |= IRQ_DSR; + + if (m_dsr[idx]) + { + m_status[idx] |= SR_DSR; + } + else + { + m_status[idx] &= ~SR_DSR; + } + update_irq(idx); + } + LOG("R65C52: %x DSR STATUS %x \n", idx + 1, m_status[idx]); +} + +void r65c52_device::write_dcd1(int state) +{ + _write_dcd(0, state); +} + +void r65c52_device::write_dcd2(int state) +{ + _write_dcd(1, state); +} + +void r65c52_device::_write_dcd(int idx, int state) +{ + if (m_dcd[idx] != state) + { + m_dcd[idx] = state; + m_isr[idx] |= IRQ_DCD; + + if (m_dcd[idx]) + { + m_status[idx] |= SR_DCD; + } + else + { + m_status[idx] &= ~SR_DCD; + } + update_irq(idx); + } + LOG("R65C52: %x DCD STATUS %x \n", idx + 1, m_status[idx]); +} + +void r65c52_device::receiver_clock(int idx, int state) +{ + if (m_rx_clock[idx] != state) + { + m_rx_clock[idx] = state; + + if (m_rx_clock[idx]) + { + m_rx_counter[idx]++; + + switch (m_rx_state[idx]) + { + case STATE_START: + if (m_rx_counter[idx] == 1) + { + if (!m_rxd[idx] && !m_dtr[idx]) + { + LOG("R65C52: RX%x START BIT \n", idx + 1); + } + else + { + m_rx_counter[idx] = 0; + } + } + + if (m_rx_counter[idx] >= m_divide[idx] / 2) + { + if (!m_rxd[idx]) + { + m_rx_state[idx] = STATE_DATA; + m_rx_counter[idx] = 0; + m_rx_shift[idx] = 0; + m_rx_parity[idx] = 0; + m_rx_bits[idx] = 0; + } + else + { + m_rx_counter[idx] = 0; + + LOG("R65C52: RX%x false START BIT\n", idx + 1); + } + } + break; + + case STATE_DATA: + if (m_rx_counter[idx] == m_divide[idx]) + { + m_rx_counter[idx] = 0; + + if (m_rx_bits[idx] < m_wordlength[idx]) + { + LOG("R65C52: RX%x DATA BIT %d %d\n", idx + 1, m_rx_bits[idx], m_rxd[idx]); + } + else + { + LOG("R65C52: RX%x PARITY BIT %x\n", idx + 1, m_rxd[idx]); + } + + if (m_rxd[idx]) + { + m_rx_shift[idx] |= 1 << m_rx_bits[idx]; + } + + m_rx_bits[idx]++; + + m_rx_parity[idx] ^= m_rxd[idx]; + + if ((m_rx_bits[idx] == m_wordlength[idx] && m_parity[idx] == PARITY_NONE) || + (m_rx_bits[idx] == (m_wordlength[idx] + 1) && m_parity[idx] != PARITY_NONE)) + { + m_rx_state[idx] = STATE_STOP; + } + } + break; + + case STATE_STOP: + if (m_rx_counter[idx] >= stoplengthcounter(idx)) + { + m_rx_counter[idx] = 0; + + LOG("R65C52: RX%x STOP BIT\n", idx + 1); + if (!(m_rdrf[idx])) + { + if (!m_rxd[idx]) + { + m_status[idx] |= SR_FRAMING_ERROR; + } + + if ((m_parity[idx] == PARITY_ODD && !m_rx_parity[idx]) || + (m_parity[idx] == PARITY_EVEN && m_rx_parity[idx])) + { + m_parity_err[idx] = true; + } + else + { + m_parity_err[idx] = false; + } + + if (m_parity_err_mode[idx]) + { + if (m_parity_err[idx]) + { + m_isr[idx] |= IRQ_PAR; + } + else + { + m_isr[idx] &= ~IRQ_PAR; + } + update_irq(idx); + } + m_rdr[idx] = m_rx_shift[idx]; + + if (m_wordlength[idx] == 7 && m_parity[idx] != PARITY_NONE) + { + m_rdr[idx] &= 0x7f; + } + + // In compare mode, we only flip RDRF if the data matches + if (m_compare[idx] == 0 || (m_compare[idx] == m_rdr[idx])) + { + m_rdrf[idx] = true; + m_isr[idx] |= IRQ_RDRF; + m_compare[idx] = 0; + update_irq(idx); + } + m_overrun[idx] = false; + } + else + { + m_overrun[idx] = true; + } + + LOG("R65C52: RX%x DATA %x\n", idx + 1, m_rdr[idx]); + + m_rx_state[idx] = STATE_START; + + if (m_dtr[idx]) + { + update_divider(idx); + } + } + break; + } + } + } +} + +void r65c52_device::transmitter_clock(int idx, int state) +{ + if (m_tx_clock[idx] != state) + { + m_tx_clock[idx] = state; + + if (!m_tx_clock[idx] && !m_dtr[idx]) + { + if (m_echo_mode[idx]) + { + if (!(m_overrun[idx])) + { + output_txd(idx, m_rxd[idx]); + } + else + { + output_txd(idx, 1); + } + } + + if (!m_cts[idx] && m_tx_output[idx] == OUTPUT_MARK && !(m_tdre[idx])) + { + m_tx_state[idx] = STATE_START; + m_tx_counter[idx] = 0; + } + + m_tx_counter[idx]++; + + switch (m_tx_state[idx]) + { + case STATE_START: + { + m_tx_counter[idx] = 0; + + m_tx_state[idx] = STATE_DATA; + m_tx_shift[idx] = m_tdr[idx]; + m_tx_bits[idx] = 0; + m_tx_parity[idx] = 0; + + if (m_cts[idx]) + { + m_tx_output[idx] = OUTPUT_MARK; + LOG("R65C52: TX%x CTS MARK START\n", idx + 1); + } + else if (!(m_tdre[idx])) + { + LOG("R65C52: TX%x DATA %x\n", idx + 1, m_tdr[idx]); + + m_tx_output[idx] = OUTPUT_TXD; + + LOG("R65C52: TX%x START BIT\n", idx + 1); + + m_tdre[idx] = true; + m_isr[idx] |= IRQ_TDRE; + update_irq(idx); + } + else if (m_brk[idx]) + { + m_tx_output[idx] = OUTPUT_BREAK; + + LOG("R65C52: TX%x BREAK START\n", idx + 1); + } + else + { + m_tx_output[idx] = OUTPUT_MARK; + LOG("R65C52: TX%x MARK START\n", idx + 1); + } + + if (m_tx_output[idx] != OUTPUT_BREAK) + { + m_tdre[idx] = true; + m_isr[idx] |= IRQ_TDRE; + update_irq(idx); + } + + // fatalerror("setup"); + output_txd(idx, 0); + break; + } + case STATE_DATA: + { + if (m_tx_counter[idx] == m_divide[idx]) + { + m_tx_counter[idx] = 0; + + if (m_tx_bits[idx] < m_wordlength[idx]) + { + output_txd(idx, (m_tx_shift[idx] >> m_tx_bits[idx]) & 1); + + m_tx_bits[idx]++; + m_tx_parity[idx] ^= m_txd[idx]; + + if (m_tx_output[idx] == OUTPUT_TXD) + { + LOG("R65C52: TX%x DATA BIT TXD %d %d\n", idx + 1, m_tx_bits[idx], m_txd[idx]); + } + } + else if (m_tx_bits[idx] == m_wordlength[idx] && m_parity[idx] != PARITY_NONE) + { + m_tx_bits[idx]++; + + switch (m_parity[idx]) + { + case PARITY_ODD: + m_tx_parity[idx] = !m_tx_parity[idx]; + break; + + case PARITY_MARK: + m_tx_parity[idx] = 1; + break; + + case PARITY_SPACE: + m_tx_parity[idx] = 0; + break; + } + + output_txd(idx, m_tx_parity[idx]); + + if (m_tx_output[idx] == OUTPUT_TXD) + { + LOG("R65C52: TX%x PARITY BIT %d\n", idx + 1, m_txd); + } + + if (!m_parity_err_mode[idx]) + { + if (m_tx_parity[idx]) + { + m_isr[idx] |= IRQ_PAR; + } + else + { + m_isr[idx] &= ~IRQ_PAR; + } + update_irq(idx); + } + } + else + { + m_tx_state[idx] = STATE_STOP; + + output_txd(idx, 1); + // m_tdre[idx] = true; + // m_isr[idx] |= IRQ_TDRE; + // update_irq(idx); + + if (m_tx_output[idx] == OUTPUT_TXD) + { + LOG("R65C52: TX%x STOP BIT\n", idx + 1); + } + } + } + break; + } + + case STATE_STOP: + { + if (m_tx_counter[idx] >= stoplengthcounter(idx)) + { + if (m_tx_output[idx] == OUTPUT_BREAK) + { + if (!m_brk[idx]) + { + LOG("R65C52: TX%x BREAK END\n", idx + 1); + + m_tx_counter[idx] = 0; + m_tx_state[idx] = STATE_STOP; + m_tx_output[idx] = OUTPUT_TXD; + + output_txd(idx, 1); + } + else + { + m_tx_counter[idx]--; + } + } + else + { + m_tx_state[idx] = STATE_START; + m_tx_counter[idx] = 0; + } + } + break; + } + } + } + } +} \ No newline at end of file diff --git a/src/devices/machine/r65c52.h b/src/devices/machine/r65c52.h new file mode 100644 index 00000000000..a8a1635f9c5 --- /dev/null +++ b/src/devices/machine/r65c52.h @@ -0,0 +1,250 @@ +// license:BSD-3-Clause +// copyright-holders:jwallace +/********************************************************************** + + Rockwell 65C52 Dual Asynchronous Communication Interface Adapter + + A slightly tweaked combination of two 6551 ACIAs on a single chip + +********************************************************************** + _____ _____ + RES 1 |* \_/ | 40 Vcc + NC 2 | | 39 _CS + XTALI 3 | | 38 R/_W + XTALO 4 | | 37 RS2 + CLKOUT 5 | | 36 RS1 + NC 6 | | 35 RS0 + _DSR2 7 | | 34 NC + _DCD2 8 | | 33 _DSR1 + _CTS2 9 | | 32 _DCD1 + _RTS2 10 | R65C52 | 31 _CTS1 + _IRQ2 11 | | 30 _RTS1 + RxD2 12 | | 29 _IRQ1 + _DTR2 13 | | 28 RxD1 + TxD2 14 | | 27 _DTR1 + TxC 15 | | 26 TxD1 + D7 16 | | 25 RxC + D6 17 | | 24 D0 + D5 18 | | 23 D1 + D4 19 | | 22 D2 + Vss 20 |_____________| 21 D3 + + + +**********************************************************************/ + +#ifndef MAME_MACHINE_R65C52_H +#define MAME_MACHINE_R65C52_H + +#pragma once + +#include "machine/clock.h" + +class r65c52_device : public device_t +{ +public: + r65c52_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + void map(address_map &map); + + auto irq1_handler() { return m_irq_handler[0].bind(); } + auto irq2_handler() { return m_irq_handler[1].bind(); } + auto txd1_handler() { return m_txd_handler[0].bind(); } + auto txd2_handler() { return m_txd_handler[1].bind(); } + auto rts1_handler() { return m_rts_handler[0].bind(); } + auto rts2_handler() { return m_rts_handler[1].bind(); } + auto dtr1_handler() { return m_dtr_handler[0].bind(); } + auto dtr2_handler() { return m_dtr_handler[1].bind(); } + + u8 read(offs_t offset); + + void write_txc(int state); // txc + void write_rxc(int state); + void write_rxd1(int state); + void write_rxd2(int state); + + void write_cts1(int state); + void write_cts2(int state); + void write_dsr1(int state); + void write_dsr2(int state); + void write_dcd1(int state); + void write_dcd2(int state); + + void _write_cts(int idx, int state); + void _write_dsr(int idx, int state); + void _write_dcd(int idx, int state); + + void set_xtal(u32 clock); + void set_xtal(const XTAL &clock) { set_xtal(clock.value()); } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + enum + { + SR_RTS = 0x01, + SR_DTR = 0x02, + SR_BRK = 0x04, + SR_DSR = 0x08, + SR_DCD = 0x10, + SR_CTS = 0x20, + SR_TUR = 0x40, + SR_FRAMING_ERROR = 0x80, + }; + + enum + { + PARITY_ODD = 0, + PARITY_EVEN = 1, + PARITY_MARK = 2, + PARITY_SPACE = 3, + PARITY_NONE = -1 + }; + + enum + { + IRQ_RDRF= 0x01, + IRQ_FOB = 0x02, + IRQ_PAR = 0x04, + IRQ_DSR = 0x08, + IRQ_DCD = 0x10, + IRQ_CTS = 0x20, + IRQ_TDRE= 0x40, + }; + + enum + { + STATE_START, + STATE_DATA, + STATE_STOP + }; + + enum + { + OUTPUT_TXD, + OUTPUT_MARK, + OUTPUT_BREAK + }; + + u8 stoplengthcounter(int idx); + + void output_irq(int idx, int irq); + void output_txd(int idx, int txd); + void output_rts(int idx, int rts); + void output_dtr(int idx, int dtr); + + void update_irq(int idx); + void update_divider(int idx); + + u8 read_isr(int idx); + u8 read_rdr(int idx); + u8 read_status(int idx); + u8 read_command(int idx); + + + void ier_0_w(u8 data) { write_ier(0, data); } + void ier_1_w(u8 data) { write_ier(1, data); } + + u8 isr_0_r() { return read_isr(0); } + u8 isr_1_r() { return read_isr(1); } + + u8 rdr_0_r() { return read_rdr(0); } + u8 rdr_1_r() { return read_rdr(1); } + + u8 status_0_r() { return read_status(0); } + u8 status_1_r() { return read_status(1); } + + void tdr_0_w(u8 data) { write_tdr(0, data); } + void tdr_1_w(u8 data) { write_tdr(1, data); } + + void write_ier(int idx, u8 data); + void write_tdr(int idx, u8 data); + + void aux_compare_0_w(u8 data); + void aux_compare_1_w(u8 data); + + void format_ctrl_0_w(u8 data); + void format_ctrl_1_w(u8 data); + + void write_aux_ctrl(int idx, u8 data); + void write_compare(int idx, u8 data); + void write_control(int idx, u8 data); + void write_format(int idx, u8 data); + void isr_bit_set(int idx); + + void internal_clock1(int state); + void internal_clock2(int state); + void receiver_clock(int idx, int state); + void transmitter_clock(int idx, int state); + + static const int internal_divider[16]; + static const int transmitter_controls[4][3]; + + required_device m_internal_clock1; + required_device m_internal_clock2; + devcb_write_line::array<2> m_irq_handler; + devcb_write_line::array<2> m_txd_handler; + devcb_write_line m_rxc_handler; + devcb_write_line::array<2> m_rts_handler; + devcb_write_line::array<2> m_dtr_handler; + + + u8 m_aux_ctrl[2]; + u8 m_control[2]; + u8 m_compare[2]; + u8 m_format[2]; + u8 m_status[2]; + u8 m_tdr[2]; + bool m_tdre[2]; + u8 m_rdr[2]; + bool m_rdrf[2]; + + u8 m_ier[2]; + u8 m_isr[2]; + + u8 m_irq[2]; + + bool m_overrun[2]; + bool m_parity_err[2]; + u8 m_parity_err_mode[2]; + + u8 m_txd[2]; + u8 m_rxc; + u8 m_rts[2]; + u8 m_dtr[2]; + + u32 m_xtal; + u8 m_divide[2]; + u8 m_cts[2]; + u8 m_dsr[2]; + u8 m_dcd[2]; + u8 m_rxd[2]; + + u8 m_wordlength[2]; + u8 m_stoplength[2]; + u8 m_brk[2]; + u8 m_echo_mode[2]; + int m_parity[2]; + + u8 m_rx_state[2]; + u8 m_rx_clock[2]; + u8 m_rx_bits[2]; + u8 m_rx_shift[2]; + int m_rx_parity[2]; + u8 m_rx_counter[2]; + + u8 m_tx_state[2]; + u8 m_tx_output[2]; + u8 m_tx_clock[2]; + u8 m_tx_bits[2]; + u8 m_tx_shift[2]; + int m_tx_parity[2]; + u8 m_tx_counter[2]; +}; + +DECLARE_DEVICE_TYPE(R65C52, r65c52_device) + +#endif // MAME_MACHINE_R65C52_H diff --git a/src/mame/atari/cops.cpp b/src/mame/atari/cops.cpp index 6f051c9ef7e..4d300b023cf 100644 --- a/src/mame/atari/cops.cpp +++ b/src/mame/atari/cops.cpp @@ -3,28 +3,32 @@ /*************************************************************************** Nova Laserdisc Games/Atari Games COPS - (hardware developed by Nova Productions Limited) + (hardware developed by Nova Productions Limited, should we move it from Atari?) Preliminary driver by Mariusz Wojcieszek, James Wallace Cops uses a Sony CD-ROM in addition to the regular setup, purely to play Bad Boys by Inner Circle, so there is musical accompaniment to areas where the laserdisc audio is muted. - The different games here have subtly different control PCBs COPS has an Atari + The different games here have subtly different control PCBs. COPS has an Atari part number (58-12B), while Revelations simply refers to a Lasermax control PCB - (Lasermax being the consumer name for the LDP series) + (Lasermax being the consumer name for the LDP series). + COPS, Street Viper and all other Nova Productions laserdisc games run on forms of this + hardware. - NOTES: To boot up Revelations, turn the refill key (R) and press button A + NOTES: To first boot up Revelations, turn the refill key (R) and press button A to init NVRAM. TODO: There are probably more ROMs for Revelations and related, the disc - contains full data for a picture based memory game called 'Vision Quest'. - However, the Vision Quest Laserdisc is slightly different, with Revelations - specific data seemingly replaced with black level. + contains full data for a non-payout US release of the game called 'Vision Quest'. + However, the Vision Quest Laserdisc for the USA is slightly different, with + Revelations specific data seemingly replaced with black level. + We have a disc image for Vision Quest in full, but no ROM + + BTANB for Revelations: + Game options cannot be adjusted, any attempt to do so resets the machine (seen on real hardware) + Volume control cycles in the 'Stereo' test, but cannot be tested. - The UK version COPS appears to want to communicate with the LDP in a - different way, passing illegal commands, need to verify this is the same - player. This should be similar hardware for Street Viper if we get a dump. ***************************************************************************/ @@ -34,28 +38,31 @@ #include "cpu/m6502/m6502.h" #include "machine/6522via.h" -#include "machine/ldp1450.h" -#include "machine/mos6551.h" +#include "machine/ldp1450hle.h" +#include "machine/r65c52.h" #include "machine/msm6242.h" +#include "machine/nvram.h" #include "sound/sn76496.h" +#include "machine/watchdog.h" + +#include "machine/bacta_datalogger.h" +#include "machine/meters.h" #include "speaker.h" #include "cops.lh" +#include "revlatns.lh" #define LOG_CDROM (1U << 1) -#define LOG_DACIA (1U << 2) -#define VERBOSE (LOG_CDROM | LOG_DACIA) +#define VERBOSE (LOG_CDROM) #include "logmacro.h" namespace { -#define CMP_REGISTER 0 -#define AUX_REGISTER 1 - -#define MAIN_CLOCK XTAL(4'000'000) +#define MAIN_CLOCK 4_MHz_XTAL +#define DACIA_CLOCK 3.6864_MHz_XTAL class cops_state : public driver_device { @@ -63,22 +70,19 @@ public: cops_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") + , m_nvram(*this, "nvram") , m_sn(*this, "snsnd") , m_ld(*this, "laserdisc") + , m_dacia(*this, "dacia") + , m_watchdog(*this, "watchdog") + , m_meters(*this, "meters") , m_switches(*this, "SW%u", 0U) , m_steer(*this, "STEER") , m_digits(*this, "digit%u", 0U) - , m_offroad_right_lamp(*this, "Offroad Right %u Lamp", 1U) - , m_offroad_left_lamp(*this, "Offroad Left %u Lamp", 1U) - , m_damage_lamp(*this, "Damage Lamp") - , m_stop_lamp(*this, "Stop Lamp") - , m_gun_active_right_lamp(*this, "Gun Active Right Lamp") - , m_gun_active_left_lamp(*this, "Gun Active Left Lamp") - , m_vest_hit_lamp(*this, "Vest Hit %u Lamp", 1U) - , m_flash_red_lamp(*this, "Flash Red Lamp") - , m_flash_blue_lamp(*this, "Flash Blue Lamp") - , m_bullet_lamp(*this, "Bullet Lamp %u", 1U) + , m_lamps(*this, "lamp%u", 0U) , m_irq(0) + , m_acia1_irq(0) + , m_acia2_irq(0) { } void revlatns(machine_config &config); @@ -99,134 +103,55 @@ protected: private: // devices required_device m_maincpu; + required_shared_ptr m_nvram; required_device m_sn; - required_device m_ld; + required_device m_ld; + required_device m_dacia; + required_device m_watchdog; + optional_device m_meters; + required_ioport_array<3> m_switches; optional_ioport m_steer; output_finder<16> m_digits; - output_finder<4> m_offroad_right_lamp; - output_finder<4> m_offroad_left_lamp; - output_finder<> m_damage_lamp; - output_finder<> m_stop_lamp; - output_finder<> m_gun_active_right_lamp; - output_finder<> m_gun_active_left_lamp; - output_finder<3> m_vest_hit_lamp; - output_finder<> m_flash_red_lamp; - output_finder<> m_flash_blue_lamp; - output_finder<6> m_bullet_lamp; - + output_finder<23> m_lamps; // screen updates - [[maybe_unused]] uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + void io1_cops_w(offs_t offset, uint8_t data); void io1_w(offs_t offset, uint8_t data); + uint8_t io1_cops_r(offs_t offset); uint8_t io1_r(offs_t offset); - uint8_t io1_lm_r(offs_t offset); void io2_w(offs_t offset, uint8_t data); uint8_t io2_r(offs_t offset); - void dacia_irq(int state); - [[maybe_unused]] void ld_w(int state); + void acia1_irq(int state); + void acia2_irq(int state); + void dacia_irq(); void via1_irq(int state); void via2_irq(int state); - void dacia_receive(uint8_t data); - void update_dacia_irq(); - void dacia_w(offs_t offset, uint8_t data); - uint8_t dacia_r(offs_t offset); + void via1_a_w(uint8_t data); + void via1_a_revlatns_w(uint8_t data); void via1_b_w(uint8_t data); void via1_cb1_w(uint8_t data); void cdrom_data_w(uint8_t data); void cdrom_ctrl_w(uint8_t data); uint8_t cdrom_data_r(); - int m_irq; + int m_irq, m_acia1_irq, m_acia2_irq; uint8_t m_lcd_addr_l, m_lcd_addr_h; uint8_t m_lcd_data_l, m_lcd_data_h; - uint8_t m_dacia_irq1_reg; - uint8_t m_dacia_rts1; - uint8_t m_dacia_dtr1; - - uint8_t m_parity_1; - uint8_t m_parity_mode_1; - uint8_t m_bpc_1; - int m_dacia_ic_div_1; - uint8_t m_dacia_echo1; - uint8_t m_dacia_stp_1; - uint8_t m_dacia_reg1; - uint8_t m_dacia_fe1; - uint8_t m_dacia_cmp1; - uint8_t m_dacia_cmpval1; - - uint8_t m_dacia_irq2_reg; - uint8_t m_dacia_rts2; - uint8_t m_dacia_dtr2; - - uint8_t m_parity_2; - uint8_t m_parity_mode_2; - uint8_t m_bpc_2; - int m_dacia_ic_div_2; - uint8_t m_dacia_echo2; - uint8_t m_dacia_stp_2; - uint8_t m_dacia_reg2; - uint8_t m_dacia_fe2; - uint8_t m_dacia_cmp2; - uint8_t m_dacia_cmpval2; - - uint8_t m_dacia_cts; - uint8_t m_dacia_dcd; - uint8_t m_dacia_trans; - uint8_t m_dacia_trans2; - - uint8_t m_dacia_receiver_data; - uint8_t m_dacia_receiver_full; - - uint8_t m_dacia_receiver_data2; - uint8_t m_dacia_receiver_full2; - uint8_t m_cdrom_ctrl; uint8_t m_cdrom_data; uint8_t m_sn_data; uint8_t m_sn_cb1; - - // LDP-1450 - emu_timer *m_ld_timer; - TIMER_CALLBACK_MEMBER(ld_timer_callback); - - uint8_t m_ld_command_to_send[8]; - uint8_t m_ld_command_current_byte; - uint8_t m_ldcount=0; - uint8_t m_lddata; - uint8_t generate_isr(); - uint8_t generate_isr2(); -// void laserdisc_w(uint8_t data); - void laserdisc_response_w(uint8_t data); -}; - -const int timer_divide_select[16] = -{ - 73728, - 33538, - 27408, - 24576, - 12288, - 6144, - 3072, - 2048, - 1536, - 1024, - 768, - 512, - 384, - 192, - 96, - 16 }; void cops_state::video_start() { } -uint32_t cops_state::screen_update( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect ) +uint32_t cops_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { return 0; } @@ -258,359 +183,42 @@ uint8_t cops_state::cdrom_data_r() LOGMASKED(LOG_CDROM, "%s:cdrom_data_r(reg = %s)\n", machine().describe_context(), regs[reg & 0x03]); return machine().rand()&0xff; } -/************************************* - * - * LDP-1450 Laserdisc - * - *************************************/ - -TIMER_CALLBACK_MEMBER(cops_state::ld_timer_callback) -{ - m_dacia_receiver_full = 1; - int m_ld_command_total_bytes =8; - - if ( m_ld_command_current_byte < m_ld_command_total_bytes ) - { - dacia_receive(m_ld_command_to_send[m_ld_command_current_byte]); - m_ld_command_current_byte++; - if ( m_ld_command_current_byte == m_ld_command_total_bytes ) - { - m_ld_command_current_byte = m_ld_command_total_bytes = 0; - } - } -} - -void cops_state::ld_w(int state) -{ - m_lddata <<= 1; - - if ( state ) m_lddata |= 1; - - if ( ++m_ldcount >= 8 ) - { - m_ldcount = 0; - m_lddata = 0; - printf("LDBYTE %d", m_lddata); - } - - printf("LDBIT %d",state); -} -/*void cops_state::laserdisc_w(uint8_t data) -{ - switch( m_ld_input_state ) - { - case LD_INPUT_TEXT_GET_X: - m_ld_input_state = LD_INPUT_TEXT_GET_Y; - break; - case LD_INPUT_TEXT_GET_Y: - m_ld_input_state = LD_INPUT_TEXT_GET_MODE; - break; - case LD_INPUT_TEXT_GET_MODE: - m_ld_input_state = LD_INPUT_GET_COMMAND; - break; - case LD_INPUT_TEXT_GET_SET_WINDOW: - m_ld_input_state = LD_INPUT_GET_COMMAND; - break; - case LD_INPUT_TEXT_GET_STRING: - if ( data == 0x1a ) - { - m_ld_input_state = LD_INPUT_GET_COMMAND; - } - break; - } - break; - } -}*/ /************************************* * - * 6552 DACIA + * 6552 DACIA - IRQs are inverted * *************************************/ -void cops_state::update_dacia_irq() -{ - uint8_t isr = generate_isr(); - //remove bits - isr &= ~m_dacia_irq1_reg; - m_maincpu->set_input_line(INPUT_LINE_NMI, isr? ASSERT_LINE:CLEAR_LINE); - - uint8_t isr2 = generate_isr2(); - //remove bits - isr2 &= ~m_dacia_irq2_reg; - m_maincpu->set_input_line(INPUT_LINE_NMI, isr2? ASSERT_LINE:CLEAR_LINE); - -} - -void cops_state::dacia_receive(uint8_t data) -{ - if (m_dacia_cmp1) - { - if (m_dacia_cmpval1 == data) - { - m_dacia_receiver_data = data; - m_dacia_receiver_full = 1; - update_dacia_irq(); - m_dacia_cmp1 =0; - m_dacia_cts =1; - m_dacia_trans =1; - } - } - else - { - m_dacia_receiver_data = data; - m_dacia_receiver_full = 1; - update_dacia_irq(); - m_dacia_cts =1; - m_dacia_trans =1; - } -} - -uint8_t cops_state::generate_isr() +void cops_state::acia1_irq(int state) { - uint8_t isr =0; - - isr |= m_dacia_receiver_full; - isr |= (m_dacia_cmp1 << 1); - isr |= (m_dacia_trans <<4); - - if (isr) - { - isr |= 0x40; - } - return isr; + m_acia1_irq=!state; + dacia_irq(); } -uint8_t cops_state::generate_isr2() +void cops_state::acia2_irq(int state) { - uint8_t isr2 =0; - - isr2 |= m_dacia_receiver_full2; - isr2 |= (m_dacia_cmp2 << 1); - isr2 |= (m_dacia_trans2 <<4); - - if (isr2) - { - isr2 |= 0x40; - } - return isr2; + m_acia2_irq=!state; + dacia_irq(); } -uint8_t cops_state::dacia_r(offs_t offset) +void cops_state::dacia_irq() { - switch(offset & 0x07) - { - case 0: /* ISR1: Interrupt Status Register */ - { - uint8_t isr = generate_isr(); - m_dacia_trans =0; - m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); - return isr; - } - - case 1: /* CSR1: Control Status Register */ - { - uint8_t csr =0; - csr |= m_dacia_rts1; - csr |= (m_dacia_dtr1 << 1); - csr |= (m_dacia_cts <<4); - csr |= (m_dacia_fe1 <<7); - LOGMASKED(LOG_DACIA, "CSR1 %02x\n",csr); - return csr; - } - - case 3: /* RDR1: Receive data register */ - { - m_dacia_receiver_full = 0; - m_dacia_fe1=0; - -// LOGMASKED(LOG_DACIA, "RDR1 %02x\n",m_dacia_receiver_data); - LOGMASKED(LOG_DACIA, "RDR1 %02x\n",m_ld->status_r()); - return m_ld->status_r(); - } - case 4: /* ISR2: Interrupt Status Register */ - { - uint8_t isr2 = generate_isr2(); - m_dacia_trans2 =0; - m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); - return isr2; - } - - case 5: /* CSR2: Control Status Register */ - { - uint8_t csr2 =0; - csr2 |= m_dacia_rts2; - csr2 |= (m_dacia_dtr2 << 1); - csr2 |= (m_dacia_cts <<4); - csr2 |= (m_dacia_fe2 <<7); - LOGMASKED(LOG_DACIA, "CSR2 %02x\n",csr2); - return csr2; - } - - case 7: /* RDR2: Receive data register */ - m_dacia_receiver_full2 = 0; - m_dacia_fe2=0; - - LOGMASKED(LOG_DACIA, "RDR2 %02x\n",m_ld->status_r()); - return m_ld->status_r(); - - - default: - LOGMASKED(LOG_DACIA, "%s:dacia_r(%02x)\n", machine().describe_context(), offset); - return 0; - } + m_maincpu->set_input_line(INPUT_LINE_NMI, m_acia1_irq | m_acia2_irq? ASSERT_LINE:CLEAR_LINE); } -void cops_state::dacia_w(offs_t offset, uint8_t data) -{ - switch(offset & 0x07) - { - case 0: /* IRQ enable Register 1 */ - m_dacia_irq1_reg &= ~0x80; - - if (data & 0x80) //enable bits - { - m_dacia_irq1_reg |= (data & 0x7f); - } - else // disable bits - { - m_dacia_irq1_reg &= ~(data & 0x7f); - } - LOGMASKED(LOG_DACIA, "DACIA IRQ 1 Register: %02x\n", m_dacia_irq1_reg); - update_dacia_irq(); - break; - - case 1: /* Control / Format Register 1 */ - if (data & 0x80) //Format Register - { - m_dacia_rts1 = (data & 0x01); - m_dacia_dtr1 = (data & 0x02 ? 1:0); - m_parity_1 = (data & 0x04); - m_parity_mode_1 = ((data & 0x18) >> 3); - m_bpc_1 = ((data & 0x60) >> 5) +5; - LOGMASKED(LOG_DACIA, "DACIA Format Register: %02x\n", data); - } - else // Control register - { - m_dacia_ic_div_1 = timer_divide_select[data & 0x15]; - m_dacia_echo1 = (data & 0x10); - m_dacia_stp_1 = (data & 0x20 ? 2:1); - if (data & 0x40) - { - m_dacia_reg1 = AUX_REGISTER; - } - else - { - m_dacia_reg1 = CMP_REGISTER; - } - LOGMASKED(LOG_DACIA, "DACIA TIME %02d\n", (XTAL(3'686'400) / m_dacia_ic_div_1).value()); - -// m_ld_timer->adjust(attotime::from_hz(XTAL(3'686'400) / m_dacia_ic_div_1), 0, attotime::from_hz(XTAL(3'686'400) / m_dacia_ic_div_1)); - - LOGMASKED(LOG_DACIA, "DACIA Ctrl Register: %02x\n", data); - - } - break; - - case 2: /* Compare / Aux Ctrl Register 1 */ - if (m_dacia_reg1 == CMP_REGISTER) - { - m_dacia_cmp1 = 1; - m_dacia_cmpval1 = data; - LOGMASKED(LOG_DACIA, "DACIA Compare mode: %02x \n", data); -// update_dacia_irq(); - } - else - { - LOGMASKED(LOG_DACIA, "DACIA Aux ctrl: %02x \n", data); - } - [[fallthrough]]; // FIXME: really? - case 3: /* Transmit Data Register 1 */ - LOGMASKED(LOG_DACIA, "DACIA Transmit: %02x %c\n", data, (char)data); - m_ld->command_w(data); - break; - - case 4: /* IRQ enable Register 2 */ - m_dacia_irq2_reg &= ~0x80; - - if (data & 0x80) //enable bits - { - m_dacia_irq2_reg |= (data & 0x7f); - } - else // disable bits - { - m_dacia_irq2_reg &= ~(data & 0x7f); - } - LOGMASKED(LOG_DACIA, "DACIA IRQ 2 Register: %02x\n", m_dacia_irq2_reg); - update_dacia_irq(); - break; - - case 5: /* Control / Format Register 2 */ - if (data & 0x80) //Format Register - { - m_dacia_rts2 = (data & 0x01); - m_dacia_dtr2 = (data & 0x02 ? 1:0); - m_parity_2 = (data & 0x04); - m_parity_mode_2 = ((data & 0x18) >> 3); - m_bpc_2 = ((data & 0x60) >> 5) +5; - LOGMASKED(LOG_DACIA, "DACIA Format Register 2: %02x\n", data); - } - else // Control register - { - m_dacia_ic_div_2 = timer_divide_select[data & 0x15]; - m_dacia_echo2 = (data & 0x10); - m_dacia_stp_2 = (data & 0x20 ? 2:1); - if (data & 0x40) - { - m_dacia_reg2 = AUX_REGISTER; - } - else - { - m_dacia_reg2 = CMP_REGISTER; - } - LOGMASKED(LOG_DACIA, "DACIA TIME 2 %02d\n", (XTAL(3'686'400) / m_dacia_ic_div_1).value()); - - m_ld_timer->adjust(attotime::from_hz(XTAL(3'686'400) / m_dacia_ic_div_2), 0, attotime::from_hz(XTAL(3'686'400) / m_dacia_ic_div_2)); - - LOGMASKED(LOG_DACIA, "DACIA Ctrl Register 2: %02x\n", data); - - } - break; - - case 6: /* Compare / Aux Ctrl Register 2 */ - if (m_dacia_reg2 == CMP_REGISTER) - { - m_dacia_cmp2 =1; - m_dacia_cmpval2=data; - LOGMASKED(LOG_DACIA, "DACIA Compare mode 2: %02x \n", data); -// update_dacia_irq(); - } - else - { - LOGMASKED(LOG_DACIA, "DACIA Aux ctrl 2: %02x \n", data); - } - [[fallthrough]]; // FIXME: really? - case 7: /* Transmit Data Register 2 */ - LOGMASKED(LOG_DACIA, "DACIA Transmit 2: %02x %c\n", data, (char)data); - - // for (int i=0; i <8; i++) - { - // m_ld_command_to_send[i] = data & (1<command_w(data); - break; - } -} /************************************* * * I/O * *************************************/ -uint8_t cops_state::io1_r(offs_t offset) +uint8_t cops_state::io1_cops_r(offs_t offset) { switch( offset & 0x0f ) { + case 0x07: /* WOP7 - watchdog*/ + return 1; case 0x08: /* SW0 */ return m_switches[0]->read(); case 0x09: /* SW1 */ @@ -623,11 +231,13 @@ uint8_t cops_state::io1_r(offs_t offset) } } -uint8_t cops_state::io1_lm_r(offs_t offset) +uint8_t cops_state::io1_r(offs_t offset) { switch( offset & 0x0f ) { - case 0x07: /* WDI */ + case 0x01: /* SW0 */ + return m_switches[0]->read(); + case 0x07: /* WOP7 - watchdog*/ return 1; case 0x08: /* SW0 */ return m_switches[0]->read(); @@ -641,7 +251,7 @@ uint8_t cops_state::io1_lm_r(offs_t offset) } } -void cops_state::io1_w(offs_t offset, uint8_t data) +void cops_state::io1_cops_w(offs_t offset, uint8_t data) { switch (offset & 0x0f) { @@ -675,27 +285,113 @@ void cops_state::io1_w(offs_t offset, uint8_t data) break; case 0x04: /* WOP4 */ for (int i = 3; i >= 0; i--) - m_offroad_right_lamp[i] = BIT(data, i + 4); + m_lamps[i + 0x4] = BIT(data, i + 4); //Offroad right 1 - 4 for (int i = 3; i >= 0; i--) - m_offroad_left_lamp[i] = BIT(data, i); + m_lamps[i] = BIT(data, i); // Offroad left 1 - 4 break; case 0x05: /* WOP5 */ - m_damage_lamp = BIT(data, 7); - m_stop_lamp = BIT(data, 6); - m_gun_active_right_lamp = BIT(data, 5); - m_vest_hit_lamp[1] = BIT(data, 4); - m_vest_hit_lamp[2] = BIT(data, 2); - m_gun_active_left_lamp = BIT(data, 1); - m_vest_hit_lamp[0] = BIT(data, 0); + m_lamps[0xe] = BIT(data, 7); // Damage + m_lamps[0xd] = BIT(data, 6); // Stop + m_lamps[0xc] = BIT(data, 5); // Gun active right + m_lamps[0x9] = BIT(data, 4); // Vest 2 + m_lamps[0xa] = BIT(data, 2); // Vest 3 + m_lamps[0xb] = BIT(data, 1); // Gun active left + m_lamps[0x8] = BIT(data, 0); // Vest 1 break; case 0x06: /* WOP6 */ logerror("WOP6: data = %02x\n", data); break; - case 0x07: /* WOP? */ - logerror("WOP7: data = %02x\n", data); + case 0x07: /* WOP7 - watchdog*/ + m_watchdog->reset_w(data); + break; + default: + logerror("Unknown io1_w, offset = %03x, data = %02x\n", offset, data); + break; + } +} + + +void cops_state::io1_w(offs_t offset, uint8_t data) +{ + switch (offset & 0x0f) + { + case 0x00: /* WOP0 Alpha display*/ + m_lcd_addr_l = data; + break; + case 0x01: /* WOP1 Alpha display*/ + m_lcd_addr_h = data; + { + // update display + constexpr uint16_t addrs_table[] = { + 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0002, 0x0001, 0x0080, + 0x1000, 0x0800, 0x0400, 0x2000, 0x4000, 0x0200, 0x0100, 0x8000 }; + const uint16_t addr = m_lcd_addr_l | (m_lcd_addr_h << 8); + for (int i = 0; i < 16; i++) + { + if (addr == addrs_table[i]) + { + const uint16_t display_data = m_lcd_data_l | (m_lcd_data_h << 8); + m_digits[i] = bitswap<16>(display_data, 4, 5, 12, 1, 0, 11, 10, 6, 7, 2, 9, 3, 15, 8, 14, 13); + break; + } + } + } + break; + case 0x02: /* WOP2 Alpha display*/ + m_lcd_data_l = data; + break; + case 0x03: /* WOP3 Alpha display*/ + m_lcd_data_h = data; + break; + case 0x04: /* WOP4 */ + /* + 0 20p + 1 40p + 2 £1 + 3 £2 + 4 £5 + 5 £10 + 6 £20 + 7 Win lamp + */ + + for (int i = 0; i < 8; i++) + m_lamps[i] = BIT(data, i); + + break; + case 0x05: /* WOP5 */ + + m_lamps[0x8] = BIT(data, 0); // A + m_lamps[0x9] = BIT(data, 1); // B + m_lamps[0xa] = BIT(data, 2); // Collect + + m_meters->update(0, BIT(data, 3)); + + m_lamps[0xb] = BIT(data, 4); // C + m_lamps[0xc] = BIT(data, 5); // Continue + + m_meters->update(1, BIT(data, 6)); + + m_lamps[0xd] = BIT(data, 7); // * + + /* + 0 A lamp + 1 B lamp + 2 Collect lamp + 3 Cash in Meter + 4 C lamp + 5 Continue lamp + 6 Cash out meter + 7 '*' lamp + */ + + // logerror("WOP5 (lamps): data = %02x\n", data); + break; + case 0x06: /* WOP6 - Not connected in Revelations, at least*/ + logerror("WOP6: data = %02x\n", data); break; - case 0x08: /* WOP0 */ - logerror("WOP8: data = %02x\n", data); + case 0x07: /* WOP7 - watchdog*/ + m_watchdog->reset_w(data); break; default: logerror("Unknown io1_w, offset = %03x, data = %02x\n", offset, data); @@ -720,17 +416,15 @@ void cops_state::io2_w(offs_t offset, uint8_t data) switch( offset & 0x0f ) { case 0x02: - m_flash_red_lamp = BIT(data, 0); - m_flash_blue_lamp = BIT(data, 7); - if ( data & ~0x91 ) logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data); + m_lamps[0xf] = BIT(data, 0); // Flash red + m_lamps[0x10] = BIT(data, 7); // Flash blue + // Any other I/O here? break; case 0x04: for (int i = 5; i >= 0; i--) - m_bullet_lamp[i] = BIT(data, i); - if ( data & ~0x3f ) logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data); + m_lamps[0x11 + i] = BIT(data, i); // bullet break; default: - logerror("Unknown io2_w, offset = %02x, data = %02x\n", offset, data); break; } } @@ -741,7 +435,7 @@ void cops_state::io2_w(offs_t offset, uint8_t data) * PA0-2 Steer * PA3 Shake motor? * PA4-6 Fade? - * PA7 STK (system rom banking) + * PA7 STK (system rom banking A13) * PB0-7 SN76489 data bus * CA1-2 n.c. * CB1 /WE SN76489 @@ -749,6 +443,7 @@ void cops_state::io2_w(offs_t offset, uint8_t data) * *************************************/ + void cops_state::via1_irq(int state) { if ( state == ASSERT_LINE ) @@ -762,6 +457,10 @@ void cops_state::via1_irq(int state) m_maincpu->set_input_line(M6502_IRQ_LINE, m_irq ? ASSERT_LINE : CLEAR_LINE); } +void cops_state::via1_a_w(uint8_t data) +{ +} + void cops_state::via1_b_w(uint8_t data) { m_sn_data = bitswap<8>(data,0,1,2,3,4,5,6,7); @@ -807,25 +506,25 @@ void cops_state::via2_irq(int state) void cops_state::cops_map(address_map &map) { - map(0x0000, 0x1fff).ram(); + map(0x0000, 0x1fff).ram().share(m_nvram); map(0x2000, 0x9fff).rom().region("program", 0); - map(0xa000, 0xafff).rw(FUNC(cops_state::io1_r), FUNC(cops_state::io1_w)); + map(0xa000, 0xafff).rw(FUNC(cops_state::io1_cops_r), FUNC(cops_state::io1_cops_w)); map(0xb000, 0xb00f).m("via6522_1", FUNC(via6522_device::map)); /* VIA 1 */ map(0xb800, 0xb80f).m("via6522_2", FUNC(via6522_device::map)); /* VIA 2 */ map(0xc000, 0xcfff).rw(FUNC(cops_state::io2_r), FUNC(cops_state::io2_w)); - map(0xd000, 0xd007).rw(FUNC(cops_state::dacia_r), FUNC(cops_state::dacia_w)); + map(0xd000, 0xd007).m(m_dacia, FUNC(r65c52_device::map)); map(0xd800, 0xd80f).m("via6522_3", FUNC(via6522_device::map)); /* VIA 3 */ map(0xe000, 0xffff).bankr("sysbank1"); } void cops_state::revlatns_map(address_map &map) { - map(0x0000, 0x1fff).ram(); + map(0x0000, 0x1fff).ram().share(m_nvram); map(0x2000, 0x9fff).rom().region("program", 0); - map(0xa000, 0xafff).rw(FUNC(cops_state::io1_lm_r), FUNC(cops_state::io1_w)); + map(0xa000, 0xafff).rw(FUNC(cops_state::io1_r), FUNC(cops_state::io1_w)); map(0xb000, 0xb00f).m("via6522_1", FUNC(via6522_device::map)); /* VIA 1 */ map(0xc000, 0xc00f).rw("rtc", FUNC(msm6242_device::read), FUNC(msm6242_device::write)); - map(0xd000, 0xd007).rw(FUNC(cops_state::dacia_r), FUNC(cops_state::dacia_w)); + map(0xd000, 0xd007).m(m_dacia, FUNC(r65c52_device::map)); map(0xe000, 0xffff).bankr("sysbank1"); } @@ -835,7 +534,7 @@ static INPUT_PORTS_START( cops ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Switch C") PORT_CODE(KEYCODE_C) PORT_IMPULSE(1) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("PROGRAM") PORT_CODE(KEYCODE_P) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN2 ) - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_IMPULSE(1) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_IMPULSE(1) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_START ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Switch B") PORT_CODE(KEYCODE_B) PORT_IMPULSE(1) @@ -848,8 +547,9 @@ static INPUT_PORTS_START( cops ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("100P LEVEL") PORT_CODE(KEYCODE_W) - PORT_START("SW2") - PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_START("SW2") //GUN? + PORT_BIT( 0x7f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) //Left floating, games will fail to boot if this is low PORT_START("STEER") PORT_BIT( 0xff, 0x80, IPT_PADDLE ) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) @@ -860,21 +560,21 @@ static INPUT_PORTS_START( revlatns ) PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("A") PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("C") PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("COLLECT") - PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN5 ) + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN5 ) //COIN5 PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_IMPULSE(1) - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN6 ) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN6 ) // COIN6 PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_NAME("Continue") PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("B") PORT_START("SW1") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE ) PORT_NAME("Refill Key") PORT_CODE(KEYCODE_R) PORT_TOGGLE PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_INTERLOCK) PORT_NAME("Cashbox Door") PORT_CODE(KEYCODE_Q) PORT_TOGGLE - PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("20P LEVEL") PORT_CODE(KEYCODE_Q) + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_CUSTOM ) // 20p level PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) - PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("*") - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("100P LEVEL") PORT_CODE(KEYCODE_W) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_NAME("*") + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_INTERLOCK) PORT_NAME("Back Door") PORT_CODE(KEYCODE_W) PORT_TOGGLE + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_CUSTOM ) // £1 level PORT_START("SW2") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_NAME("50p") @@ -884,30 +584,13 @@ static INPUT_PORTS_START( revlatns ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_CUSTOM ) - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_CUSTOM ) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) //Left floating, games will fail to boot if this is low INPUT_PORTS_END void cops_state::machine_start() { m_digits.resolve(); - m_offroad_right_lamp.resolve(); - m_offroad_left_lamp.resolve(); - m_damage_lamp.resolve(); - m_stop_lamp.resolve(); - m_gun_active_right_lamp.resolve(); - m_gun_active_left_lamp.resolve(); - m_vest_hit_lamp.resolve(); - m_flash_red_lamp.resolve(); - m_flash_blue_lamp.resolve(); - m_bullet_lamp.resolve(); - - m_ld_timer = timer_alloc(FUNC(cops_state::ld_timer_callback), this); - - m_ld_timer->adjust(attotime::from_hz(167*5), 0, attotime::from_hz(167*5)); - - m_dacia_cmpval1 = m_dacia_cmpval2 = 0; - m_ld_command_current_byte = 0; - std::fill(std::begin(m_ld_command_to_send), std::end(m_ld_command_to_send), 0); + m_lamps.resolve(); } void cops_state::machine_reset() @@ -915,16 +598,7 @@ void cops_state::machine_reset() m_irq = 0; m_lcd_addr_l = m_lcd_addr_h = 0; m_lcd_data_l = m_lcd_data_h = 0; - - m_dacia_cts = 0; - m_dacia_dcd = 0; - - m_dacia_irq1_reg = 0x80; - m_dacia_rts1 = 1; - m_dacia_dtr1 = 1; - m_dacia_fe1 = 1; - m_dacia_receiver_full = 1; -} + } void cops_state::init_cops() @@ -938,29 +612,34 @@ void cops_state::init_cops() void cops_state::base(machine_config &config) { - M6502(config, m_maincpu, MAIN_CLOCK/2); + M6502(config, m_maincpu, MAIN_CLOCK/2/2); // fed through two dividers - SONY_LDP1450(config, m_ld, 9600); + SONY_LDP1450HLE(config, m_ld, 0); m_ld->set_screen("screen"); + m_ld->set_overlay(256, 256, FUNC(cops_state::screen_update)); + m_ld->add_route(0, "lspeaker", 0.50); + m_ld->add_route(1, "rspeaker", 0.50); + m_ld->set_baud(9600); + m_ld->add_ntsc_screen(config, "screen"); + m_ld->serial_tx().set("dacia", FUNC(r65c52_device::write_rxd1)); - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_video_attributes(VIDEO_SELF_RENDER); - screen.set_raw(XTAL(14'318'181)*2, 910, 0, 704, 525, 44, 524); - screen.set_screen_update("laserdisc", FUNC(laserdisc_device::screen_update)); + NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); - /* via */ - via6522_device &via1(MOS6522(config, "via6522_1", MAIN_CLOCK/2)); - via1.irq_handler().set(FUNC(cops_state::via1_irq)); - via1.writepb_handler().set(FUNC(cops_state::via1_b_w)); - via1.cb1_handler().set(FUNC(cops_state::via1_cb1_w)); + SPEAKER(config, "lspeaker").front_left(); + + SPEAKER(config, "mspeaker").front_center(); - SPEAKER(config, "mono").front_center(); + SPEAKER(config, "rspeaker").front_right(); - /* acia (really a 65C52)*/ + R65C52(config, m_dacia, DACIA_CLOCK); + m_dacia->txd1_handler().set("laserdisc", FUNC(sony_ldp1450hle_device::rx_w)); + m_dacia->irq1_handler().set(FUNC(cops_state::acia1_irq)); + m_dacia->irq2_handler().set(FUNC(cops_state::acia2_irq)); - /* TODO: Verify clock */ SN76489(config, m_sn, MAIN_CLOCK/2); - m_sn->add_route(ALL_OUTPUTS, "mono", 0.50); + m_sn->add_route(ALL_OUTPUTS, "mspeaker", 0.30); + + WATCHDOG_TIMER(config, "watchdog").set_time(attotime::from_msec(1600)); } void cops_state::cops(machine_config &config) @@ -969,10 +648,17 @@ void cops_state::cops(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &cops_state::cops_map); - via6522_device &via2(MOS6522(config, "via6522_2", MAIN_CLOCK/2)); + /* via */ + via6522_device &via1(MOS6522(config, "via6522_1", MAIN_CLOCK/4)); + via1.irq_handler().set(FUNC(cops_state::via1_irq)); + via1.writepa_handler().set(FUNC(cops_state::via1_a_w)); + via1.writepb_handler().set(FUNC(cops_state::via1_b_w)); + via1.cb1_handler().set(FUNC(cops_state::via1_cb1_w)); + + via6522_device &via2(MOS6522(config, "via6522_2", MAIN_CLOCK/4)); via2.irq_handler().set(FUNC(cops_state::via2_irq)); - via6522_device &via3(MOS6522(config, "via6522_3", MAIN_CLOCK/2)); + via6522_device &via3(MOS6522(config, "via6522_3", MAIN_CLOCK/4)); via3.readpa_handler().set(FUNC(cops_state::cdrom_data_r)); via3.writepa_handler().set(FUNC(cops_state::cdrom_data_w)); via3.writepb_handler().set(FUNC(cops_state::cdrom_ctrl_w)); @@ -984,7 +670,23 @@ void cops_state::revlatns(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &cops_state::revlatns_map); - MSM6242(config, "rtc", XTAL(32'768)); + + /* via */ + via6522_device &via1(MOS6522(config, "via6522_1", MAIN_CLOCK/4)); + via1.irq_handler().set(FUNC(cops_state::via1_irq)); + via1.writepb_handler().set(FUNC(cops_state::via1_b_w)); + via1.cb1_handler().set(FUNC(cops_state::via1_cb1_w)); + + METERS(config, m_meters, 0).set_number(2); + + bacta_datalogger_device &bacta(BACTA_DATALOGGER(config, "bacta", 0)); + + m_dacia->txd1_handler().set("laserdisc", FUNC(sony_ldp1450hle_device::rx_w)); + m_dacia->txd2_handler().set("bacta", FUNC(bacta_datalogger_device::write_txd)); + + bacta.rxd_handler().set("dacia", FUNC(r65c52_device::write_rxd2)); + + MSM6242(config, "rtc", 32.768_kHz_XTAL); } /*************************************************************************** @@ -1004,7 +706,7 @@ ROM_START( cops ) DISK_IMAGE_READONLY( "copscd", 0, NO_DUMP ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "cops", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "copsld", 0, NO_DUMP ) ROM_END ROM_START( copsuk ) @@ -1023,18 +725,18 @@ ROM_END ROM_START( revlatns ) ROM_REGION( 0x8000, "program", 0 ) - ROM_LOAD( "revelations_prog.bin", 0x0000, 0x8000, CRC(5ab41ac3) SHA1(0f7027551da17011576cf077e2f199729bb10482) ) + ROM_LOAD( "revelations_prog.u31", 0x0000, 0x8000, CRC(5ab41ac3) SHA1(0f7027551da17011576cf077e2f199729bb10482) ) ROM_REGION( 0x8000, "system", 0 ) - ROM_LOAD( "revelations_sys.bin", 0x0000, 0x8000, CRC(43e5e3ec) SHA1(fa44b102b5aa7ad2421c575abdc67f1c29f23bc1) ) + ROM_LOAD( "revelations_sys.u17", 0x0000, 0x8000, CRC(43e5e3ec) SHA1(fa44b102b5aa7ad2421c575abdc67f1c29f23bc1) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "revlatns", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "nova dp1-3a", 0, BAD_DUMP SHA1(f69c6a3def1e1eec0a58862c487e47d4da12b25e)) //one disc, no correction, old method ROM_END } // Anonymous namespace -GAMEL( 1994, cops, 0, cops, cops, cops_state, init_cops, ROT0, "Atari Games", "Cops (USA)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops ) -GAMEL( 1994, copsuk, cops, cops, cops, cops_state, init_cops, ROT0, "Nova Productions / Deith Leisure", "Cops (UK)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops ) -GAMEL( 1994, revlatns, 0, revlatns, revlatns, cops_state, init_cops, ROT0, "Nova Productions", "Revelations", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops ) +GAMEL( 1994, cops, 0, cops, cops, cops_state, init_cops, ROT0, "Atari Games", "Cops (USA)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops ) +GAMEL( 1994, copsuk, cops, cops, cops, cops_state, init_cops, ROT0, "Nova Productions Ltd./ Deith Leisure", "Cops (UK)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_cops ) +GAMEL( 1991, revlatns, 0, revlatns, revlatns, cops_state, init_cops, ROT0, "Nova Productions Ltd.", "Revelations", MACHINE_SUPPORTS_SAVE, layout_revlatns ) diff --git a/src/mame/layout/cops.lay b/src/mame/layout/cops.lay index 14501520153..32e1961bfb2 100644 --- a/src/mame/layout/cops.lay +++ b/src/mame/layout/cops.lay @@ -17,7 +17,7 @@ license:CC0-1.0 - + diff --git a/src/mame/layout/revlatns.lay b/src/mame/layout/revlatns.lay new file mode 100644 index 00000000000..ae2cac78a3d --- /dev/null +++ b/src/mame/layout/revlatns.lay @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mame/sega/timetrv.cpp b/src/mame/sega/timetrv.cpp index 0f7d5910214..ea67523bb1e 100644 --- a/src/mame/sega/timetrv.cpp +++ b/src/mame/sega/timetrv.cpp @@ -8,7 +8,7 @@ Driver by Angelo Salese LaserDisc and artwork hookup by Ryan Holtz TODO: -- Unemulated Sony LDP-1450 player, and Pioneer LD-V4200 is HLE; needs a dump of the BIOSes and +- Sony LDP-1450 player (not hooked up) and Pioneer LD-V4200 are HLE; needs a dump of the BIOSes and proper hook-up. ================================================================================================== -- cgit v1.2.3