Merge pull request #1954 from ajrhacker/msm5205_change_clock

MSM5205 architectural cleanups (nw)
This commit is contained in:
R. Belmont 2017-01-12 08:25:51 -05:00 committed by GitHub
commit 936498261b
4 changed files with 117 additions and 73 deletions

View File

@ -28,6 +28,9 @@
Data is streamed from a CPU by means of a clock generated on the chip. 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. A reset signal is set high or low to determine whether playback (and interrupts) are occurring.
MSM6585: is an upgraded MSM5205 voice synth IC. MSM6585: is an upgraded MSM5205 voice synth IC.
@ -38,7 +41,7 @@
Differences between MSM6585 & MSM5205: Differences between MSM6585 & MSM5205:
MSM6586 MSM5205 MSM6585 MSM5205
Master clock frequency 640kHz 384kHz Master clock frequency 640kHz 384kHz
Sampling frequency 4k/8k/16k/32kHz 4k/6k/8kHz Sampling frequency 4k/8k/16k/32kHz 4k/6k/8kHz
ADPCM bit length 4-bit 3-bit/4-bit ADPCM bit length 4-bit 3-bit/4-bit
@ -46,7 +49,7 @@
Low-pass filter -40dB/oct N/A Low-pass filter -40dB/oct N/A
Overflow prevent circuit Included 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: TODO:
- lowpass filter for MSM6585 - lowpass filter for MSM6585
@ -57,39 +60,50 @@ const device_type MSM5205 = &device_creator<msm5205_device>;
const device_type MSM6585 = &device_creator<msm6585_device>; const device_type MSM6585 = &device_creator<msm6585_device>;
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_t(mconfig, MSM5205, "MSM5205", tag, owner, clock, "msm5205", __FILE__),
device_sound_interface(mconfig, *this), device_sound_interface(mconfig, *this),
m_prescaler(0), m_s1(false),
m_bitwidth(0), m_s2(false),
m_select(0), m_bitwidth(4),
m_vclk_cb(*this) 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_t(mconfig, type, name, tag, owner, clock, shortname, source),
device_sound_interface(mconfig, *this), device_sound_interface(mconfig, *this),
m_prescaler(0), m_s1(false),
m_bitwidth(0), m_s2(false),
m_select(0), m_bitwidth(4),
m_vclk_cb(*this) 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__) : 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<msm5205_device &>(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 // device_start - device-specific startup
//------------------------------------------------- //-------------------------------------------------
void msm5205_device::device_start() void msm5205_device::device_start()
{ {
m_mod_clock = clock();
m_vclk_cb.resolve(); m_vclk_cb.resolve();
/* compute the difference tables */ /* 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)); m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(msm5205_device::vclk_callback), this));
/* register for save states */ /* register for save states */
save_item(NAME(m_mod_clock));
save_item(NAME(m_data)); save_item(NAME(m_data));
save_item(NAME(m_vclk)); save_item(NAME(m_vclk));
save_item(NAME(m_reset)); 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_bitwidth));
save_item(NAME(m_signal)); save_item(NAME(m_signal));
save_item(NAME(m_step)); save_item(NAME(m_step));
@ -118,13 +132,10 @@ void msm5205_device::device_reset()
{ {
/* initialize work */ /* initialize work */
m_data = 0; m_data = 0;
m_vclk = 0; m_vclk = 0;
m_reset = 0; m_reset = 0;
m_signal = 0; m_signal = 0;
m_step = 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 ) TIMER_CALLBACK_MEMBER( msm5205_device::vclk_callback )
{ {
int val; int val;
int new_signal; int new_signal;
/* callback user handler and latch next data */ // callback user handler and latch next data
if (!m_vclk_cb.isnull()) if (!m_vclk_cb.isnull())
m_vclk_cb(1); m_vclk_cb(1);
/* reset check at last hiedge of VCLK */ // reset check at last hiedge of VCK
if (m_reset) if (m_reset)
{ {
new_signal = 0; 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 * 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) if (get_prescaler() != 0)
logerror("error: msm5205_vclk_w() called with chip = '%s', but VCLK selected master mode\n", this->device().tag()); logerror("Error: vclk_w() called but VCK selected master mode\n");
else else
{ {
if (m_vclk != vclk) if (m_vclk != state)
{ {
m_vclk = vclk; m_vclk = state;
if (!vclk) if (!state)
vclk_callback(this, 0); 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) * 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); 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 * Handle a change of the selector
*/ */
void msm5205_device::playmode_w(int select) 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; int bitwidth = (select & 4) ? 4 : 3;
if (m_prescaler != prescaler) if ((select & 3) != ((m_s1 << 1) | m_s2))
{ {
m_stream->update(); m_stream->update();
m_prescaler = prescaler; m_s1 = BIT(select, 1);
m_s2 = BIT(select, 0);
/* timer set */ /* timer set */
if (prescaler) notify_clock_changed();
{
attotime period = attotime::from_hz(m_mod_clock) * prescaler;
m_timer->adjust(period, 0, period);
}
else
m_timer->adjust(attotime::never);
} }
if (m_bitwidth != bitwidth) if (m_bitwidth != bitwidth)
@ -294,15 +307,42 @@ void msm5205_device::playmode_w(int select)
} }
} }
WRITE_LINE_MEMBER(msm5205_device::s1_w)
void msm5205_device::change_clock_w(int32_t clock)
{ {
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);
} }

View File

@ -43,15 +43,15 @@ class msm5205_device : public device_t,
public device_sound_interface public device_sound_interface
{ {
public: public:
msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); 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, uint32_t clock, const char *shortname, const char *source); 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() {} ~msm5205_device() {}
static void set_prescaler_selector(device_t &device, int select) { downcast<msm5205_device &>(device).m_select = select; } static void set_prescaler_selector(device_t &device, int select);
template<class _Object> static devcb_base &set_vclk_callback(device_t &device, _Object object) { return downcast<msm5205_device &>(device).m_vclk_cb.set_callback(object); } template<class _Object> static devcb_base &set_vclk_callback(device_t &device, _Object object) { return downcast<msm5205_device &>(device).m_vclk_cb.set_callback(object); }
// reset signal should keep for 2cycle of VCLK // 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 // adpcmata is latched after vclk_interrupt callback
void data_w(int data); void data_w(int data);
@ -60,17 +60,18 @@ public:
// VCLK slave mode option // VCLK slave mode option
// if VCLK and reset or data is changed at the same time, // if VCLK and reset or data is changed at the same time,
// call vclk_w after data_w and reset_w. // call vclk_w after data_w and reset_w.
void vclk_w(int vclk); DECLARE_WRITE_LINE_MEMBER(vclk_w);
// option , selected pin selector // option , selected pin selector
void playmode_w(int select); void playmode_w(int select);
DECLARE_WRITE_LINE_MEMBER(s1_w);
void change_clock_w(int32_t clock); DECLARE_WRITE_LINE_MEMBER(s2_w);
protected: protected:
// device-level overrides // device-level overrides
virtual void device_start() override; virtual void device_start() override;
virtual void device_reset() override; virtual void device_reset() override;
virtual void device_clock_changed() override;
TIMER_CALLBACK_MEMBER(vclk_callback); 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; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
void compute_tables(); void compute_tables();
virtual int get_prescaler() const;
// internal state // internal state
sound_stream * m_stream; /* number of stream system */ sound_stream * m_stream; // number of stream system
int32_t m_mod_clock; /* clock rate */ emu_timer *m_timer; // VCK callback timer
emu_timer *m_timer; /* VCLK callback timer */ u8 m_data; // next adpcm data
int32_t m_data; /* next adpcm data */ bool m_vclk; // VCK signal (external mode)
int32_t m_vclk; /* vclk signal (external mode) */ bool m_reset; // reset pin signal
int32_t m_reset; /* reset pin signal */ bool m_s1; // prescaler selector S1
int32_t m_prescaler; /* prescaler selector S1 and S2 */ bool m_s2; // prescaler selector S2
int32_t m_bitwidth; /* bit width selector -3B/4B */ u8 m_bitwidth; // bit width selector -3B/4B
int32_t m_signal; /* current ADPCM signal */ s32 m_signal; // current ADPCM signal
int32_t m_step; /* current ADPCM step */ s32 m_step; // current ADPCM step
int m_diff_lookup[49*16]; int m_diff_lookup[49*16];
int m_select;
devcb_write_line m_vclk_cb; devcb_write_line m_vclk_cb;
}; };
@ -100,7 +101,10 @@ extern const device_type MSM5205;
class msm6585_device : public msm5205_device class msm6585_device : public msm5205_device
{ {
public: 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 // sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;

View File

@ -234,7 +234,7 @@ WRITE8_MEMBER(joctronic_state::resint_w)
WRITE8_MEMBER(joctronic_state::slalom03_oki_bank_w) WRITE8_MEMBER(joctronic_state::slalom03_oki_bank_w)
{ {
m_soundbank->set_entry((data & 0xc0) >> 6); 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)); m_oki->reset_w(BIT(data, 0));
} }

View File

@ -229,7 +229,7 @@ void pce_cd_device::late_setup()
} }
// MSM5205 might be initialized after PCE CD as well... // 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) 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; break;
case 0x0E: /* ADPCM playback rate */ case 0x0E: /* ADPCM playback rate */
m_adpcm_clock_divider = 0x10 - (data & 0x0f); 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; break;
case 0x0F: /* ADPCM and CD audio fade timer */ case 0x0F: /* ADPCM and CD audio fade timer */
/* TODO: timers needs HW tests */ /* TODO: timers needs HW tests */