diff options
Diffstat (limited to 'src/devices/sound/rf5c68.cpp')
-rw-r--r-- | src/devices/sound/rf5c68.cpp | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/devices/sound/rf5c68.cpp b/src/devices/sound/rf5c68.cpp new file mode 100644 index 00000000000..af99edd197a --- /dev/null +++ b/src/devices/sound/rf5c68.cpp @@ -0,0 +1,233 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert,Aaron Giles +/*********************************************************/ +/* ricoh RF5C68(or clone) PCM controller */ +/*********************************************************/ + +#include "emu.h" +#include "rf5c68.h" + + +// device type definition +const device_type RF5C68 = &device_creator<rf5c68_device>; + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// rf5c68_device - constructor +//------------------------------------------------- + +rf5c68_device::rf5c68_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, RF5C68, "RF5C68", tag, owner, clock, "rf5c68", __FILE__), + device_sound_interface(mconfig, *this), + m_stream(NULL), + m_cbank(0), + m_wbank(0), + m_enable(0) +{ + memset(m_data, 0, sizeof(UINT8)*0x10000); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void rf5c68_device::device_start() +{ + m_sample_end_cb.bind_relative_to(*owner()); + + /* allocate memory for the chip */ + memset(m_data, 0xff, sizeof(m_data)); + + /* allocate the stream */ + m_stream = stream_alloc(0, 2, clock() / 384); +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void rf5c68_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *left = outputs[0]; + stream_sample_t *right = outputs[1]; + int i, j; + + /* start with clean buffers */ + memset(left, 0, samples * sizeof(*left)); + memset(right, 0, samples * sizeof(*right)); + + /* bail if not enabled */ + if (!m_enable) + return; + + /* loop over channels */ + for (i = 0; i < RF5C68_NUM_CHANNELS; i++) + { + rf5c68_pcm_channel *chan = &m_chan[i]; + + /* if this channel is active, accumulate samples */ + if (chan->enable) + { + int lv = (chan->pan & 0x0f) * chan->env; + int rv = ((chan->pan >> 4) & 0x0f) * chan->env; + + /* loop over the sample buffer */ + for (j = 0; j < samples; j++) + { + int sample; + + /* trigger sample callback */ + if(!m_sample_end_cb.isnull()) + { + if(((chan->addr >> 11) & 0xfff) == 0xfff) + m_sample_end_cb((chan->addr >> 11)/0x2000); + } + + /* fetch the sample and handle looping */ + sample = m_data[(chan->addr >> 11) & 0xffff]; + if (sample == 0xff) + { + chan->addr = chan->loopst << 11; + sample = m_data[(chan->addr >> 11) & 0xffff]; + + /* if we loop to a loop point, we're effectively dead */ + if (sample == 0xff) + break; + } + chan->addr += chan->step; + + /* add to the buffer */ + if (sample & 0x80) + { + sample &= 0x7f; + left[j] += (sample * lv) >> 5; + right[j] += (sample * rv) >> 5; + } + else + { + left[j] -= (sample * lv) >> 5; + right[j] -= (sample * rv) >> 5; + } + } + } + } + + /* now clamp and shift the result (output is only 10 bits) */ + for (j = 0; j < samples; j++) + { + stream_sample_t temp; + + temp = left[j]; + if (temp > 32767) temp = 32767; + else if (temp < -32768) temp = -32768; + left[j] = temp & ~0x3f; + + temp = right[j]; + if (temp > 32767) temp = 32767; + else if (temp < -32768) temp = -32768; + right[j] = temp & ~0x3f; + } +} + + +//------------------------------------------------- +// RF5C68 write register +//------------------------------------------------- + +READ8_MEMBER( rf5c68_device::rf5c68_r ) +{ + UINT8 shift; + + m_stream->update(); + shift = (offset & 1) ? 11 + 8 : 11; + +// printf("%08x\n",(m_chan[(offset & 0x0e) >> 1].addr)); + + return (m_chan[(offset & 0x0e) >> 1].addr) >> (shift); +} + +WRITE8_MEMBER( rf5c68_device::rf5c68_w ) +{ + rf5c68_pcm_channel *chan = &m_chan[m_cbank]; + int i; + + /* force the stream to update first */ + m_stream->update(); + + /* switch off the address */ + switch (offset) + { + case 0x00: /* envelope */ + chan->env = data; + break; + + case 0x01: /* pan */ + chan->pan = data; + break; + + case 0x02: /* FDL */ + chan->step = (chan->step & 0xff00) | (data & 0x00ff); + break; + + case 0x03: /* FDH */ + chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00); + break; + + case 0x04: /* LSL */ + chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff); + break; + + case 0x05: /* LSH */ + chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00); + break; + + case 0x06: /* ST */ + chan->start = data; + if (!chan->enable) + chan->addr = chan->start << (8 + 11); + break; + + case 0x07: /* control reg */ + m_enable = (data >> 7) & 1; + if (data & 0x40) + m_cbank = data & 7; + else + m_wbank = data & 15; + break; + + case 0x08: /* channel on/off reg */ + for (i = 0; i < 8; i++) + { + m_chan[i].enable = (~data >> i) & 1; + if (!m_chan[i].enable) + m_chan[i].addr = m_chan[i].start << (8 + 11); + } + break; + } +} + + +//------------------------------------------------- +// RF5C68 read memory +//------------------------------------------------- + +READ8_MEMBER( rf5c68_device::rf5c68_mem_r ) +{ + return m_data[m_wbank * 0x1000 + offset]; +} + + +//------------------------------------------------- +// RF5C68 write memory +//------------------------------------------------- + +WRITE8_MEMBER( rf5c68_device::rf5c68_mem_w ) +{ + m_data[m_wbank * 0x1000 + offset] = data; +} |