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.
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<msm5205_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_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<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
//-------------------------------------------------
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));
@ -122,9 +136,6 @@ void msm5205_device::device_reset()
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,16 +307,43 @@ 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;
//-------------------------------------------------
// 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:
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<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); }
// 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;

View File

@ -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));
}

View File

@ -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 */