mirror of
https://github.com/holub/mame
synced 2025-06-26 14:24:12 +03:00
Merge pull request #1954 from ajrhacker/msm5205_change_clock
MSM5205 architectural cleanups (nw)
This commit is contained in:
commit
936498261b
@ -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);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user