From ba60c481b596a5bcdb9e1f4c2d3e0a29e5787840 Mon Sep 17 00:00:00 2001 From: MooglyGuy Date: Sun, 7 Jan 2024 17:47:49 +0100 Subject: Added LD-V1000 HLE device, placeholder SSI-263A HLE device, and promoted Thayer's Quest. (#11915) [Ryan Holtz] -cinematronics/thayers.cpp: Added CHD and promoted to working. [Ryan Holtz] * Fixed periodic IRQ hookup. * Fixed COP421 clock divisor. * Switched to LD-V1000 by default and removed LD-PR7820 support for now. * Switched to using logmacro and shorthand data types. * Adjusted IRQ triggering and acknowledgement according to schematics. -sound/ssi263hle.cpp: Added a temporary SSI-263 device which remaps SC-02 phonemes onto the SC-01's phoneme set. [Ryan Holtz] * SSI-263 skeleton extracted from cinematronics/thayers.cpp. -machine/ldv1000hle.cpp: Added an HLE version of the Pioneer LD-V1000 laserdisc player. [Ryan Holtz] -machine/laserdsc.h: Added a general-purpose parallel LaserDisc player interface, to have a common class parent for LD-PR7820. [Ryan Holtz] -sega/timetrv.cpp: Replaced timetrv2 LaserDisc image with a good capture. [Ryan Holtz, Matt Ownby, ld-decode Team] Systems promoted to working --------------------------- Thayer's Quest (set 1) [Ryan Holtz, Matt Ownby, ld-decode Team] Clones promoted to working -------------------------- Thayer's Quest (set 2) [Ryan Holtz, Matt Ownby, ld-decode Team] --- scripts/src/machine.lua | 12 + scripts/src/sound.lua | 14 + src/devices/machine/laserdsc.cpp | 95 ++-- src/devices/machine/laserdsc.h | 23 +- src/devices/machine/ldstub.cpp | 4 +- src/devices/machine/ldstub.h | 16 +- src/devices/machine/ldv1000.cpp | 12 +- src/devices/machine/ldv1000.h | 12 +- src/devices/machine/ldv1000hle.cpp | 1047 ++++++++++++++++++++++++++++++++++++ src/devices/machine/ldv1000hle.h | 201 +++++++ src/devices/machine/ldv4200hle.h | 2 +- src/devices/sound/ssi263hle.cpp | 230 ++++++++ src/devices/sound/ssi263hle.h | 76 +++ src/mame/cinematronics/dlair.cpp | 4 +- src/mame/konami/konblands.cpp | 2 +- src/mame/layout/thayers.lay | 78 +-- src/mame/misc/esh.cpp | 2 +- src/mame/misc/istellar.cpp | 2 +- src/mame/misc/thayers.cpp | 867 +++++++++++++---------------- src/mame/sega/gpworld.cpp | 2 +- src/mame/sega/segald.cpp | 2 +- src/mame/sega/timetrv.cpp | 2 +- src/mame/taito/lgp.cpp | 2 +- src/mame/universal/superdq.cpp | 2 +- 24 files changed, 2053 insertions(+), 656 deletions(-) create mode 100644 src/devices/machine/ldv1000hle.cpp create mode 100644 src/devices/machine/ldv1000hle.h create mode 100644 src/devices/sound/ssi263hle.cpp create mode 100644 src/devices/sound/ssi263hle.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 049cae04d36..f17010c82f0 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -2048,6 +2048,18 @@ if (MACHINES["LDV1000"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/ldv1000hle.h,MACHINES["LDV1000HLE"] = true +--------------------------------------------------- + +if (MACHINES["LDV1000HLE"]~=null) then + files { + MAME_DIR .. "src/devices/machine/ldv1000hle.cpp", + MAME_DIR .. "src/devices/machine/ldv1000hle.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/ldv4200hle.h,MACHINES["LDV4200HLE"] = true diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 67aa52cfd68..ed9652835b2 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1010,6 +1010,20 @@ end +--------------------------------------------------- +-- Silicon Systems SSI-263A HLE +--@src/devices/sound/ssi263hle.h,SOUNDS["SSI263HLE"] = true +--------------------------------------------------- + +if (SOUNDS["SSI263HLE"]~=null) then + files { + MAME_DIR .. "src/devices/sound/ssi263hle.cpp", + MAME_DIR .. "src/devices/sound/ssi263hle.h", + } +end + + + --------------------------------------------------- -- Texas Instruments TMS36xx doorbell chime --@src/devices/sound/tms36xx.h,SOUNDS["TMS36XX"] = true diff --git a/src/devices/machine/laserdsc.cpp b/src/devices/machine/laserdsc.cpp index 9c6cd3c48c5..246369f15ef 100644 --- a/src/devices/machine/laserdsc.cpp +++ b/src/devices/machine/laserdsc.cpp @@ -60,49 +60,54 @@ const uint32_t VIRTUAL_LEAD_OUT_TRACKS = LEAD_OUT_MIN_SIZE_IN_UM * 1000 / NOMINA ALLOW_SAVE_TYPE(laserdisc_device::player_state); ALLOW_SAVE_TYPE(laserdisc_device::slider_position); +parallel_laserdisc_device::parallel_laserdisc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : laserdisc_device(mconfig, type, tag, owner, clock) +{ +} + //------------------------------------------------- // laserdisc_device - constructor //------------------------------------------------- laserdisc_device::laserdisc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock), - device_sound_interface(mconfig, *this), - device_video_interface(mconfig, *this), - m_getdisc_callback(*this), - m_audio_callback(*this), - m_overwidth(0), - m_overheight(0), - 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), - m_samplerate(0), - m_readresult(), - m_chdtracks(0), - m_work_queue(osd_work_queue_alloc(WORK_QUEUE_FLAG_IO)), - m_audiosquelch(0), - m_videosquelch(0), - m_fieldnum(0), - m_curtrack(0), - m_maxtrack(0), - m_attospertrack(0), - m_sliderupdate(attotime::zero), - m_videoindex(0), - m_stream(nullptr), - m_audiobufsize(0), - m_audiobufin(0), - m_audiobufout(0), - m_audiocursamples(0), - m_audiomaxsamples(0), - m_videoenable(false), - m_videotex(nullptr), - m_videopalette(nullptr), - m_overenable(false), - m_overindex(0), - m_overtex(nullptr) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , device_video_interface(mconfig, *this) + , m_getdisc_callback(*this) + , m_audio_callback(*this) + , m_overwidth(0) + , m_overheight(0) + , 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) + , m_samplerate(0) + , m_readresult() + , m_chdtracks(0) + , m_work_queue(osd_work_queue_alloc(WORK_QUEUE_FLAG_IO)) + , m_audiosquelch(0) + , m_videosquelch(0) + , m_fieldnum(0) + , m_curtrack(0) + , m_maxtrack(0) + , m_attospertrack(0) + , m_sliderupdate(attotime::zero) + , m_videoindex(0) + , m_stream(nullptr) + , m_audiobufsize(0) + , m_audiobufin(0) + , m_audiobufout(0) + , m_audiocursamples(0) + , m_audiomaxsamples(0) + , m_videoenable(false) + , m_videotex(nullptr) + , m_videopalette(nullptr) + , m_overenable(false) + , m_overindex(0) + , m_overtex(nullptr) { // initialize overlay_config m_orig_config.m_overposx = m_orig_config.m_overposy = 0.0f; @@ -453,30 +458,30 @@ void laserdisc_device::sound_stream_update(sound_stream &stream, std::vector 0) - m_attospertrack = (vsyncperiod / tracks_per_vsync).as_attoseconds(); + m_attospertrack = DOUBLE_TO_ATTOSECONDS(vsyncperiod / tracks_per_vsync); // negative values store negative times else { - m_attospertrack = -(vsyncperiod / -tracks_per_vsync).as_attoseconds(); + m_attospertrack = DOUBLE_TO_ATTOSECONDS(-vsyncperiod / -tracks_per_vsync); } - LOGMASKED(LOG_SLIDER, "Slider speed = %d\n", tracks_per_vsync); + LOGMASKED(LOG_SLIDER, "Slider speed = %f\n", tracks_per_vsync); } @@ -994,7 +999,7 @@ void laserdisc_device::read_track_data() vbidata.line16 = 0; vbidata.line17 = vbidata.line18 = vbidata.line1718 = VBI_CODE_LEADIN; } -//printf("track %5d.%d: %06X %06X %06X\n", m_curtrack, m_fieldnum, vbidata.line16, vbidata.line17, vbidata.line18); + LOGMASKED(LOG_SLIDER, "track %5d.%d: %06X %06X %06X\n", m_curtrack, m_fieldnum, vbidata.line16, vbidata.line17, vbidata.line18); // if we're about to read the first field in a frame, advance frame_data *frame = &m_frame[m_videoindex]; diff --git a/src/devices/machine/laserdsc.h b/src/devices/machine/laserdsc.h index abc536a3be0..b66029d7ee5 100644 --- a/src/devices/machine/laserdsc.h +++ b/src/devices/machine/laserdsc.h @@ -221,7 +221,7 @@ protected: // subclass helpers void set_audio_squelch(bool squelchleft, bool squelchright) { m_stream->update(); m_audiosquelch = (squelchleft ? 1 : 0) | (squelchright ? 2 : 0); } void set_video_squelch(bool squelch) { m_videosquelch = squelch; } - void set_slider_speed(int32_t tracks_per_vsync); + void set_slider_speed(const double tracks_per_vsync); void advance_slider(int32_t numtracks); slider_position get_slider_position(); int32_t generic_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime, player_state_info &curstate); @@ -304,7 +304,7 @@ private: // audio data sound_stream * m_stream; - std::vector m_audiobuffer[2]; // buffer for audio samples + std::vector m_audiobuffer[2]; // buffer for audio samples uint32_t m_audiobufsize; // size of buffer uint32_t m_audiobufin; // input index uint32_t m_audiobufout; // output index @@ -335,6 +335,25 @@ typedef device_interface_enumerator laserdisc_device_enumerato // INLINE FUNCTIONS //************************************************************************** +// ======================> parallel_laserdisc_device + +class parallel_laserdisc_device : public laserdisc_device +{ +public: + + virtual void data_w(u8 data) = 0; + virtual u8 data_r() = 0; + virtual void enter_w(int state) {} + virtual int data_available_r() { return CLEAR_LINE; } + virtual int status_strobe_r() { return CLEAR_LINE; } + virtual int ready_r() { return ASSERT_LINE; } + +protected: + parallel_laserdisc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock = 0); + + virtual void player_overlay(bitmap_yuy16 &bitmap) override { } +}; + //------------------------------------------------- // is_start_of_frame - return true if this is // the start of a frame diff --git a/src/devices/machine/ldstub.cpp b/src/devices/machine/ldstub.cpp index c0c9a7ac05a..3fabac8cc45 100644 --- a/src/devices/machine/ldstub.cpp +++ b/src/devices/machine/ldstub.cpp @@ -22,12 +22,12 @@ DEFINE_DEVICE_TYPE(PHILIPS_22VP932, philips_22vp932_device, "22vp932", "Philips pioneer_pr7820_device::pioneer_pr7820_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : laserdisc_device(mconfig, PIONEER_PR7820, tag, owner, clock) + : parallel_laserdisc_device(mconfig, PIONEER_PR7820, tag, owner, clock) { } philips_22vp932_device::philips_22vp932_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : laserdisc_device(mconfig, PHILIPS_22VP932, tag, owner, clock) + : parallel_laserdisc_device(mconfig, PHILIPS_22VP932, tag, owner, clock) { } diff --git a/src/devices/machine/ldstub.h b/src/devices/machine/ldstub.h index 9fbc756b240..77eba3d67da 100644 --- a/src/devices/machine/ldstub.h +++ b/src/devices/machine/ldstub.h @@ -31,18 +31,15 @@ DECLARE_DEVICE_TYPE(PHILIPS_22VP932, philips_22vp932_device) // ======================> pioneer_pr7820_device -class pioneer_pr7820_device : public laserdisc_device +class pioneer_pr7820_device : public parallel_laserdisc_device { public: // construction/destruction pioneer_pr7820_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // input/output - uint8_t data_available_r() { return CLEAR_LINE; } - uint8_t ready_r() { return ASSERT_LINE; } - uint8_t data_r() { return 0; } - void data_w(uint8_t data) { } - void enter_w(uint8_t data) { } + virtual void data_w(uint8_t data) override { } + virtual uint8_t data_r() override { return 0; } protected: // subclass overrides @@ -54,16 +51,15 @@ protected: // ======================> philips_22vp932_device -class philips_22vp932_device : public laserdisc_device +class philips_22vp932_device : public parallel_laserdisc_device { public: // construction/destruction philips_22vp932_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // input/output - uint8_t data_r() { return 0; } - void data_w(uint8_t data) { } - void enter_w(uint8_t data) { } + virtual void data_w(uint8_t data) override { } + virtual uint8_t data_r() override { return 0; } protected: // subclass overrides diff --git a/src/devices/machine/ldv1000.cpp b/src/devices/machine/ldv1000.cpp index 5b418d13bf0..4ee45b098d0 100644 --- a/src/devices/machine/ldv1000.cpp +++ b/src/devices/machine/ldv1000.cpp @@ -34,6 +34,7 @@ #define LOG_STATUS_CHANGES (1U << 2) #define LOG_FRAMES_SEEN (1U << 3) #define LOG_COMMANDS (1U << 4) + #define VERBOSE (0) #include "logmacro.h" @@ -104,7 +105,7 @@ ROM_END //------------------------------------------------- pioneer_ldv1000_device::pioneer_ldv1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : laserdisc_device(mconfig, PIONEER_LDV1000, tag, owner, clock), + : parallel_laserdisc_device(mconfig, PIONEER_LDV1000, tag, owner, clock), m_z80_cpu(*this, "ldv1000"), m_z80_ctc(*this, "ldvctc"), m_multitimer(nullptr), @@ -137,15 +138,6 @@ void pioneer_ldv1000_device::data_w(uint8_t data) } -//------------------------------------------------- -// enter_w - set the state of the ENTER strobe -//------------------------------------------------- - -void pioneer_ldv1000_device::enter_w(uint8_t data) -{ -} - - //------------------------------------------------- // device_start - device initialization //------------------------------------------------- diff --git a/src/devices/machine/ldv1000.h b/src/devices/machine/ldv1000.h index a4ffa12f5b2..f666e8f9ac0 100644 --- a/src/devices/machine/ldv1000.h +++ b/src/devices/machine/ldv1000.h @@ -35,7 +35,7 @@ DECLARE_DEVICE_TYPE(PIONEER_LDV1000, pioneer_ldv1000_device) // ======================> pioneer_ldv1000_device // base ldv1000 class -class pioneer_ldv1000_device : public laserdisc_device +class pioneer_ldv1000_device : public parallel_laserdisc_device { public: // construction/destruction @@ -44,11 +44,11 @@ public: auto command_strobe_callback() { return m_command_strobe_cb.bind(); } // input and output - void data_w(uint8_t data); - void enter_w(uint8_t data); - uint8_t status_r() const { return m_status; } - uint8_t status_strobe_r() const { return (m_portc1 & 0x20) ? ASSERT_LINE : CLEAR_LINE; } - uint8_t command_strobe_r() const { return (m_portc1 & 0x10) ? ASSERT_LINE : CLEAR_LINE; } + virtual void data_w(uint8_t data) override; + virtual void enter_w(int state) override { } + virtual uint8_t data_r() override { return m_status; } + virtual int status_strobe_r() override { static int old_val = 0; int value = BIT(m_portc1, 5); if (value != old_val) { old_val = value; printf("status_strobe_r: %d\n", value); } return value; } + virtual int ready_r() override { static int old_val = 0; int value = BIT(m_portc1, 4); if (value != old_val) { old_val = value; printf("ready_r: %d\n", value); } return value; } protected: // device-level overrides diff --git a/src/devices/machine/ldv1000hle.cpp b/src/devices/machine/ldv1000hle.cpp new file mode 100644 index 00000000000..ef89c5de25e --- /dev/null +++ b/src/devices/machine/ldv1000hle.cpp @@ -0,0 +1,1047 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/************************************************************************* + + ldv1000hle.cpp + + Pioneer LDV-1000 laserdisc player simulation. + +************************************************************************** + + To do: + + * On-screen display support + * Commands that Dragon's Lair doesn't use + +*************************************************************************/ + + +#include "emu.h" +#include "ldv1000hle.h" + + +#define LOG_COMMAND_BYTES (1U << 1) +#define LOG_COMMANDS (1U << 2) +#define LOG_REPLIES (1U << 3) +#define LOG_SEARCHES (1U << 4) +#define LOG_STOPS (1U << 5) +#define LOG_SQUELCHES (1U << 6) +#define LOG_FRAMES (1U << 7) +#define LOG_ALL (LOG_COMMAND_BYTES | LOG_COMMANDS | LOG_REPLIES | LOG_SEARCHES | LOG_STOPS | LOG_SQUELCHES | LOG_FRAMES) + +#define VERBOSE (0) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(PIONEER_LDV1000HLE, pioneer_ldv1000hle_device, "ldv1000hle", "Pioneer LDV-1000 HLE") + + +//------------------------------------------------- +// pioneer_ldv1000hle_device - constructor +//------------------------------------------------- + +pioneer_ldv1000hle_device::pioneer_ldv1000hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : parallel_laserdisc_device(mconfig, PIONEER_LDV1000HLE, tag, owner, clock) + , m_vbi_fetch(nullptr) + , m_stop_timer(nullptr) + , m_park_strobe_timer(nullptr) + , m_assert_status_strobe_timer(nullptr) + , m_deassert_status_strobe_timer(nullptr) + , m_assert_command_strobe_timer(nullptr) + , m_deassert_command_strobe_timer(nullptr) + , m_cmd_length(0) + , m_status(STATUS_PARK | STATUS_READY) + , m_pre_stop_status(0) + , m_mode(MODE_PARK) + , m_curr_frame(0) + , m_search_frame(~0U) + , m_stop_frame(~0U) + , m_cmd_number_length(0) + , m_curr_register(0) + , m_scan_speed(60) + , m_scan_speed_accum(0) + , m_play_speed(0.0) + , m_status_strobe(true) + , m_command_strobe(true) +{ +} + + +//------------------------------------------------- +// device_start - device initialization +//------------------------------------------------- + +void pioneer_ldv1000hle_device::device_start() +{ + // pass through to the parent + parallel_laserdisc_device::device_start(); + + // allocate timers + m_vbi_fetch = timer_alloc(FUNC(pioneer_ldv1000hle_device::process_vbi_data), this); + m_stop_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::resume_from_stop), this); + m_park_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::park_strobe_tick), this);; + m_assert_status_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::assert_status_strobe), this); + m_deassert_status_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::deassert_status_strobe), this); + m_assert_command_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::assert_command_strobe), this); + m_deassert_command_strobe_timer = timer_alloc(FUNC(pioneer_ldv1000hle_device::deassert_command_strobe), this); + + // register state saving + save_item(NAME(m_cmd_buffer)); + save_item(NAME(m_cmd_length)); + save_item(NAME(m_status)); + save_item(NAME(m_pre_stop_status)); + save_item(NAME(m_mode)); + save_item(NAME(m_curr_frame)); + save_item(NAME(m_search_frame)); + save_item(NAME(m_stop_frame)); + save_item(NAME(m_cmd_number)); + save_item(NAME(m_cmd_number_length)); + save_item(NAME(m_user_ram)); + save_item(NAME(m_curr_register)); + save_item(NAME(m_scan_speed)); + save_item(NAME(m_scan_speed_accum)); + save_item(NAME(m_play_speed)); + save_item(NAME(m_audio_enable)); + save_item(NAME(m_status_strobe)); + save_item(NAME(m_command_strobe)); +} + + +//------------------------------------------------- +// device_reset - device reset +//------------------------------------------------- + +void pioneer_ldv1000hle_device::device_reset() +{ + // pass through to the parent + parallel_laserdisc_device::device_reset(); + + // reset our state + m_vbi_fetch->adjust(attotime::never); + + std::fill_n(m_cmd_buffer, std::size(m_cmd_buffer), 0); + m_cmd_length = 0; + m_status = STATUS_PARK | STATUS_READY; + m_pre_stop_status = 0; + m_mode = MODE_PARK; + m_curr_frame = 0; + m_search_frame = ~0U; + m_stop_frame = ~0U; + m_cmd_number_length = 0; + m_curr_register = 0; + m_scan_speed = 0; + m_scan_speed_accum = 0; + m_audio_enable[0] = true; + m_audio_enable[1] = true; + m_status_strobe = true; + m_command_strobe = true; + + set_video_squelch(false); + update_video_enable(); + update_audio_enable(); + + m_park_strobe_timer->adjust(attotime::from_msec(21), 0, attotime::from_msec(21)); +} + + +//------------------------------------------------- +// data_w - handle a new data byte +// received over the parallel link +//------------------------------------------------- + +void pioneer_ldv1000hle_device::data_w(u8 data) +{ + LOGMASKED(LOG_COMMAND_BYTES, "data_w: Command byte added: %02x\n", data); + if (m_cmd_length < std::size(m_cmd_buffer)) + { + m_cmd_buffer[m_cmd_length] = data; + m_cmd_length++; + } + + if (is_command_byte(data)) + { + LOGMASKED(LOG_COMMAND_BYTES, "data_w: %02x is a command byte, processing command buffer\n", data); + process_command_buffer(); + } + else + { + LOGMASKED(LOG_COMMAND_BYTES, "data_w: %02x is not a command byte, leaving in the queue for now\n", data); + } +} + + +//------------------------------------------------- +// data_r - returns the current status byte or +// reply data when necessary +//------------------------------------------------- + +u8 pioneer_ldv1000hle_device::data_r() +{ + u8 data = m_status; + LOGMASKED(LOG_REPLIES, "Sending reply %02x\n", data); + return data; +} + + +//------------------------------------------------- +// enter_w - used to request service by a host +// application, not currently implemented +//------------------------------------------------- + +void pioneer_ldv1000hle_device::enter_w(int state) +{ +} + + +//------------------------------------------------- +// player_vsync - VSYNC callback, called at the +// start of the blanking period +//------------------------------------------------- + +void pioneer_ldv1000hle_device::player_vsync(const vbi_metadata &vbi, int fieldnum, const attotime &curtime) +{ + // set a timer to fetch the VBI data when it is ready + if (m_mode != MODE_PARK) + { + m_vbi_fetch->adjust(screen().time_until_pos(19*2)); + } +} + + +//------------------------------------------------- +// player_update - update callback, called on +// the first visible line of the frame +//------------------------------------------------- + +s32 pioneer_ldv1000hle_device::player_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime) +{ + if (m_mode != MODE_PARK) + { + m_assert_status_strobe_timer->adjust(attotime::from_usec(500)); + } + + if (!fieldnum) + return 0; + + if (m_mode == MODE_SCAN_FORWARD || m_mode == MODE_SCAN_REVERSE) + { + m_scan_speed_accum += m_scan_speed; + int elapsed_tracks = m_scan_speed_accum / 60; + m_scan_speed_accum -= elapsed_tracks * 60; + if (m_mode == MODE_SCAN_REVERSE) + elapsed_tracks *= -1; + + if (m_stop_frame != ~0U) + { + const int next_frame = (int)m_curr_frame + elapsed_tracks; + if (next_frame >= m_stop_frame) + { + // If we've landed on (or exceeded) our desired frame and are in a SKIP FORWARD command, resume from the desired frame. + elapsed_tracks = (int)m_stop_frame - (int)m_curr_frame; + m_stop_frame = ~0U; + set_playing(STATUS_FORWARD, m_play_speed); + } + } + return elapsed_tracks; + } + + if (m_mode == MODE_PLAY || m_mode == MODE_SEARCH) + { + return 1; + } + + return 0; +} + + +//------------------------------------------------- +// process_vbi_data - process VBI data and +// act on search/play seeking +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::process_vbi_data) +{ + uint32_t line = get_field_code(LASERDISC_CODE_LINE1718, false); + if ((line & 0xf00000) == 0xf00000 || line == VBI_CODE_LEADIN || line == VBI_CODE_LEADOUT) + { + uint32_t old_frame = m_curr_frame; + if (line == VBI_CODE_LEADIN) + m_curr_frame = 0; + else if (line == VBI_CODE_LEADOUT) + m_curr_frame = 54000; + else + m_curr_frame = bcd_to_literal(line & 0x7ffff); + + LOGMASKED(LOG_FRAMES, "Current frame is %d (VBI 16: %06x, VBI 17: %06x, VBI 18: %06x, VBI 1718: %06x\n", m_curr_frame, + get_field_code(LASERDISC_CODE_LINE16, false), + get_field_code(LASERDISC_CODE_LINE17, false), + get_field_code(LASERDISC_CODE_LINE18, false), + line); + + if (m_mode != MODE_STOP) + { + if (m_stop_frame != ~0U && m_search_frame == ~0U) + { + s32 old_delta = (s32)m_stop_frame - (s32)old_frame; + s32 curr_delta = (s32)m_stop_frame - (s32)m_curr_frame; + LOGMASKED(LOG_STOPS, "%s: Stop frame is currently %d, old frame is %d, current frame is %d, old delta %d, curr delta %d\n", machine().describe_context(), m_stop_frame, old_frame, m_curr_frame, old_delta, curr_delta); + if (curr_delta == 0 || (old_delta < 0) != (curr_delta < 0)) + { + m_stop_frame = ~0U; + LOGMASKED(LOG_STOPS, "%s: Stop frame: Zero delta, entering stop mode\n", machine().describe_context()); + set_stopped((m_status & STATUS_READY) | STATUS_STOP); + } + } + + if (m_search_frame != ~0U) + { + s32 delta = (s32)m_search_frame - (s32)m_curr_frame; + LOGMASKED(LOG_SEARCHES, "%s: Searching from current frame %d with delta %d\n", machine().describe_context(), m_curr_frame, delta); + if (delta == 0) + { + // We've found our frame, enter play, pause or still mode. + m_search_frame = ~0U; + LOGMASKED(LOG_SEARCHES, "%s: Search Mark: Zero delta, entering still mode\n", machine().describe_context()); + set_stopped(STATUS_SEARCH_FINISH); + } + else if (delta <= 2 && delta > 0) + { + LOGMASKED(LOG_SEARCHES, "%s: Positive near delta, letting disc run to current\n", machine().describe_context()); + // We're approaching our frame, let it run up. + } + else + { + if (delta < 0) + { + advance_slider(std::min(-2, delta / 2)); + } + else + { + advance_slider(std::max(1, delta / 2)); + } + } + } + } + } +} + + +//------------------------------------------------- +// resume_from_stop - resume from a previous +// STOP command +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::resume_from_stop) +{ + set_playing(m_pre_stop_status, m_play_speed); +} + + +//------------------------------------------------- +// park_strobe_tick - used to trigger periodic +// status strobes when the player is parked +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::park_strobe_tick) +{ + m_status_strobe = false; + m_deassert_status_strobe_timer->adjust(attotime::from_usec(26)); + m_assert_command_strobe_timer->adjust(attotime::from_usec(54)); +} + + +//------------------------------------------------- +// assert_status_strobe - assert the status +// strobe signal to a host application at the +// appropriate time, and prepare for command +// strobe +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::assert_status_strobe) +{ + m_status_strobe = false; + m_deassert_status_strobe_timer->adjust(attotime::from_usec(26)); + m_assert_command_strobe_timer->adjust(attotime::from_usec(54)); +} + + +//------------------------------------------------- +// deassert_status_strobe +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::deassert_status_strobe) +{ + m_status_strobe = true; +} + + +//------------------------------------------------- +// assert_command_strobe +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::assert_command_strobe) +{ + m_command_strobe = false; + m_deassert_command_strobe_timer->adjust(attotime::from_usec(25)); +} + + +//------------------------------------------------- +// deassert_command_strobe +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(pioneer_ldv1000hle_device::deassert_command_strobe) +{ + m_command_strobe = true; +} + + +//------------------------------------------------- +// process_command_buffer - process a command +// line sent from the host +//------------------------------------------------- + +void pioneer_ldv1000hle_device::process_command_buffer() +{ + size_t cmd_index = 0; + while (cmd_index < m_cmd_length) + { + process_command(cmd_index); + cmd_index++; + } + + m_cmd_length = 0; +} + + +//------------------------------------------------- +// is_command_byte - returns whether or not a +// given byte is a recognized command +//------------------------------------------------- + +bool pioneer_ldv1000hle_device::is_command_byte(const u8 data) +{ + switch (data) + { + case CMD_CLEAR: + case CMD_0: + case CMD_1: + case CMD_2: + case CMD_3: + case CMD_4: + case CMD_5: + case CMD_6: + case CMD_7: + case CMD_8: + case CMD_9: + case CMD_STORE: + case CMD_RECALL: + case CMD_DISPLAY: + case CMD_AUDIO1: + case CMD_AUDIO2: + case CMD_PLAY: + case CMD_STOP: + case CMD_AUTOSTOP: + case CMD_SEARCH: + case CMD_SCAN_FWD: + case CMD_SCAN_REV: + case CMD_STEP_FWD: + case CMD_STEP_REV: + case CMD_REJECT: + case CMD_NO_ENTRY: + case CMD_LOAD: + case CMD_DISPLAY_DISABLE: + case CMD_DISPLAY_ENABLE: + case CMD_GET_FRAME_NUM: + case CMD_GET_2ND_DISPLAY: + case CMD_GET_1ST_DISPLAY: + case CMD_TRANSFER_MEMORY: + case CMD_FWD_X0: + case CMD_FWD_X1_4: + case CMD_FWD_X1_2: + case CMD_FWD_X1: + case CMD_FWD_X2: + case CMD_FWD_X3: + case CMD_FWD_X4: + case CMD_FWD_X5: + case CMD_SKIP_FWD_10: + case CMD_SKIP_FWD_20: + case CMD_SKIP_FWD_30: + case CMD_SKIP_FWD_40: + case CMD_SKIP_FWD_50: + case CMD_SKIP_FWD_60: + case CMD_SKIP_FWD_70: + case CMD_SKIP_FWD_80: + case CMD_SKIP_FWD_90: + case CMD_SKIP_FWD_100: + return true; + } + return false; +} + + +//------------------------------------------------- +// is_command_number - returns whether or not a +// given byte is a number-command +//------------------------------------------------- + +bool pioneer_ldv1000hle_device::is_command_number(const u8 data) +{ + switch (data) + { + case CMD_0: + case CMD_1: + case CMD_2: + case CMD_3: + case CMD_4: + case CMD_5: + case CMD_6: + case CMD_7: + case CMD_8: + case CMD_9: + return true; + } + return false; +} + + +//------------------------------------------------- +// process_command - processes a single command +// from the command buffer +//------------------------------------------------- + +void pioneer_ldv1000hle_device::process_command(size_t cmd_index) +{ + const u8 command = m_cmd_buffer[cmd_index]; + LOGMASKED(LOG_COMMAND_BYTES, "process_command: Command byte %02x with status %02x\n", command, m_status); + if (BIT(m_status, 7)) + { + m_status &= ~STATUS_READY; + + switch (command) + { + case CMD_CLEAR: + LOGMASKED(LOG_COMMANDS, "process_command: Clear\n"); + break; + + case CMD_0: + case CMD_1: + case CMD_2: + case CMD_3: + case CMD_4: + case CMD_5: + case CMD_6: + case CMD_7: + case CMD_8: + case CMD_9: + m_cmd_number[m_cmd_number_length] = cmd_to_number(command); + LOGMASKED(LOG_COMMANDS, "process_command: %d (number length now %d)\n", m_cmd_number[m_cmd_number_length], m_cmd_number_length); + m_cmd_number_length++; + break; + + case CMD_STORE: + if (m_cmd_number_length == 0) + { + LOGMASKED(LOG_COMMANDS, "process_command: Store (no value specified, storing current frame in current register)\n"); + m_user_ram[m_curr_register] = literal_to_bcd(m_curr_frame); + } + else + { + m_user_ram[m_curr_register] = cmd_number_to_bcd(); + LOGMASKED(LOG_COMMANDS, "process_command: Store %05d\n", m_user_ram[m_curr_register]); + } + break; + + case CMD_RECALL: + LOGMASKED(LOG_COMMANDS, "process_command: Recall (not yet implemented)\n"); + break; + + case CMD_DISPLAY: + LOGMASKED(LOG_COMMANDS, "process_command: Display %d (not yet implemented)\n", cmd_number_to_bcd()); + break; + + case CMD_AUDIO1: + if (m_cmd_number_length == 0) + { + m_audio_enable[0] = !m_audio_enable[0]; + LOGMASKED(LOG_COMMANDS, "process_command: Audio1, toggling audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]); + } + else + { + const u32 cmd_number = cmd_number_to_bcd(); + m_audio_enable[0] = BIT(cmd_number, 0); + LOGMASKED(LOG_COMMANDS, "process_command: Audio1, setting audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]); + } + update_audio_enable(); + break; + + case CMD_AUDIO2: + if (m_cmd_number_length == 0) + { + m_audio_enable[1] = !m_audio_enable[1]; + LOGMASKED(LOG_COMMANDS, "process_command: Audio2, toggling audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]); + } + else + { + const u32 cmd_number = cmd_number_to_bcd(); + m_audio_enable[1] = BIT(cmd_number, 0); + LOGMASKED(LOG_COMMANDS, "process_command: Audio2, setting audio enable, channels now %d/%d\n", m_audio_enable[0], m_audio_enable[1]); + } + update_audio_enable(); + break; + + case CMD_PLAY: + LOGMASKED(LOG_COMMANDS, "process_command: Play\n"); + cmd_play(); + break; + + case CMD_STOP: + cmd_stop(); + break; + + case CMD_AUTOSTOP: + { + const u32 value = bcd_to_literal(cmd_number_to_bcd()); + LOGMASKED(LOG_COMMANDS, "process_command: Autostop (at frame %d)\n", m_stop_frame); + if (value < m_curr_frame) + { + // Autostop with a frame number less than the current one will cause a search. + LOGMASKED(LOG_COMMANDS, "process_command: Requested autostop frame is less than current frame (%d), performing a search instead.\n", m_curr_frame); + cmd_search(value); + break; + } + m_status = STATUS_AUTOSTOP; + break; + } + + case CMD_SEARCH: + { + const u32 value = bcd_to_literal(cmd_number_to_bcd()); + LOGMASKED(LOG_COMMANDS, "process_command: Search (to frame %d)\n", value); + cmd_search(value); + break; + } + + case CMD_SCAN_FWD: + case CMD_SCAN_REV: + LOGMASKED(LOG_COMMANDS, "process_command: Scan %s\n", command == CMD_SCAN_FWD ? "Forward" : "Reverse"); + set_mode(command == CMD_SCAN_FWD ? MODE_SCAN_FORWARD : MODE_SCAN_REVERSE); + m_status = STATUS_SCAN; + m_scan_speed = 4000; // "The two SCAN commands move the player's optical head at the rate of approximately 2000 frames per second in the direction specified" + m_scan_speed_accum = 0; + m_stop_frame = ~0U; + m_search_frame = ~0U; + break; + + case CMD_STEP_FWD: + LOGMASKED(LOG_COMMANDS, "process_command: Step Forward\n"); + cmd_step(1); + break; + + case CMD_STEP_REV: + LOGMASKED(LOG_COMMANDS, "process_command: Step Reverse\n"); + cmd_step(-1); + break; + + case CMD_REJECT: + LOGMASKED(LOG_COMMANDS, "process_command: Reject\n"); + set_mode(MODE_PARK); + m_status = STATUS_PARK; + m_stop_frame = ~0U; + m_search_frame = ~0U; + break; + + case CMD_NO_ENTRY: + LOGMASKED(LOG_COMMANDS, "process_command: No Entry\n"); + m_status |= STATUS_READY; + break; + + case CMD_LOAD: + LOGMASKED(LOG_COMMANDS, "process_command: Load (not yet implemented)\n"); + break; + + case CMD_DISPLAY_DISABLE: + LOGMASKED(LOG_COMMANDS, "process_command: Display Disable (not yet implemented)\n"); + break; + + case CMD_DISPLAY_ENABLE: + LOGMASKED(LOG_COMMANDS, "process_command: Display Enable (not yet implemented)\n"); + break; + + case CMD_GET_FRAME_NUM: + LOGMASKED(LOG_COMMANDS, "process_command: Get Frame Number (not yet implemented)\n"); + break; + + case CMD_GET_2ND_DISPLAY: + LOGMASKED(LOG_COMMANDS, "process_command: Get 2nd Display (not yet implemented)\n"); + break; + + case CMD_GET_1ST_DISPLAY: + LOGMASKED(LOG_COMMANDS, "process_command: Get 1st Display (not yet implemented)\n"); + break; + + case CMD_TRANSFER_MEMORY: + LOGMASKED(LOG_COMMANDS, "process_command: Transfer Memory (not yet implemented)\n"); + break; + + case CMD_FWD_X0: + cmd_play_forward(0.0); + break; + + case CMD_FWD_X1_4: + cmd_play_forward(0.25); + break; + + case CMD_FWD_X1_2: + cmd_play_forward(0.5); + break; + + case CMD_FWD_X1: + cmd_play_forward(1.0); + break; + + case CMD_FWD_X2: + cmd_play_forward(2.0); + break; + + case CMD_FWD_X3: + cmd_play_forward(3.0); + break; + + case CMD_FWD_X4: + cmd_play_forward(4.0); + break; + + case CMD_FWD_X5: + cmd_play_forward(5.0); + break; + + case CMD_SKIP_FWD_10: + cmd_skip_forward(10); + break; + + case CMD_SKIP_FWD_20: + cmd_skip_forward(20); + break; + + case CMD_SKIP_FWD_30: + cmd_skip_forward(30); + break; + + case CMD_SKIP_FWD_40: + cmd_skip_forward(40); + break; + + case CMD_SKIP_FWD_50: + cmd_skip_forward(50); + break; + + case CMD_SKIP_FWD_60: + cmd_skip_forward(60); + break; + + case CMD_SKIP_FWD_70: + cmd_skip_forward(70); + break; + + case CMD_SKIP_FWD_80: + cmd_skip_forward(80); + break; + + case CMD_SKIP_FWD_90: + cmd_skip_forward(90); + break; + + case CMD_SKIP_FWD_100: + cmd_skip_forward(100); + break; + } + + if (!is_command_number(command)) + { + m_cmd_number_length = 0; + } + + LOGMASKED(LOG_COMMAND_BYTES, "process_command: Status is now %02x\n", m_status); + } + else if (command == CMD_NO_ENTRY) + { + m_status |= STATUS_READY; + LOGMASKED(LOG_COMMAND_BYTES, "process_command: Received no-entry command while busy, setting ready, status is now %02x\n", m_status); + } + else + { + m_status &= ~STATUS_READY; + LOGMASKED(LOG_COMMAND_BYTES, "process_command: Received command %02x while busy, clearing ready, status is now %02x\n", command, m_status); + } +} + + +//------------------------------------------------- +// bcd_to_literal - converts a BCD value used in +// commands a direct numeric value +//------------------------------------------------- + +u32 pioneer_ldv1000hle_device::bcd_to_literal(u32 bcd) +{ + u32 value = 0; + u32 shift = 28; + u32 multiplier = 10000000; + for (u32 i = 0; i < 8; i++) + { + u32 digit = (bcd >> shift) & 0xf; + bcd &= ~(0xf << shift); + + value += digit * multiplier; + + multiplier /= 10; + shift -= 4; + } + return value; +} + + +//------------------------------------------------- +// literal_to_bcd - converts a literal value +// into a BCD representation for commands +//------------------------------------------------- + +u32 pioneer_ldv1000hle_device::literal_to_bcd(u32 value) +{ + LOGMASKED(LOG_COMMANDS, "literal_to_bcd: Converting %08x to BCD\n", value); + u32 bcd_value = 0; + u32 shift = 28; + u32 multiplier = 10000000; + for (u32 i = 0; i < 8; i++) + { + u32 digit = value / multiplier; + bcd_value |= digit << shift; + + value -= digit * multiplier; + multiplier /= 10; + shift -= 4; + } + LOGMASKED(LOG_COMMANDS, "literal_to_bcd: Result: %08x\n", bcd_value); + return bcd_value; +} + + +//------------------------------------------------- +// literal_to_bcd - converts a literal value +// into a BCD representation for commands +//------------------------------------------------- + +u32 pioneer_ldv1000hle_device::cmd_number_to_bcd() +{ + if (m_cmd_number_length == 0) + { + LOGMASKED(LOG_COMMANDS, "cmd_number_to_bcd: No command number length, returning 0\n"); + return 0; + } + u32 bcd_value = 0; + size_t shift = 0; + for (int i = (int)m_cmd_number_length - 1; i >= 0; i--) + { + bcd_value |= m_cmd_number[i] << shift; + shift += 4; + } + LOGMASKED(LOG_COMMANDS, "cmd_number_to_bcd: %05x\n", bcd_value); + return bcd_value; +} + + +//------------------------------------------------- +// set_mode - set the current high-level player +// mode, updating any enables as necessary +//------------------------------------------------- + +u8 pioneer_ldv1000hle_device::cmd_to_number(const u8 cmd) +{ + switch (cmd) + { + case CMD_0: return 0; + case CMD_1: return 1; + case CMD_2: return 2; + case CMD_3: return 3; + case CMD_4: return 4; + case CMD_5: return 5; + case CMD_6: return 6; + case CMD_7: return 7; + case CMD_8: return 8; + case CMD_9: return 9; + } + return 0; +} + + +//------------------------------------------------- +// set_mode - set the current high-level player +// mode, updating any enables as necessary +//------------------------------------------------- + +void pioneer_ldv1000hle_device::set_mode(const u8 mode) +{ + if (m_mode == mode) + { + return; + } + + m_mode = mode; + if (mode != MODE_PARK) + { + m_park_strobe_timer->adjust(attotime::never); + } + else + { + m_park_strobe_timer->adjust(attotime::from_msec(21), 0, attotime::from_msec(21)); + } + + update_video_enable(); + update_audio_enable(); +} + + +//------------------------------------------------- +// set_playing - general-purpose function for +// setting a forward play mode +//------------------------------------------------- + +void pioneer_ldv1000hle_device::set_playing(const u8 new_status, const double fields_per_vsync) +{ + set_mode(MODE_PLAY); + m_status = new_status; + m_play_speed = fields_per_vsync; + m_search_frame = ~0U; + m_stop_frame = ~0U; +} + + +//------------------------------------------------- +// set_playing - general-purpose function for +// setting a forward play mode +//------------------------------------------------- + +void pioneer_ldv1000hle_device::set_stopped(const u8 new_status) +{ + set_mode(MODE_STOP); + m_status = new_status; +} + + +//------------------------------------------------- +// cmd_play - play forward at 1x speed +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_play() +{ + LOGMASKED(LOG_COMMANDS, "process_command: Play\n"); + set_slider_speed(0); + set_playing(STATUS_PLAY, 1.0); +} + + +//------------------------------------------------- +// cmd_stop - stop/pause/freeze-frame +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_stop() +{ + if (m_cmd_number_length > 0) + { + const u32 command_value = cmd_number_to_bcd(); + m_stop_timer->adjust(attotime::from_msec(100 * command_value)); + m_pre_stop_status = m_status; + LOGMASKED(LOG_COMMANDS, "process_command: Stop (for %d.%d seconds)\n", command_value / 10, command_value % 10); + } + else + { + LOGMASKED(LOG_COMMANDS, "process_command: Stop\n"); + } + set_stopped(STATUS_STOP); +} + + +//------------------------------------------------- +// cmd_step - step one frame forward or backward +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_step(const int direction) +{ + advance_slider(direction); + set_stopped(STATUS_STOP); + m_stop_frame = ~0U; + m_search_frame = ~0U; +} + + +//------------------------------------------------- +// cmd_play_forward - play forward at a specific +// multiplier +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_play_forward(const double fields_per_vsync) +{ + LOGMASKED(LOG_COMMANDS, "process_command: Forward x%f\n", fields_per_vsync); + set_slider_speed(fields_per_vsync); + set_playing(STATUS_FORWARD, fields_per_vsync); +} + + +//------------------------------------------------- +// cmd_search - begin a search operation to a +// specific frame +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_search(const u32 frame) +{ + set_mode(MODE_SEARCH); + m_status = STATUS_SEARCH; + m_search_frame = frame; + m_stop_frame = ~0U; +} + + +//------------------------------------------------- +// cmd_skip_forward - skip forward a specific +// number of frames while in forward-play mode +//------------------------------------------------- + +void pioneer_ldv1000hle_device::cmd_skip_forward(const s32 amount) +{ + LOGMASKED(LOG_COMMANDS, "Command: Skip Forward %d\n", amount); + set_mode(MODE_SEARCH); + m_search_frame = ~0U; + m_stop_frame = m_curr_frame + amount; + m_scan_speed = amount; +} + + +//------------------------------------------------- +// update_video_enable - set video enable state +// on the base device based on our video switch +//------------------------------------------------- + +void pioneer_ldv1000hle_device::update_video_enable() +{ + LOGMASKED(LOG_SQUELCHES, "%s: Updating video enable (mode %d), video %s enabled\n", machine().describe_context(), m_mode, m_mode != MODE_PARK ? "is" : "is not"); + video_enable(m_mode == MODE_PLAY || m_mode == MODE_STOP); +} + + +//------------------------------------------------- +// update_audio_enable - set audio enable state +// depending on our current mode or channel mute +//------------------------------------------------- + +void pioneer_ldv1000hle_device::update_audio_enable() +{ + if (m_mode == MODE_PLAY && m_play_speed == 1.0) + { + LOGMASKED(LOG_SQUELCHES, "%s: Updating audio enable (playing at 1x, channels %d/%d)\n", machine().describe_context(), m_audio_enable[0], m_audio_enable[1]); + set_audio_squelch(!m_audio_enable[0], !m_audio_enable[1]); + } + else + { + LOGMASKED(LOG_SQUELCHES, "%s: Updating audio enable (muted, mode %d, speed %f)\n", machine().describe_context(), m_mode, m_play_speed); + set_audio_squelch(true, true); + } +} diff --git a/src/devices/machine/ldv1000hle.h b/src/devices/machine/ldv1000hle.h new file mode 100644 index 00000000000..8484e27825b --- /dev/null +++ b/src/devices/machine/ldv1000hle.h @@ -0,0 +1,201 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/************************************************************************* + + ldv1000hle.h + + Pioneer LDV-1000 laserdisc player simulation. + +*************************************************************************/ + +#ifndef MAME_MACHINE_LDV1000HLE_H +#define MAME_MACHINE_LDV1000HLE_H + +#pragma once + +#include "laserdsc.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DECLARE_DEVICE_TYPE(PIONEER_LDV1000HLE, pioneer_ldv1000hle_device) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class pioneer_ldv1000hle_device : public parallel_laserdisc_device +{ +public: + // construction/destruction + pioneer_ldv1000hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + virtual void data_w(u8 data) override; + virtual u8 data_r() override; + virtual void enter_w(int state) override; + virtual int status_strobe_r() override { return m_status_strobe; } + virtual int ready_r() override { return m_command_strobe; } + +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; + + TIMER_CALLBACK_MEMBER(process_vbi_data); + TIMER_CALLBACK_MEMBER(resume_from_stop); + TIMER_CALLBACK_MEMBER(park_strobe_tick); + TIMER_CALLBACK_MEMBER(assert_status_strobe); + TIMER_CALLBACK_MEMBER(deassert_status_strobe); + TIMER_CALLBACK_MEMBER(assert_command_strobe); + TIMER_CALLBACK_MEMBER(deassert_command_strobe); + +private: + enum player_command : u8 + { + CMD_CLEAR = 0xbf, + CMD_0 = 0x3f, + CMD_1 = 0x0f, + CMD_2 = 0x8f, + CMD_3 = 0x4f, + CMD_4 = 0x2f, + CMD_5 = 0xaf, + CMD_6 = 0x6f, + CMD_7 = 0x1f, + CMD_8 = 0x9f, + CMD_9 = 0x5f, + CMD_STORE = 0xf5, + CMD_RECALL = 0x7f, + CMD_DISPLAY = 0xf1, + CMD_AUDIO1 = 0xf4, + CMD_AUDIO2 = 0xfc, + CMD_PLAY = 0xfd, + CMD_STOP = 0xfb, + CMD_AUTOSTOP = 0xf3, + CMD_SEARCH = 0xf7, + CMD_SCAN_FWD = 0xf0, + CMD_SCAN_REV = 0xf8, + CMD_STEP_FWD = 0xf6, + CMD_STEP_REV = 0xfe, + CMD_REJECT = 0xf9, + CMD_NO_ENTRY = 0xff, + CMD_LOAD = 0xcc, + CMD_DISPLAY_DISABLE = 0xcd, + CMD_DISPLAY_ENABLE = 0xce, + CMD_GET_FRAME_NUM = 0xc2, + CMD_GET_2ND_DISPLAY = 0xc3, + CMD_GET_1ST_DISPLAY = 0xc4, + CMD_TRANSFER_MEMORY = 0xc8, + CMD_FWD_X0 = 0xa0, + CMD_FWD_X1_4 = 0xa1, + CMD_FWD_X1_2 = 0xa2, + CMD_FWD_X1 = 0xa3, + CMD_FWD_X2 = 0xa4, + CMD_FWD_X3 = 0xa5, + CMD_FWD_X4 = 0xa6, + CMD_FWD_X5 = 0xa7, + CMD_SKIP_FWD_10 = 0xb1, + CMD_SKIP_FWD_20 = 0xb2, + CMD_SKIP_FWD_30 = 0xb3, + CMD_SKIP_FWD_40 = 0xb4, + CMD_SKIP_FWD_50 = 0xb5, + CMD_SKIP_FWD_60 = 0xb6, + CMD_SKIP_FWD_70 = 0xb7, + CMD_SKIP_FWD_80 = 0xb8, + CMD_SKIP_FWD_90 = 0xb9, + CMD_SKIP_FWD_100 = 0xba + }; + + enum player_status : u8 + { + STATUS_PARK = 0x7c, + STATUS_PLAY = 0x64, + STATUS_STOP = 0x65, + STATUS_SEARCH = 0x50, + STATUS_SEARCH_FINISH = 0xd0, + STATUS_SEARCH_ERROR = 0x90, + STATUS_AUTOSTOP = 0x54, + STATUS_SCAN = 0x4c, + STATUS_FORWARD = 0x2e, + STATUS_LOAD = 0x48, + STATUS_LOAD_END = 0xc8, + STATUS_LOAD_ERROR = 0xc4, + STATUS_FOCUS_UNLOCK = 0xbc, + STATUS_LEADIN = 0x58, + STATUS_LEADOUT = 0x5c, + STATUS_REJECT = 0x60, + STATUS_READY = 0x80 + }; + + enum player_mode : u8 + { + MODE_PARK, + MODE_PLAY, + MODE_SCAN_FORWARD, + MODE_SCAN_REVERSE, + MODE_SEARCH, + MODE_STOP + }; + + void process_command_buffer(); + bool is_command_byte(const u8 data); + bool is_command_number(const u8 data); + void process_command(size_t cmd_index); + + static u32 bcd_to_literal(u32 bcd); + u32 literal_to_bcd(u32 value); + u32 cmd_number_to_bcd(); + u8 cmd_to_number(const u8 cmd); + + void set_mode(const u8 mode); + void set_playing(const u8 new_status, const double fields_per_vsync); + void set_stopped(const u8 new_status); + void cmd_play(); + void cmd_stop(); + void cmd_step(const int direction); + void cmd_play_forward(const double fields_per_vsync); + void cmd_search(const u32 frame); + void cmd_skip_forward(const s32 amount); + + void update_video_enable(); + void update_audio_enable(); + + // internal state + emu_timer * m_vbi_fetch; + emu_timer * m_stop_timer; + emu_timer * m_park_strobe_timer; + emu_timer * m_assert_status_strobe_timer; + emu_timer * m_deassert_status_strobe_timer; + emu_timer * m_assert_command_strobe_timer; + emu_timer * m_deassert_command_strobe_timer; + u8 m_cmd_buffer[21]; + u32 m_cmd_length; + u8 m_status; + u8 m_pre_stop_status; + + u8 m_mode; // current player mode + u32 m_curr_frame; // frame number + u32 m_search_frame; + u32 m_stop_frame; + u8 m_cmd_number[5]; + u32 m_cmd_number_length; + + u16 m_user_ram[1024]; + u32 m_curr_register; + u32 m_scan_speed; + u32 m_scan_speed_accum; + double m_play_speed; + bool m_audio_enable[2]; + bool m_status_strobe; + bool m_command_strobe; +}; + +#endif // MAME_MACHINE_LDV1000HLE_H diff --git a/src/devices/machine/ldv4200hle.h b/src/devices/machine/ldv4200hle.h index c7ee5738e2a..3c282667010 100644 --- a/src/devices/machine/ldv4200hle.h +++ b/src/devices/machine/ldv4200hle.h @@ -2,7 +2,7 @@ // copyright-holders:Ryan Holtz /************************************************************************* - ldv1000hle.h + ldv4200hle.h Pioneer LD-V4200 laserdisc player simulation. diff --git a/src/devices/sound/ssi263hle.cpp b/src/devices/sound/ssi263hle.cpp new file mode 100644 index 00000000000..738b6c61084 --- /dev/null +++ b/src/devices/sound/ssi263hle.cpp @@ -0,0 +1,230 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/*************************************************************************** + + Silicon Systems SSI-263A Phoneme Speech Synthesizer + + Temporary implementation using the Votrax SC-01A + + NOTE: This is completely wrong, and exists only to have + working audio in Thayer's Quest, which would not otherwise + be playable due to relying on speech output for important + gameplay cues. + +****************************************************************************/ + +#include "emu.h" + +#include "ssi263hle.h" + + +DEFINE_DEVICE_TYPE(SSI263HLE, ssi263hle_device, "ssi263hle", "SSI-263A Speech Synthesizer") + +namespace +{ + +static const char PHONEME_NAMES[0x40][5] = +{ + "PA", "E", "E1", "Y", "YI", "AY", "IE", "I", "A", "AI", "EH", "EH1", "AE", "AE1", "AH", "AH1", "W", "O", "OU", "OO", "IU", "IU1", "U", "U1", "UH", "UH1", "UH2", "UH3", "ER", "R", "R1", "R2", + "L", "L1", "LF", "W", "B", "D", "KV", "P", "T", "K", "HV", "HVC", "HF", "HFC", "HN", "Z", "S", "J", "SCH", "V", "F", "THV", "TH", "M", "N", "NG", ":A", ":OH", ":U", ":UH", "E2", "LB" +}; + +static const u8 PHONEMES_TO_SC01[0x40] = +{ + 0x03, 0x2c, 0x3b, 0x3c, 0x22, 0x21, 0x29, 0x27, 0x20, 0x05, 0x01, 0x00, 0x2e, 0x2f, 0x15, 0x15, + 0x13, 0x26, 0x35, 0x17, 0x36, 0x16, 0x28, 0x37, 0x32, 0x32, 0x31, 0x23, 0x3a, 0x2b, 0x2b, 0x2b, + 0x18, 0x18, 0x18, 0x2d, 0x0e, 0x1e, 0x1c, 0x25, 0x2a, 0x19, 0x03, 0x03, 0x1b, 0x03, 0x03, 0x12, + 0x1f, 0x07, 0x11, 0x0f, 0x1d, 0x38, 0x39, 0x0c, 0x0d, 0x14, 0x08, 0x34, 0x28, 0x37, 0x02, 0x18 +}; + +} // anonymous namespace + +ssi263hle_device::ssi263hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, SSI263HLE, tag, owner, clock) + , device_mixer_interface(mconfig, *this, 1) + , m_votrax(*this, "votrax") + , m_ar_cb(*this) + , m_phoneme_timer(nullptr) + , m_duration(0) + , m_phoneme(0) + , m_inflection(0) + , m_rate(0) + , m_articulation(0) + , m_control(false) + , m_amplitude(0) + , m_filter(0) + , m_mode(0) + , m_data_request(1) + , m_votrax_fifo_wr(0) + , m_votrax_fifo_rd(0) + , m_votrax_fifo_cnt(0) +{ +} + +void ssi263hle_device::device_start() +{ + m_phoneme_timer = timer_alloc(FUNC(ssi263hle_device::phoneme_tick), this); + + save_item(NAME(m_duration)); + save_item(NAME(m_phoneme)); + save_item(NAME(m_inflection)); + save_item(NAME(m_rate)); + save_item(NAME(m_articulation)); + save_item(NAME(m_control)); + save_item(NAME(m_amplitude)); + save_item(NAME(m_filter)); + save_item(NAME(m_mode)); + + save_item(NAME(m_votrax_fifo)); + save_item(NAME(m_votrax_fifo_wr)); + save_item(NAME(m_votrax_fifo_rd)); + save_item(NAME(m_votrax_fifo_cnt)); +} + +void ssi263hle_device::device_reset() +{ + m_phoneme_timer->adjust(attotime::never); + + m_duration = 0; + m_phoneme = 0; + m_inflection = 0; + m_rate = 0; + m_articulation = 0; + m_control = false; + m_amplitude = 0; + m_filter = 0; + m_mode = 0; + + m_data_request = 1; + + std::fill(std::begin(m_votrax_fifo), std::end(m_votrax_fifo), 0); + m_votrax_fifo_wr = 0; + m_votrax_fifo_rd = 0; + m_votrax_fifo_cnt = 0; +} + +void ssi263hle_device::map(address_map &map) +{ + map(0x00, 0x00).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::duration_phoneme_w)); + map(0x01, 0x01).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::inflection_w)); + map(0x02, 0x02).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::rate_inflection_w)); + map(0x03, 0x03).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::control_articulation_amplitude_w)); + map(0x04, 0x07).rw(FUNC(ssi263hle_device::status_r), FUNC(ssi263hle_device::filter_frequency_w)); +} + +void ssi263hle_device::device_add_mconfig(machine_config &config) +{ + VOTRAX_SC01(config, m_votrax, DERIVED_CLOCK(1, 1)); + m_votrax->ar_callback().set(FUNC(ssi263hle_device::votrax_request)); + m_votrax->add_route(ALL_OUTPUTS, *this, 1.0, AUTO_ALLOC_INPUT, 0); +} + +TIMER_CALLBACK_MEMBER(ssi263hle_device::phoneme_tick) +{ + m_data_request = 0; + m_ar_cb(m_data_request); +} + +void ssi263hle_device::duration_phoneme_w(u8 data) +{ + const int frame_time = ((4096 * (16 - m_rate)) / 2); // microseconds, should actually be derived from our clock, but this way we get microseconds directly + const int phoneme_time = frame_time * (4 - m_duration); // microseconds + + m_duration = (data >> 5) & 0x03; + m_phoneme = data & 0x3f; + + m_data_request = 1; + m_ar_cb(m_data_request); + + switch (m_mode) + { + case 0: + case 1: + // phoneme timing response + m_phoneme_timer->adjust(attotime::from_usec(phoneme_time)); + break; + case 2: + // frame timing response + m_phoneme_timer->adjust(attotime::from_usec(frame_time)); + break; + case 3: + // disable A/_R output + break; + } + + if (m_phoneme) + { + if (m_votrax_fifo_cnt < std::size(m_votrax_fifo)) + { + m_votrax_fifo[m_votrax_fifo_wr] = PHONEMES_TO_SC01[m_phoneme]; + if (m_votrax_fifo_cnt == 0) + { + m_votrax->write(PHONEMES_TO_SC01[m_phoneme]); + } + m_votrax_fifo_wr = (m_votrax_fifo_wr + 1) % std::size(m_votrax_fifo); + m_votrax_fifo_cnt++; + } + } +} + +void ssi263hle_device::inflection_w(u8 data) +{ + m_inflection &= 0x807; + m_inflection |= data << 3; +} + +void ssi263hle_device::rate_inflection_w(u8 data) +{ + m_inflection &= 0x7f8; + m_inflection |= (BIT(data, 3) << 11) | (data & 0x07); + m_rate = data >> 4; + m_votrax->inflection_w(1); +} + +void ssi263hle_device::control_articulation_amplitude_w(u8 data) +{ + if (m_control && !BIT(data, 7)) + { + m_mode = m_duration; + } + + m_control = BIT(data, 7); + m_articulation = (data >> 4) & 0x07; + m_amplitude = data & 0x0f; +} + +void ssi263hle_device::filter_frequency_w(u8 data) +{ + m_filter = data; +} + +u8 ssi263hle_device::status_r() +{ + // D7 is an output for the inverted state of A/_R. Register address bits are ignored. + return BIT(~m_data_request, 0) << 7; +} + +void ssi263hle_device::votrax_request(int state) +{ + if (m_votrax_fifo_cnt == 0 || state != ASSERT_LINE) + { + return; + } + + m_votrax_fifo_cnt--; + const u8 previous_phoneme = m_votrax_fifo[m_votrax_fifo_rd]; + m_votrax_fifo_rd = (m_votrax_fifo_rd + 1) % std::size(m_votrax_fifo); + if (m_votrax_fifo_cnt == 0) + { + if (previous_phoneme != 0x3f) + { + m_votrax_fifo[m_votrax_fifo_wr] = 0x3f; + m_votrax_fifo_wr = (m_votrax_fifo_wr + 1) % std::size(m_votrax_fifo); + m_votrax_fifo_cnt++; + m_votrax->write(0x3f); + } + return; + } + + m_votrax->write(m_votrax_fifo[m_votrax_fifo_rd]); +} diff --git a/src/devices/sound/ssi263hle.h b/src/devices/sound/ssi263hle.h new file mode 100644 index 00000000000..48b4a8ccb18 --- /dev/null +++ b/src/devices/sound/ssi263hle.h @@ -0,0 +1,76 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/********************************************************************** + + Silicon Systems SSI-263A Phoneme Speech Synthesizer + + Temporary implementation using the Votrax SC-01A + + NOTE: This is completely wrong, and exists only to have + working audio in Thayer's Quest, which would not otherwise + be playable due to relying on speech output for important + gameplay cues. + +**********************************************************************/ + +#ifndef MAME_SOUND_SSI263HLE_H +#define MAME_SOUND_SSI263HLE_H + +#pragma once + +#include "sound/votrax.h" + +class ssi263hle_device : public device_t, public device_mixer_interface +{ +public: + static constexpr feature_type imperfect_features() { return feature::SOUND; } + + ssi263hle_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + + void map(address_map &map); + + auto ar_callback() { return m_ar_cb.bind(); } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + required_device m_votrax; + + TIMER_CALLBACK_MEMBER(phoneme_tick); + + void duration_phoneme_w(u8 data); + void inflection_w(u8 data); + void rate_inflection_w(u8 data); + void control_articulation_amplitude_w(u8 data); + void filter_frequency_w(u8 data); + u8 status_r(); + + void votrax_request(int state); + + devcb_write_line m_ar_cb; + + emu_timer *m_phoneme_timer = nullptr; + + u8 m_duration; + u8 m_phoneme; + u16 m_inflection; + u8 m_rate; + u8 m_articulation; + bool m_control; + u8 m_amplitude; + u8 m_filter; + u8 m_mode; + u8 m_data_request; + + u8 m_votrax_fifo[1024]; + u32 m_votrax_fifo_wr; + u32 m_votrax_fifo_rd; + u32 m_votrax_fifo_cnt; +}; + +DECLARE_DEVICE_TYPE(SSI263HLE, ssi263hle_device) + +#endif diff --git a/src/mame/cinematronics/dlair.cpp b/src/mame/cinematronics/dlair.cpp index d480b9fcfb7..9c29f8c7235 100644 --- a/src/mame/cinematronics/dlair.cpp +++ b/src/mame/cinematronics/dlair.cpp @@ -96,7 +96,7 @@ private: uint8_t laserdisc_data_r() { - if (m_ldv1000 != nullptr) return m_ldv1000->status_r(); + if (m_ldv1000 != nullptr) return m_ldv1000->data_r(); if (m_pr7820 != nullptr) return m_pr7820->data_r(); if (m_22vp932 != nullptr) return m_22vp932->data_r(); return 0; @@ -115,7 +115,7 @@ private: uint8_t laserdisc_ready_r() { - if (m_ldv1000 != nullptr) return m_ldv1000->command_strobe_r(); + if (m_ldv1000 != nullptr) return m_ldv1000->ready_r(); if (m_pr7820 != nullptr) return m_pr7820->ready_r(); return CLEAR_LINE; } diff --git a/src/mame/konami/konblands.cpp b/src/mame/konami/konblands.cpp index a021c3c8c13..891b4b0529a 100644 --- a/src/mame/konami/konblands.cpp +++ b/src/mame/konami/konblands.cpp @@ -126,7 +126,7 @@ uint32_t konblands_state::screen_update(screen_device &screen, bitmap_rgb32 &bit uint8_t konblands_state::ldp_r() { - return m_laserdisc->status_r(); + return m_laserdisc->data_r(); } void konblands_state::nmi_enable_w(uint8_t data) diff --git a/src/mame/layout/thayers.lay b/src/mame/layout/thayers.lay index 0c7667fdfdc..a452bba02e1 100644 --- a/src/mame/layout/thayers.lay +++ b/src/mame/layout/thayers.lay @@ -9,78 +9,16 @@ license:CC0-1.0 - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/mame/misc/esh.cpp b/src/mame/misc/esh.cpp index 5162f10194d..7048d52296e 100644 --- a/src/mame/misc/esh.cpp +++ b/src/mame/misc/esh.cpp @@ -148,7 +148,7 @@ uint32_t esh_state::screen_update_esh(screen_device &screen, bitmap_rgb32 &bitma uint8_t esh_state::ldp_read() { - return m_laserdisc->status_r(); + return m_laserdisc->data_r(); } void esh_state::misc_write(uint8_t data) diff --git a/src/mame/misc/istellar.cpp b/src/mame/misc/istellar.cpp index abdbeab2fa4..bb5a82355ae 100644 --- a/src/mame/misc/istellar.cpp +++ b/src/mame/misc/istellar.cpp @@ -177,7 +177,7 @@ void istellar_state::machine_start() // Z80 2 R/W uint8_t istellar_state::z80_2_ldp_read() { - uint8_t readResult = m_laserdisc->status_r(); + uint8_t readResult = m_laserdisc->data_r(); LOGCPU2("CPU2 : reading LDP : %x\n", readResult); return readResult; } diff --git a/src/mame/misc/thayers.cpp b/src/mame/misc/thayers.cpp index 581b4935232..786fd1e7bce 100644 --- a/src/mame/misc/thayers.cpp +++ b/src/mame/misc/thayers.cpp @@ -1,63 +1,56 @@ // license:BSD-3-Clause // copyright-holders:Andrew Gardner -/* +/************************************************************************* - TODO: + RDI Video Sytems' Thayer's Quest + +************************************************************************** - - LDV1000 mode - - PR7820 INT/_EXT line - - coin counter - - convert SSI-263 to a sound device - - dump laserdisc + TODO: + - No viable solution for reading configuration DIPs at init time, + so the driver is hard-coded to LD-V1000 mode. -*/ +*************************************************************************/ #include "emu.h" #include "cpu/cop400/cop400.h" #include "cpu/z80/z80.h" #include "machine/ldstub.h" -#include "machine/ldv1000.h" +#include "machine/ldv1000hle.h" +#include "sound/ssi263hle.h" #include "speaker.h" #include "thayers.lh" +#define LOG_IRQS (1U << 1) +#define LOG_COP (1U << 2) +#define LOG_KEYBOARD (1U << 3) +#define LOG_PLAYER (1U << 4) +#define LOG_COINS (1U << 5) +#define LOG_ALL (LOG_IRQS | LOG_COP | LOG_KEYBOARD | LOG_PLAYER | LOG_COINS) -namespace { - - -#define LOG 0 +#define VERBOSE (0) +#include "logmacro.h" -struct ssi263_t -{ - uint8_t dr = 0; - uint8_t p = 0; - uint16_t i = 0; - uint8_t r = 0; - uint8_t t = 0; - uint8_t c = 0; - uint8_t a = 0; - uint8_t f = 0; - uint8_t mode = 0; -}; +namespace { class thayers_state : public driver_device { public: thayers_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) - , m_pr7820(*this, "laserdisc") - , m_ldv1000(*this, "ldv1000") + , m_player(*this, "laserdisc") , m_maincpu(*this, "maincpu") + , m_ssi(*this, "ssi") , m_row(*this, "ROW.%u", 0) + , m_coins(*this, "COIN") + , m_dswb(*this, "DSWB") , m_digits(*this, "digit%u", 0U) { } void thayers(machine_config &config); - int laserdisc_enter_r(); - int laserdisc_ready_r(); - private: virtual void machine_start() override; virtual void machine_reset() override; @@ -65,280 +58,344 @@ private: void thayers_map(address_map &map); void thayers_io_map(address_map &map); + TIMER_CALLBACK_MEMBER(firstirq_tick); TIMER_CALLBACK_MEMBER(intrq_tick); - TIMER_CALLBACK_MEMBER(phoneme_tick); + void ssi_data_request_w(int state); - optional_device m_pr7820; - optional_device m_ldv1000; - required_device m_maincpu; + required_device m_player; + required_device m_maincpu; + required_device m_ssi; required_ioport_array<10> m_row; + required_ioport m_coins; + required_ioport m_dswb; output_finder<16> m_digits; emu_timer *m_intrq_timer = nullptr; emu_timer *m_phoneme_timer = nullptr; - uint8_t m_laserdisc_data = 0; - int m_rx_bit = 0; - int m_keylatch = 0; - uint8_t m_keydata = 0; - bool m_kbdata = false; - bool m_kbclk = false; - uint8_t m_cop_data_latch = 0; - int m_cop_data_latch_enable = 0; - uint8_t m_cop_l = 0; - uint8_t m_cop_cmd_latch = 0; - int m_timer_int = 0; - int m_data_rdy_int = 0; - int m_ssi_data_request = 0; - int m_cart_present = 0; - int m_pr7820_enter = 0; - struct ssi263_t m_ssi263; - - void intrq_w(uint8_t data); - uint8_t irqstate_r(); - void timer_int_ack_w(uint8_t data); - void data_rdy_int_ack_w(uint8_t data); - void cop_d_w(uint8_t data); - uint8_t cop_data_r(); - void cop_data_w(uint8_t data); - uint8_t cop_l_r(); - void cop_l_w(uint8_t data); - uint8_t cop_g_r(); - void control_w(uint8_t data); - void cop_g_w(uint8_t data); + + u8 m_laserdisc_data = 0; + u8 m_laserdisc_control = 0; + + u8 m_rx_bit = 0; + u8 m_keylatch = 0; + u8 m_keydata = 0; + u8 m_kbdata = 0; + u8 m_kbclk = 0; + + u8 m_cop_data_latch = 0; + u8 m_cop_data_latch_enable = 0; + u8 m_cop_l = 0; + u8 m_cop_cmd_latch = 0; + + u8 m_z80_int = 0; + u8 m_periodic_int = 0; + u8 m_timer_int = 1; + u8 m_data_rdy_int = 1; + u8 m_ssi_data_request = 1; + u8 m_cart_present = 1; + + void periodic_int_ack_w(u8 data); + u8 irqstate_r(); + void timer_int_ack_w(u8 data); + void data_rdy_int_ack_w(u8 data); + void check_interrupt(); + + void cop_d_w(u8 data); + u8 cop_data_r(); + void cop_data_w(u8 data); + u8 cop_l_r(); + void cop_l_w(u8 data); + u8 cop_g_r(); + void cop_g_w(u8 data); + + void control_w(u8 data); + void control2_w(u8 data); + u8 dsw_b_r(); + int kbdata_r(); void kbclk_w(int state); - void control2_w(uint8_t data); - uint8_t dsw_b_r(); - uint8_t laserdsc_data_r(); - void laserdsc_data_w(uint8_t data); - void laserdsc_control_w(uint8_t data); - void den1_w(uint8_t data); - void den2_w(uint8_t data); - void ssi263_register_w(offs_t offset, uint8_t data); - uint8_t ssi263_register_r(); - void check_interrupt(); + u8 laserdisc_data_r(); + void laserdisc_data_w(u8 data); + void laserdisc_control_w(u8 data); + + void den1_w(u8 data); + void den2_w(u8 data); + + static constexpr u8 LED_MAP[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x00 }; }; -static const uint8_t led_map[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x00 }; +void thayers_state::machine_start() +{ + m_digits.resolve(); + + m_intrq_timer = timer_alloc(FUNC(thayers_state::intrq_tick), this); + + save_item(NAME(m_laserdisc_data)); + save_item(NAME(m_laserdisc_control)); + + save_item(NAME(m_rx_bit)); + save_item(NAME(m_keylatch)); + save_item(NAME(m_keydata)); + save_item(NAME(m_kbdata)); + save_item(NAME(m_kbclk)); + + save_item(NAME(m_cop_data_latch)); + save_item(NAME(m_cop_data_latch_enable)); + save_item(NAME(m_cop_l)); + save_item(NAME(m_cop_cmd_latch)); + + save_item(NAME(m_z80_int)); + save_item(NAME(m_periodic_int)); + save_item(NAME(m_timer_int)); + save_item(NAME(m_data_rdy_int)); + save_item(NAME(m_ssi_data_request)); + save_item(NAME(m_cart_present)); +} + +void thayers_state::machine_reset() +{ + m_intrq_timer->adjust(attotime::never); + + m_laserdisc_data = 0; + m_laserdisc_control = 0; + + m_rx_bit = 0; + m_keylatch = 0; + m_keydata = m_row[m_keylatch]->read(); + m_kbdata = 1; + m_kbclk = 0; + + m_cop_data_latch = 0; + m_cop_data_latch_enable = 0; + m_cop_l = 0; + m_cop_cmd_latch = 0; + + m_z80_int = 0; + m_periodic_int = 0; + m_timer_int = 1; + m_data_rdy_int = 1; + m_ssi_data_request = 1; + m_cart_present = 1; +} -/* Interrupts */ TIMER_CALLBACK_MEMBER(thayers_state::intrq_tick) { - m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + m_periodic_int = 0; + check_interrupt(); } -TIMER_CALLBACK_MEMBER(thayers_state::phoneme_tick) +void thayers_state::ssi_data_request_w(int state) { - m_ssi_data_request = 0; + m_ssi_data_request = state; check_interrupt(); } void thayers_state::check_interrupt() { - if (!m_timer_int || !m_data_rdy_int || !m_ssi_data_request) - { - m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); - } + const bool old_int = m_z80_int; + m_z80_int = (!m_timer_int || !m_data_rdy_int || !m_ssi_data_request || !m_periodic_int); + if (m_z80_int == old_int) + return; + + if (m_z80_int) + LOGMASKED(LOG_IRQS, "%s: Raising Z80 IRQ (timer %d, data %d, SSI %d, periodic %d)\n", machine().describe_context(), m_timer_int, m_data_rdy_int, m_ssi_data_request, m_periodic_int); else - { - m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); - } + LOGMASKED(LOG_IRQS, "%s: Clearing Z80 IRQ\n", machine().describe_context()); + + m_maincpu->set_input_line(INPUT_LINE_IRQ0, m_z80_int ? ASSERT_LINE : CLEAR_LINE); } -void thayers_state::intrq_w(uint8_t data) +void thayers_state::periodic_int_ack_w(u8 data) { // T = 1.1 * R30 * C53 = 1.1 * 750K * 0.01uF = 8.25 ms - - m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); + m_periodic_int = 1; + check_interrupt(); m_intrq_timer->adjust(attotime::from_usec(8250)); + LOGMASKED(LOG_IRQS, "%s: periodic_int_ack_w: %02x\n", machine().describe_context(), data); } -uint8_t thayers_state::irqstate_r() +u8 thayers_state::irqstate_r() { - /* - - bit description - - 0 - 1 - 2 SSI263 A/_R - 3 tied to +5V - 4 _TIMER INT - 5 _DATA RDY INT - 6 _CART PRES - 7 + // bit description + // + // 0 + // 1 + // 2 SSI263 A/_R + // 3 tied to +5V + // 4 _TIMER INT + // 5 _DATA RDY INT + // 6 _CART PRES + // 7 - */ - - return m_cart_present << 6 | (m_data_rdy_int << 5) | (m_timer_int << 4) | 0x08 | (m_ssi_data_request << 2); + const u8 data = (m_cart_present << 6) | (m_data_rdy_int << 5) | (m_timer_int << 4) | 0x08 | (m_ssi_data_request << 2); + LOGMASKED(LOG_IRQS, "%s: irqstate_r: %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_IRQS, "%s: SSI263 A/_R: %d\n", machine().describe_context(), BIT(data, 2)); + LOGMASKED(LOG_IRQS, "%s: _TIMER_INT: %d\n", machine().describe_context(), BIT(data, 4)); + LOGMASKED(LOG_IRQS, "%s: _DATA_RDY_INT: %d\n", machine().describe_context(), BIT(data, 5)); + LOGMASKED(LOG_IRQS, "%s: _CART_PRES: %d\n", machine().describe_context(), BIT(data, 6)); + return data; } -void thayers_state::timer_int_ack_w(uint8_t data) +void thayers_state::timer_int_ack_w(u8 data) { - if (LOG) logerror("%s %s TIMER INT ACK\n", machine().time().as_string(), machine().describe_context()); - + LOGMASKED(LOG_IRQS, "%s: timer_int_ack_w: %02x\n", machine().describe_context(), data); m_timer_int = 1; - check_interrupt(); } -void thayers_state::data_rdy_int_ack_w(uint8_t data) +void thayers_state::data_rdy_int_ack_w(u8 data) { - if (LOG) logerror("%s %s DATA RDY INT ACK\n", machine().time().as_string(), machine().describe_context()); - + LOGMASKED(LOG_IRQS, "%s: data_rdy_int_ack_w: %02x\n", machine().describe_context(), data); m_data_rdy_int = 1; - check_interrupt(); } -void thayers_state::cop_d_w(uint8_t data) +void thayers_state::cop_d_w(u8 data) { - /* - - bit description - - D0 _TIMER INT - D1 _DATA RDY INT - D2 - D3 - - */ + // bit description + // + // D0 _TIMER INT + // D1 _DATA RDY INT + // D2 + // D3 + LOGMASKED(LOG_COP, "%s: cop_d_w = %02x\n", machine().describe_context(), data); if (!BIT(data, 0)) { - if (LOG) logerror("%s %s TIMER INT\n", machine().time().as_string(), machine().describe_context()); + LOGMASKED(LOG_COP | LOG_IRQS, "%s: /TIMER_INT: %d\n", machine().describe_context(), BIT(data, 0)); m_timer_int = 0; } if (!BIT(data, 1)) { - if (LOG) logerror("%s %s DATA RDY INT\n", machine().time().as_string(), machine().describe_context()); + LOGMASKED(LOG_COP | LOG_IRQS, "%s: /DATA_RDY_INT: %d\n", machine().describe_context(), BIT(data, 1)); m_data_rdy_int = 0; } check_interrupt(); } -/* COP Communication */ -uint8_t thayers_state::cop_data_r() +u8 thayers_state::cop_data_r() { + u8 data = m_cop_l; if (!m_cop_data_latch_enable) { - return m_cop_data_latch; - } - else - { - return m_cop_l; + data = m_cop_data_latch; } + + LOGMASKED(LOG_COP, "%s: cop_data_r: %02x (data latch enable: %d, %02x/%02x)\n", machine().describe_context(), data, m_cop_data_latch_enable, m_cop_l, m_cop_data_latch); + return data; } -void thayers_state::cop_data_w(uint8_t data) +void thayers_state::cop_data_w(u8 data) { m_cop_data_latch = data; - if (LOG) logerror("COP DATA %02x\n", m_cop_data_latch); + LOGMASKED(LOG_COP, "%s: cop_data_w: %02x (data latch enable: %d)\n", machine().describe_context(), m_cop_data_latch, m_cop_data_latch_enable); } -uint8_t thayers_state::cop_l_r() +u8 thayers_state::cop_l_r() { + u8 data = 0; if (!m_cop_data_latch_enable) { - return m_cop_data_latch; - } - else - { - return 0; + data = m_cop_data_latch; } + + LOGMASKED(LOG_COP, "%s: cop_l_r: %02x (data latch enable: %d, %02x/%02x)\n", machine().describe_context(), data, m_cop_data_latch_enable, 0, m_cop_data_latch); + return data; } -void thayers_state::cop_l_w(uint8_t data) +void thayers_state::cop_l_w(u8 data) { + LOGMASKED(LOG_COP, "%s: cop_l_w = %02x\n", machine().describe_context(), data); m_cop_l = data; - if (LOG) logerror("COP L %02x\n", m_cop_l); } -uint8_t thayers_state::cop_g_r() +u8 thayers_state::cop_g_r() { - /* - - bit description - - G0 U16 Q0 - G1 U16 Q1 - G2 U16 Q2 - G3 - - */ + // bit description + // + // G0 U16 Q0 + // G1 U16 Q1 + // G2 U16 Q2 + // G3 - return m_cop_cmd_latch; + const u8 data = m_cop_cmd_latch; + LOGMASKED(LOG_COP, "%s: cop_g_r: %02x\n", machine().describe_context(), data); + return data; } -void thayers_state::control_w(uint8_t data) +void thayers_state::control_w(u8 data) { - /* - - bit description - - 0 - 1 _CS128A - 2 _BANKSEL1 - 3 - 4 - 5 COP G0 - 6 COP G1 - 7 COP G2 - - */ + // bit description + // + // 0 + // 1 _CS128A + // 2 _BANKSEL1 + // 3 + // 4 + // 5 COP G0 + // 6 COP G1 + // 7 COP G2 m_cop_cmd_latch = (data >> 5) & 0x07; - if (LOG) logerror("COP G0..2 %u\n", m_cop_cmd_latch); + LOGMASKED(LOG_COP, "%s: control_w: %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_COP, "%s: /CS128A: %d\n", machine().describe_context(), BIT(data, 1)); + LOGMASKED(LOG_COP, "%s: /BANKSEL1: %d\n", machine().describe_context(), BIT(data, 2)); + LOGMASKED(LOG_COP, "%s: COP G0: %d\n", machine().describe_context(), BIT(data, 5)); + LOGMASKED(LOG_COP, "%s: COP G1: %d\n", machine().describe_context(), BIT(data, 6)); + LOGMASKED(LOG_COP, "%s: COP G2: %d\n", machine().describe_context(), BIT(data, 7)); } -void thayers_state::cop_g_w(uint8_t data) +void thayers_state::cop_g_w(u8 data) { - /* - - bit description - - G0 - G1 - G2 - G3 U17 enable - - */ + // bit description + // + // G0 + // G1 + // G2 + // G3 U17 (L-port latch) enable m_cop_data_latch_enable = BIT(data, 3); - if (LOG) logerror("U17 enable %u\n", m_cop_data_latch_enable); + LOGMASKED(LOG_COP, "%s: cop_g_w = %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_COP, "%s: U17 Enable: %d\n", machine().describe_context(), BIT(data, 3)); } -/* Keyboard */ int thayers_state::kbdata_r() { - if (LOG) logerror("%s KBDATA %u BIT %u\n",machine().time().as_string(),m_kbdata,m_rx_bit); - return m_kbdata; + const u8 data = m_kbdata; + LOGMASKED(LOG_KEYBOARD, "%s: kbdata_r: %02x\n", machine().describe_context(), data); + return data; } void thayers_state::kbclk_w(int state) { - if (m_kbclk != state) { - if (LOG) logerror("%s %s KBCLK %u\n", machine().time().as_string(), machine().describe_context(),state); - } + if (m_kbclk == state) + return; + + m_kbclk = state; - if (!m_kbclk && state) { - m_rx_bit++; + if (m_kbclk && state) + return; - // 1, 1, 0, 1, Q9, P3, P2, P1, P0, 0 - switch (m_rx_bit) - { - case 0: case 1: case 3: + m_rx_bit++; + + // 1, 1, 0, 1, Q9, P3, P2, P1, P0, 0 + switch (m_rx_bit) + { + case 0: + case 1: + case 3: m_kbdata = 1; break; - case 2: case 9: + case 2: + case 9: m_kbdata = 0; break; @@ -348,13 +405,10 @@ void thayers_state::kbclk_w(int state) m_keylatch++; - if (m_keylatch == 10) { + if (m_keylatch == 10) m_keylatch = 0; - } m_keydata = m_row[m_keylatch]->read(); - - if (LOG) logerror("keylatch %u\n",m_keylatch); break; case 4: @@ -364,262 +418,116 @@ void thayers_state::kbclk_w(int state) default: m_kbdata = BIT(m_keydata, 3); m_keydata <<= 1; - - if (LOG) logerror("keydata %02x shift\n",m_keydata); break; - } } - - m_kbclk = state; } -/* I/O Board */ -void thayers_state::control2_w(uint8_t data) +void thayers_state::control2_w(u8 data) { - /* - - bit description + // bit description + // + // 0 + // 1 _RESOI + // 2 _ENCARTDET + // 3 + // 4 + // 5 + // 6 + // 7 - 0 - 1 _RESOI (?) - 2 _ENCARTDET - 3 - 4 - 5 - 6 - 7 - - */ - - if ((!BIT(data, 2)) & m_cart_present) - { - m_maincpu->set_input_line(INPUT_LINE_NMI, HOLD_LINE); - } + LOG("%s: control2_w: %02x\n", machine().describe_context(), data); + LOG("%s: /RESOI: %d\n", machine().describe_context(), BIT(data, 1)); + LOG("%s: /ENCARTDET: %d\n", machine().describe_context(), BIT(data, 2)); } -uint8_t thayers_state::dsw_b_r() +u8 thayers_state::dsw_b_r() { - return (ioport("COIN")->read() & 0xf0) | (ioport("DSWB")->read() & 0x0f); + const u8 data = (m_coins->read() & 0xf0) | (m_dswb->read() & 0x0f); + LOGMASKED(LOG_COINS, "%s: dsw_b_r: %02x, data & 0x30 is %02x\n", machine().describe_context(), data, data & 0x30); + return data; } -uint8_t thayers_state::laserdsc_data_r() +u8 thayers_state::laserdisc_data_r() { - if (m_ldv1000 != nullptr) return m_ldv1000->status_r(); - if (m_pr7820 != nullptr) return m_pr7820->data_r(); - return 0; + const u8 data = m_player->data_r(); + LOGMASKED(LOG_PLAYER, "%s: laserdisc_data_r: %02x\n", machine().describe_context(), data); + return data; } -void thayers_state::laserdsc_data_w(uint8_t data) +void thayers_state::laserdisc_data_w(u8 data) { + LOGMASKED(LOG_PLAYER, "%s: laserdisc_data_w: %02x\n", machine().describe_context(), data); m_laserdisc_data = data; + if (BIT(m_laserdisc_control, 5)) + { + m_player->data_w(m_laserdisc_data); + } } -void thayers_state::laserdsc_control_w(uint8_t data) +void thayers_state::laserdisc_control_w(u8 data) { - /* + // bit description + // + // 0 + // 1 + // 2 + // 3 + // 4 coin counter + // 5 U16 (laserdisc data) output enable + // 6 ENTER if switch B5 closed + // 7 INT/_EXT - bit description + LOGMASKED(LOG_PLAYER, "%s: laserdisc_control_w: %02x\n", machine().describe_context(), data); + LOGMASKED(LOG_PLAYER, "%s: Coin Counter: %d\n", machine().describe_context(), BIT(data, 4)); + LOGMASKED(LOG_PLAYER, "%s: U16 Enable: %d\n", machine().describe_context(), BIT(data, 5)); + LOGMASKED(LOG_PLAYER, "%s: ENTER: %d\n", machine().describe_context(), BIT(data, 6)); + LOGMASKED(LOG_PLAYER, "%s: INT/_EXT: %d\n", machine().describe_context(), BIT(data, 7)); - 0 - 1 - 2 - 3 - 4 coin counter - 5 U16 output enable - 6 ENTER if switch B5 closed - 7 INT/_EXT - - */ + const u8 diff = m_laserdisc_control ^ data; + m_laserdisc_control = data; machine().bookkeeping().coin_counter_w(0, BIT(data, 4)); - if (BIT(data, 5)) + if (BIT(diff, 5) && BIT(data, 5)) { - if (m_ldv1000 != nullptr) - { - m_ldv1000->data_w(m_laserdisc_data); - m_ldv1000->enter_w(BIT(data, 7) ? CLEAR_LINE : ASSERT_LINE); - } - if (m_pr7820 != nullptr) - { - m_pr7820->data_w(m_laserdisc_data); - m_pr7820_enter = BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE; - m_pr7820->enter_w(m_pr7820_enter); - // BIT(data, 7) is INT/_EXT, but there is no such input line in laserdsc.h - } + m_player->data_w(m_laserdisc_data); + m_player->enter_w(BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE); } } -void thayers_state::den1_w(uint8_t data) +void thayers_state::den1_w(u8 data) { - /* - - bit description - - 0 DD0 - 1 DD1 - 2 DD2 - 3 DD3 - 4 DA0 - 5 DA1 - 6 DA2 - 7 DA3 + // bit description + // + // 0 DD0 + // 1 DD1 + // 2 DD2 + // 3 DD3 + // 4 DA0 + // 5 DA1 + // 6 DA2 + // 7 DA3 - */ - - m_digits[data >> 4] = led_map[data & 0x0f]; + m_digits[data >> 4] = LED_MAP[data & 0x0f]; } -void thayers_state::den2_w(uint8_t data) +void thayers_state::den2_w(u8 data) { - /* - - bit description - - 0 DD0 - 1 DD1 - 2 DD2 - 3 DD3 - 4 DA0 - 5 DA1 - 6 DA2 - 7 DA3 + // bit description + // + // 0 DD0 + // 1 DD1 + // 2 DD2 + // 3 DD3 + // 4 DA0 + // 5 DA1 + // 6 DA2 + // 7 DA3 - */ - - m_digits[8 + (data >> 4)] = led_map[data & 0x0f]; + m_digits[8 + (data >> 4)] = LED_MAP[data & 0x0f]; } -/* SSI-263 */ - -/* - - The following information is from the SSI-263A data sheet. - - Thayer's Quest uses an SSI-263, so this might be inaccurate, but it works for now - -*/ - -#define SSI263_CLOCK (XTAL(4'000'000)/2) - -static const char SSI263_PHONEMES[0x40][5] = -{ - "PA", "E", "E1", "Y", "YI", "AY", "IE", "I", "A", "AI", "EH", "EH1", "AE", "AE1", "AH", "AH1", "W", "O", "OU", "OO", "IU", "IU1", "U", "U1", "UH", "UH1", "UH2", "UH3", "ER", "R", "R1", "R2", - "L", "L1", "LF", "W", "B", "D", "KV", "P", "T", "K", "HV", "HVC", "HF", "HFC", "HN", "Z", "S", "J", "SCH", "V", "F", "THV", "TH", "M", "N", "NG", ":A", ":OH", ":U", ":UH", "E2", "LB" -}; - -void thayers_state::ssi263_register_w(offs_t offset, uint8_t data) -{ - struct ssi263_t &ssi263 = m_ssi263; - switch (offset) - { - case 0: - { - int frame_time = ((4096 * (16 - ssi263.r)) / 2); // us, /2 should actually be /SSI263_CLOCK, but this way we get microseconds directly - int phoneme_time = frame_time * (4 - ssi263.dr); // us - - // duration/phoneme register - ssi263.dr = (data >> 5) & 0x03; - ssi263.p = data & 0x3f; - - m_ssi_data_request = 1; - check_interrupt(); - - switch (ssi263.mode) - { - case 0: - case 1: - // phoneme timing response - m_phoneme_timer->adjust(attotime::from_usec(phoneme_time)); - break; - case 2: - // frame timing response - m_phoneme_timer->adjust(attotime::from_usec(frame_time)); - break; - case 3: - // disable A/_R output - break; - } - - //logerror("SSI263 Phoneme Duration: %u\n", ssi263.dr); - //logerror("SSI263 Phoneme: %02x %s\n", ssi263.p, SSI263_PHONEMES[ssi263.p]); - if (LOG && ssi263.p) printf("%s ", SSI263_PHONEMES[ssi263.p]); - } - break; - - case 1: - // inflection register - ssi263.i = (data << 3) | (ssi263.i & 0x403); - - //logerror("SSI263 Inflection: %u\n", ssi263.i); - break; - - case 2: - // rate/inflection register - ssi263.i = (BIT(data, 4) << 11) | (ssi263.i & 0x7f8) | (data & 0x07); - ssi263.r = data >> 4; - - //logerror("SSI263 Inflection: %u\n", ssi263.i); - //logerror("SSI263 Rate: %u\n", ssi263.r); - break; - - case 3: - // control/articulation/amplitude register - if (ssi263.c && !BIT(data, 7)) - { - ssi263.mode = ssi263.dr; - - switch (ssi263.mode) - { - case 0: - //logerror("SSI263 Phoneme Timing Response, Transitioned Inflection\n"); - break; - - case 1: - //logerror("SSI263 Phoneme Timing Response, Immediate Inflection\n"); - break; - - case 2: - // activate A/_R - //logerror("SSI263 Frame Timing Response, Immediate Inflection\n"); - break; - - case 3: - // disable A/_R output - //logerror("SSI263 A/R Output Disabled\n"); - break; - } - } - - ssi263.c = BIT(data, 7); - ssi263.t = (data >> 4) & 0x07; - ssi263.a = data & 0x0f; - - //logerror("SSI263 Control: %u\n", ssi263.c); - //logerror("SSI263 Articulation: %u\n", ssi263.t); - //logerror("SSI263 Amplitude: %u\n", ssi263.a); - break; - - case 4: - case 5: - case 6: - case 7: - // filter frequency register - ssi263.f = data; - //logerror("SSI263 Filter Frequency: %u\n", ssi263.f); - break; - } -} - -uint8_t thayers_state::ssi263_register_r() -{ - // D7 becomes an output, as the inverted state of A/_R. The register address bits are ignored. - - return !m_ssi_data_request << 7; -} - -/* Memory Maps */ void thayers_state::thayers_map(address_map &map) { @@ -631,37 +539,22 @@ void thayers_state::thayers_map(address_map &map) void thayers_state::thayers_io_map(address_map &map) { map.global_mask(0xff); - map(0x00, 0x07).rw(FUNC(thayers_state::ssi263_register_r), FUNC(thayers_state::ssi263_register_w)); + map(0x00, 0x07).m(m_ssi, FUNC(ssi263hle_device::map)); map(0x20, 0x20).w(FUNC(thayers_state::control_w)); map(0x40, 0x40).rw(FUNC(thayers_state::irqstate_r), FUNC(thayers_state::control2_w)); map(0x80, 0x80).rw(FUNC(thayers_state::cop_data_r), FUNC(thayers_state::cop_data_w)); map(0xa0, 0xa0).w(FUNC(thayers_state::timer_int_ack_w)); map(0xc0, 0xc0).w(FUNC(thayers_state::data_rdy_int_ack_w)); - map(0xf0, 0xf0).r(FUNC(thayers_state::laserdsc_data_r)); + map(0xf0, 0xf0).r(FUNC(thayers_state::laserdisc_data_r)); map(0xf1, 0xf1).r(FUNC(thayers_state::dsw_b_r)); map(0xf2, 0xf2).portr("DSWA"); - map(0xf3, 0xf3).w(FUNC(thayers_state::intrq_w)); - map(0xf4, 0xf4).w(FUNC(thayers_state::laserdsc_data_w)); - map(0xf5, 0xf5).w(FUNC(thayers_state::laserdsc_control_w)); + map(0xf3, 0xf3).w(FUNC(thayers_state::periodic_int_ack_w)); + map(0xf4, 0xf4).w(FUNC(thayers_state::laserdisc_data_w)); + map(0xf5, 0xf5).w(FUNC(thayers_state::laserdisc_control_w)); map(0xf6, 0xf6).w(FUNC(thayers_state::den1_w)); map(0xf7, 0xf7).w(FUNC(thayers_state::den2_w)); } -/* Input Ports */ - -int thayers_state::laserdisc_enter_r() -{ - if (m_pr7820 != nullptr) return m_pr7820_enter; - if (m_ldv1000 != nullptr) return (m_ldv1000->status_strobe_r() == ASSERT_LINE) ? 0 : 1; - return 0; -} - -int thayers_state::laserdisc_ready_r() -{ - if (m_pr7820 != nullptr) return (m_pr7820->ready_r() == ASSERT_LINE) ? 0 : 1; - if (m_ldv1000 != nullptr) return (m_ldv1000->command_strobe_r() == ASSERT_LINE) ? 0 : 1; - return 0; -} static INPUT_PORTS_START( thayers ) PORT_START("DSWA") @@ -692,125 +585,99 @@ static INPUT_PORTS_START( thayers ) PORT_SERVICE_DIPLOC( 0x01, 0x01, "B:1" ) PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "B:2" ) PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "B:3" ) - PORT_DIPNAME( 0x18, 0x00, "LD Player" ) PORT_DIPLOCATION( "B:5,4" ) + PORT_DIPNAME( 0x18, 0x18, "LD Player" ) PORT_DIPLOCATION( "B:5,4" ) PORT_DIPSETTING( 0x18, "LDV-1000" ) - PORT_DIPSETTING( 0x00, "PR-7820" ) + PORT_DIPSETTING( 0x00, "PR-7820 (Not Working)" ) PORT_DIPUNUSED_DIPLOC( 0xe0, IP_ACTIVE_LOW, "B:8,7,6" ) PORT_START("COIN") + PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN2 ) - PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(thayers_state, laserdisc_enter_r) - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(thayers_state, laserdisc_ready_r) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("laserdisc", parallel_laserdisc_device, status_strobe_r) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("laserdisc", parallel_laserdisc_device, ready_r) PORT_START("ROW.0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 YES") PORT_CODE(KEYCODE_1) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1 CLEAR") PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_BACKSPACE) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 ITEMS") PORT_CODE(KEYCODE_2) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("W AMULET") PORT_CODE(KEYCODE_W) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z SPELL OF RELEASE") PORT_CODE(KEYCODE_Z) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 DROP ITEM") PORT_CODE(KEYCODE_3) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E BLACK MACE") PORT_CODE(KEYCODE_E) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("S DAGGER") PORT_CODE(KEYCODE_S) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("X SCEPTER") PORT_CODE(KEYCODE_X) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.3") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 GIVE SCORE") PORT_CODE(KEYCODE_4) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R BLOOD SWORD") PORT_CODE(KEYCODE_R) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D GREAT CIRCLET") PORT_CODE(KEYCODE_D) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C SPELL OF SEEING") PORT_CODE(KEYCODE_C) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.4") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 REPLAY") PORT_CODE(KEYCODE_5) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("T CHALICE") PORT_CODE(KEYCODE_T) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F HUNTING HORN") PORT_CODE(KEYCODE_F) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("V SHIELD") PORT_CODE(KEYCODE_V) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.5") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 COMBINE ACTION") PORT_CODE(KEYCODE_6) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Y COINS") PORT_CODE(KEYCODE_Y) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("G LONG BOW") PORT_CODE(KEYCODE_G) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B SILVER WHEAT") PORT_CODE(KEYCODE_B) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.6") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 SAVE GAME") PORT_CODE(KEYCODE_7) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U COLD FIRE") PORT_CODE(KEYCODE_U) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("H MEDALLION") PORT_CODE(KEYCODE_H) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("N STAFF") PORT_CODE(KEYCODE_N) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.7") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 UPDATE") PORT_CODE(KEYCODE_8) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("I CROWN") PORT_CODE(KEYCODE_I) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("J ONYX SEAL") PORT_CODE(KEYCODE_J) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M SPELL OF UNDERSTANDING") PORT_CODE(KEYCODE_M) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.8") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 HINT") PORT_CODE(KEYCODE_9) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O CRYSTAL") PORT_CODE(KEYCODE_O) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("K ORB OF QUOID") PORT_CODE(KEYCODE_K) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4 SPACE") PORT_CODE(KEYCODE_F4) PORT_CODE(KEYCODE_SPACE) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("ROW.9") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 NO") PORT_CODE(KEYCODE_0) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3 ENTER") PORT_CODE(KEYCODE_F3) PORT_CODE(KEYCODE_ENTER) + PORT_BIT( 0xf0, IP_ACTIVE_HIGH, IPT_UNUSED ) INPUT_PORTS_END -/* Machine Initialization */ - -void thayers_state::machine_start() -{ - m_digits.resolve(); - memset(&m_ssi263, 0, sizeof(m_ssi263)); - - m_intrq_timer = timer_alloc(FUNC(thayers_state::intrq_tick), this); - m_phoneme_timer = timer_alloc(FUNC(thayers_state::phoneme_tick), this); -} - -void thayers_state::machine_reset() -{ - m_laserdisc_data = 0; - - m_rx_bit = 0; - m_kbdata = 1; - m_keylatch = 0; - m_keydata = m_row[m_keylatch]->read(); - - m_cop_data_latch = 0; - m_cop_data_latch_enable = 0; - m_cop_l = 0; - m_cop_cmd_latch = 0; - - m_timer_int = 1; - m_data_rdy_int = 1; - m_ssi_data_request = 1; - - m_cart_present = 0; - m_pr7820_enter = 0; - - m_intrq_timer->adjust(attotime::never); - m_phoneme_timer->adjust(attotime::never); -} - -/* Machine Driver */ void thayers_state::thayers(machine_config &config) { - /* basic machine hardware */ + // basic machine hardware Z80(config, m_maincpu, XTAL(4'000'000)); m_maincpu->set_addrmap(AS_PROGRAM, &thayers_state::thayers_map); m_maincpu->set_addrmap(AS_IO, &thayers_state::thayers_io_map); cop421_cpu_device &mcu(COP421(config, "mcu", XTAL(4'000'000)/2)); // COP421L-PCA/N - mcu.set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); + mcu.set_config(COP400_CKI_DIVISOR_32, COP400_CKO_OSCILLATOR_OUTPUT, false); mcu.read_l().set(FUNC(thayers_state::cop_l_r)); mcu.write_l().set(FUNC(thayers_state::cop_l_w)); mcu.read_g().set(FUNC(thayers_state::cop_g_r)); @@ -819,27 +686,29 @@ void thayers_state::thayers(machine_config &config) mcu.read_si().set(FUNC(thayers_state::kbdata_r)); mcu.write_so().set(FUNC(thayers_state::kbclk_w)); - /* video hardware */ - PIONEER_PR7820(config, m_pr7820, 0); - m_pr7820->set_screen("screen"); + config.set_maximum_quantum(attotime::from_hz(262)); + + // video hardware + PIONEER_LDV1000HLE(config, m_player, 0); + m_player->set_screen("screen"); 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)); + screen.set_screen_update(m_player, FUNC(pioneer_ldv1000hle_device::screen_update)); - PALETTE(config, "palette").set_entries(256); - - /* sound hardware */ + // sound hardware SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); - // SSI 263 @ 2MHz + m_player->add_route(0, "lspeaker", 1.0); + m_player->add_route(1, "rspeaker", 1.0); - m_pr7820->add_route(0, "lspeaker", 1.0); - m_pr7820->add_route(1, "rspeaker", 1.0); + SSI263HLE(config, m_ssi, 860000); + m_ssi->ar_callback().set(FUNC(thayers_state::ssi_data_request_w)); + m_ssi->add_route(0, "lspeaker", 1.0); + m_ssi->add_route(1, "lspeaker", 1.0); } -/* ROMs */ ROM_START( thayers ) ROM_REGION( 0xe000, "maincpu", 0 ) @@ -850,26 +719,24 @@ ROM_START( thayers ) ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "thayers", 0, SHA1(cfc517b1cc6f756ef76ae18e84092435b9af0511) ) ROM_END ROM_START( thayersa ) ROM_REGION( 0xe000, "maincpu", 0 ) ROM_LOAD( "tq_u33.bin", 0x0000, 0x8000, CRC(82df5d89) SHA1(58dfd62bf8c5a55d1eba397d2c284e99a4685a3f) ) - ROM_LOAD( "tq_u1.bin", 0xc000, 0x2000, CRC(33817e25) SHA1(f9750da863dd57fe2f5b6e8fce9c6695dc5c9adc) ) // sldh + ROM_LOAD( "tq_u1.bin", 0xc000, 0x2000, CRC(33817e25) SHA1(f9750da863dd57fe2f5b6e8fce9c6695dc5c9adc) ) ROM_REGION( 0x400, "mcu", 0 ) ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP ) + DISK_IMAGE_READONLY( "thayers", 0, SHA1(cfc517b1cc6f756ef76ae18e84092435b9af0511) ) ROM_END } // anonymous namespace -/* Game Drivers */ - -// YEAR NAME PARENT MACHINE INPUT CLASS INIT MONITOR COMPANY FULLNAME FLAGS LAYOUT -GAMEL( 1984, thayers, 0, thayers, thayers, thayers_state, empty_init, ROT0, "RDI Video Systems", "Thayer's Quest (set 1)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_thayers) -GAMEL( 1984, thayersa, thayers, thayers, thayers, thayers_state, empty_init, ROT0, "RDI Video Systems", "Thayer's Quest (set 2)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_thayers) +// YEAR NAME PARENT MACHINE INPUT CLASS INIT MONITOR COMPANY FULLNAME FLAGS LAYOUT +GAMEL( 1984, thayers, 0, thayers, thayers, thayers_state, empty_init, ROT0, "RDI Video Systems", "Thayer's Quest (set 1)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE, layout_thayers) +GAMEL( 1984, thayersa, thayers, thayers, thayers, thayers_state, empty_init, ROT0, "RDI Video Systems", "Thayer's Quest (set 2)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE, layout_thayers) diff --git a/src/mame/sega/gpworld.cpp b/src/mame/sega/gpworld.cpp index 6159948dc84..3707a8bee25 100644 --- a/src/mame/sega/gpworld.cpp +++ b/src/mame/sega/gpworld.cpp @@ -459,7 +459,7 @@ INTERRUPT_GEN_MEMBER(gpworld_state::vblank_callback) if (m_nmi_enable) { m_laserdisc->data_w(m_ldp_write_latch); - m_ldp_read_latch = m_laserdisc->status_r(); + m_ldp_read_latch = m_laserdisc->data_r(); device.execute().pulse_input_line(INPUT_LINE_NMI, attotime::zero); } diff --git a/src/mame/sega/segald.cpp b/src/mame/sega/segald.cpp index 49eebf72d28..9386e4cc29d 100644 --- a/src/mame/sega/segald.cpp +++ b/src/mame/sega/segald.cpp @@ -137,7 +137,7 @@ uint32_t segald_state::screen_update_astron(screen_device &screen, bitmap_rgb32 uint8_t segald_state::astron_DISC_read(offs_t offset) { if (m_nmi_enable) - m_ldv1000_input_latch = m_laserdisc->status_r(); + m_ldv1000_input_latch = m_laserdisc->data_r(); logerror("DISC read (0x%04x) @ 0x%04x [0x%x]\n", m_ldv1000_input_latch, offset, m_maincpu->pc()); diff --git a/src/mame/sega/timetrv.cpp b/src/mame/sega/timetrv.cpp index 5c0552bbae5..f3b62b6288c 100644 --- a/src/mame/sega/timetrv.cpp +++ b/src/mame/sega/timetrv.cpp @@ -352,7 +352,7 @@ ROM_START( timetrv2 ) ROM_LOAD( "epr-72491.u9", 0xc0000, 0x40000, CRC(c7998e2f) SHA1(26060653b2368f52c304e6433b4f447f99a36839) ) DISK_REGION( "laserdisc" ) - DISK_IMAGE_READONLY( "timetrv", 0, SHA1(8abb5e6aa58ab49477ef89f507264d35454f99d3) BAD_DUMP ) + DISK_IMAGE_READONLY( "timetrv2", 0, SHA1(0bb0c34df0aae2b5e019c6dc2fc071e23c82ba75) ) ROM_END } // anonymous namespace diff --git a/src/mame/taito/lgp.cpp b/src/mame/taito/lgp.cpp index 9a327615756..b8c77182aaf 100644 --- a/src/mame/taito/lgp.cpp +++ b/src/mame/taito/lgp.cpp @@ -154,7 +154,7 @@ uint32_t lgp_state::screen_update_lgp(screen_device &screen, bitmap_rgb32 &bitma /* Main Z80 R/W */ uint8_t lgp_state::ldp_read() { - return m_laserdisc->status_r(); + return m_laserdisc->data_r(); } /* Sound Z80 R/W */ diff --git a/src/mame/universal/superdq.cpp b/src/mame/universal/superdq.cpp index 3500258b152..a1be0c9bc5e 100644 --- a/src/mame/universal/superdq.cpp +++ b/src/mame/universal/superdq.cpp @@ -155,7 +155,7 @@ INTERRUPT_GEN_MEMBER(superdq_state::superdq_vblank) /* status is read when the STATUS line from the laserdisc toggles (600usec after the vblank). We could set up a timer to do that, but this works as well */ - m_ld_in_latch = m_laserdisc->status_r(); + m_ld_in_latch = m_laserdisc->data_r(); /* command is written when the COMMAND line from the laserdisc toggles (680usec after the vblank). We could set up a -- cgit v1.2.3