diff --git a/src/devices/sound/msm5205.cpp b/src/devices/sound/msm5205.cpp index c49a4ddc62f..845e10737ac 100644 --- a/src/devices/sound/msm5205.cpp +++ b/src/devices/sound/msm5205.cpp @@ -28,6 +28,9 @@ Data is streamed from a CPU by means of a clock generated on the chip. + Holding the rate selector lines (S1 and S2) both high places the MSM5205 in an undocumented + mode which disables the sampling clock generator and makes VCK an input line. + A reset signal is set high or low to determine whether playback (and interrupts) are occurring. MSM6585: is an upgraded MSM5205 voice synth IC. @@ -38,7 +41,7 @@ Differences between MSM6585 & MSM5205: - MSM6586 MSM5205 + MSM6585 MSM5205 Master clock frequency 640kHz 384kHz Sampling frequency 4k/8k/16k/32kHz 4k/6k/8kHz ADPCM bit length 4-bit 3-bit/4-bit @@ -46,7 +49,7 @@ Low-pass filter -40dB/oct N/A Overflow prevent circuit Included N/A - Timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585) + Data input follows VCK falling edge on MSM5205 (VCK rising edge on MSM6585) TODO: - lowpass filter for MSM6585 @@ -57,39 +60,50 @@ const device_type MSM5205 = &device_creator; const device_type MSM6585 = &device_creator; -msm5205_device::msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +msm5205_device::msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, MSM5205, "MSM5205", tag, owner, clock, "msm5205", __FILE__), device_sound_interface(mconfig, *this), - m_prescaler(0), - m_bitwidth(0), - m_select(0), + m_s1(false), + m_s2(false), + m_bitwidth(4), m_vclk_cb(*this) { } -msm5205_device::msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source) +msm5205_device::msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, u32 clock, const char *shortname, const char *source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), device_sound_interface(mconfig, *this), - m_prescaler(0), - m_bitwidth(0), - m_select(0), + m_s1(false), + m_s2(false), + m_bitwidth(4), m_vclk_cb(*this) { } -msm6585_device::msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +msm6585_device::msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : msm5205_device(mconfig, MSM6585, "MSM6585", tag, owner, clock, "msm6585", __FILE__) { } +//------------------------------------------------- +// set_prescaler_selector - configuration helper +//------------------------------------------------- + +void msm5205_device::set_prescaler_selector(device_t &device, int select) +{ + msm5205_device &msm = downcast(device); + msm.m_s1 = BIT(select, 1); + msm.m_s2 = BIT(select, 0); + msm.m_bitwidth = (select & 4) ? 4 : 3; +} + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void msm5205_device::device_start() { - m_mod_clock = clock(); m_vclk_cb.resolve(); /* compute the difference tables */ @@ -100,11 +114,11 @@ void msm5205_device::device_start() m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(msm5205_device::vclk_callback), this)); /* register for save states */ - save_item(NAME(m_mod_clock)); save_item(NAME(m_data)); save_item(NAME(m_vclk)); save_item(NAME(m_reset)); - save_item(NAME(m_prescaler)); + save_item(NAME(m_s1)); + save_item(NAME(m_s2)); save_item(NAME(m_bitwidth)); save_item(NAME(m_signal)); save_item(NAME(m_step)); @@ -118,13 +132,10 @@ void msm5205_device::device_reset() { /* initialize work */ m_data = 0; - m_vclk = 0; + m_vclk = 0; m_reset = 0; m_signal = 0; m_step = 0; - - /* timer and bitwidth set */ - playmode_w(m_select); } @@ -170,17 +181,17 @@ void msm5205_device::compute_tables() } } -/* timer callback at VCLK low edge on MSM5205 (at rising edge on MSM6585) */ +// timer callback at VCK low edge on MSM5205 (at rising edge on MSM6585) TIMER_CALLBACK_MEMBER( msm5205_device::vclk_callback ) { int val; int new_signal; - /* callback user handler and latch next data */ + // callback user handler and latch next data if (!m_vclk_cb.isnull()) m_vclk_cb(1); - /* reset check at last hiedge of VCLK */ + // reset check at last hiedge of VCK if (m_reset) { new_signal = 0; @@ -213,19 +224,19 @@ TIMER_CALLBACK_MEMBER( msm5205_device::vclk_callback ) /* - * Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF) + * Handle an update of the VCK status of a chip (1 is reset ON, 0 is reset OFF) * This function can use selector = MSM5205_SEX only */ -void msm5205_device::vclk_w(int vclk) +WRITE_LINE_MEMBER(msm5205_device::vclk_w) { - if (m_prescaler != 0) - logerror("error: msm5205_vclk_w() called with chip = '%s', but VCLK selected master mode\n", this->device().tag()); + if (get_prescaler() != 0) + logerror("Error: vclk_w() called but VCK selected master mode\n"); else { - if (m_vclk != vclk) + if (m_vclk != state) { - m_vclk = vclk; - if (!vclk) + m_vclk = state; + if (!state) vclk_callback(this, 0); } } @@ -235,9 +246,9 @@ void msm5205_device::vclk_w(int vclk) * Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF) */ -void msm5205_device::reset_w(int reset) +WRITE_LINE_MEMBER(msm5205_device::reset_w) { - m_reset = reset; + m_reset = state; } /* @@ -257,34 +268,36 @@ WRITE8_MEMBER(msm5205_device::data_w) data_w(data); } +int msm5205_device::get_prescaler() const +{ + if (m_s1) + return m_s2 ? 0 : 64; + else + return m_s2 ? 48 : 96; +} + +int msm6585_device::get_prescaler() const +{ + return (m_s1 ? 20 : 40) * (m_s2 ? 1 : 4); +} + /* * Handle a change of the selector */ void msm5205_device::playmode_w(int select) { - static const int prescaler_table[2][4] = - { - { 96, 48, 64, 0}, - {160, 40, 80, 20} - }; - int prescaler = prescaler_table[select >> 3 & 1][select & 3]; int bitwidth = (select & 4) ? 4 : 3; - if (m_prescaler != prescaler) + if ((select & 3) != ((m_s1 << 1) | m_s2)) { m_stream->update(); - m_prescaler = prescaler; + m_s1 = BIT(select, 1); + m_s2 = BIT(select, 0); /* timer set */ - if (prescaler) - { - attotime period = attotime::from_hz(m_mod_clock) * prescaler; - m_timer->adjust(period, 0, period); - } - else - m_timer->adjust(attotime::never); + notify_clock_changed(); } if (m_bitwidth != bitwidth) @@ -294,15 +307,42 @@ void msm5205_device::playmode_w(int select) } } - -void msm5205_device::change_clock_w(int32_t clock) +WRITE_LINE_MEMBER(msm5205_device::s1_w) { - attotime period; + if (m_s1 != bool(state)) + { + m_stream->update(); + m_s1 = state; + notify_clock_changed(); + } +} - m_mod_clock = clock; +WRITE_LINE_MEMBER(msm5205_device::s2_w) +{ + if (m_s2 != bool(state)) + { + m_stream->update(); + m_s2 = state; + notify_clock_changed(); + } +} - period = attotime::from_hz(m_mod_clock) * m_prescaler; - m_timer->adjust(period, 0, period); + +//------------------------------------------------- +// device_clock_changed - called when the +// device clock is altered in any way +//------------------------------------------------- + +void msm5205_device::device_clock_changed() +{ + int prescaler = get_prescaler(); + if (prescaler != 0) + { + attotime period = attotime::from_hz(clock()) * prescaler; + m_timer->adjust(period, 0, period); + } + else + m_timer->adjust(attotime::never); } diff --git a/src/devices/sound/msm5205.h b/src/devices/sound/msm5205.h index a7afa5bd8af..156d4bfc448 100644 --- a/src/devices/sound/msm5205.h +++ b/src/devices/sound/msm5205.h @@ -43,15 +43,15 @@ class msm5205_device : public device_t, public device_sound_interface { public: - msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, uint32_t clock, const char *shortname, const char *source); + msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + msm5205_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, u32 clock, const char *shortname, const char *source); ~msm5205_device() {} - static void set_prescaler_selector(device_t &device, int select) { downcast(device).m_select = select; } + static void set_prescaler_selector(device_t &device, int select); template static devcb_base &set_vclk_callback(device_t &device, _Object object) { return downcast(device).m_vclk_cb.set_callback(object); } // reset signal should keep for 2cycle of VCLK - void reset_w(int reset); + DECLARE_WRITE_LINE_MEMBER(reset_w); // adpcmata is latched after vclk_interrupt callback void data_w(int data); @@ -60,17 +60,18 @@ public: // VCLK slave mode option // if VCLK and reset or data is changed at the same time, // call vclk_w after data_w and reset_w. - void vclk_w(int vclk); + DECLARE_WRITE_LINE_MEMBER(vclk_w); // option , selected pin selector void playmode_w(int select); - - void change_clock_w(int32_t clock); + DECLARE_WRITE_LINE_MEMBER(s1_w); + DECLARE_WRITE_LINE_MEMBER(s2_w); protected: // device-level overrides virtual void device_start() override; virtual void device_reset() override; + virtual void device_clock_changed() override; TIMER_CALLBACK_MEMBER(vclk_callback); @@ -78,20 +79,20 @@ protected: virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; void compute_tables(); + virtual int get_prescaler() const; // internal state - sound_stream * m_stream; /* number of stream system */ - int32_t m_mod_clock; /* clock rate */ - emu_timer *m_timer; /* VCLK callback timer */ - int32_t m_data; /* next adpcm data */ - int32_t m_vclk; /* vclk signal (external mode) */ - int32_t m_reset; /* reset pin signal */ - int32_t m_prescaler; /* prescaler selector S1 and S2 */ - int32_t m_bitwidth; /* bit width selector -3B/4B */ - int32_t m_signal; /* current ADPCM signal */ - int32_t m_step; /* current ADPCM step */ + sound_stream * m_stream; // number of stream system + emu_timer *m_timer; // VCK callback timer + u8 m_data; // next adpcm data + bool m_vclk; // VCK signal (external mode) + bool m_reset; // reset pin signal + bool m_s1; // prescaler selector S1 + bool m_s2; // prescaler selector S2 + u8 m_bitwidth; // bit width selector -3B/4B + s32 m_signal; // current ADPCM signal + s32 m_step; // current ADPCM step int m_diff_lookup[49*16]; - int m_select; devcb_write_line m_vclk_cb; }; @@ -100,7 +101,10 @@ extern const device_type MSM5205; class msm6585_device : public msm5205_device { public: - msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + virtual int get_prescaler() const override; // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; diff --git a/src/mame/drivers/joctronic.cpp b/src/mame/drivers/joctronic.cpp index 021b1c651a8..d9b0470b771 100644 --- a/src/mame/drivers/joctronic.cpp +++ b/src/mame/drivers/joctronic.cpp @@ -234,7 +234,7 @@ WRITE8_MEMBER(joctronic_state::resint_w) WRITE8_MEMBER(joctronic_state::slalom03_oki_bank_w) { m_soundbank->set_entry((data & 0xc0) >> 6); - m_oki->playmode_w(BIT(data, 1) ? MSM5205_S48_4B : MSM5205_S96_4B); // to S1 on MSM5205 + m_oki->s1_w(BIT(data, 1)); m_oki->reset_w(BIT(data, 0)); } diff --git a/src/mame/machine/pce_cd.cpp b/src/mame/machine/pce_cd.cpp index c8b5052207d..13db6a9b06d 100644 --- a/src/mame/machine/pce_cd.cpp +++ b/src/mame/machine/pce_cd.cpp @@ -229,7 +229,7 @@ void pce_cd_device::late_setup() } // MSM5205 might be initialized after PCE CD as well... - m_msm->change_clock_w((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); + m_msm->set_unscaled_clock((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); } void pce_cd_device::nvram_init(nvram_device &nvram, void *data, size_t size) @@ -1180,7 +1180,7 @@ WRITE8_MEMBER(pce_cd_device::intf_w) break; case 0x0E: /* ADPCM playback rate */ m_adpcm_clock_divider = 0x10 - (data & 0x0f); - m_msm->change_clock_w((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); + m_msm->set_unscaled_clock((PCE_CD_CLOCK / 6) / m_adpcm_clock_divider); break; case 0x0F: /* ADPCM and CD audio fade timer */ /* TODO: timers needs HW tests */