mirror of
https://github.com/holub/mame
synced 2025-05-30 09:33:05 +03:00
239 lines
5.5 KiB
C
239 lines
5.5 KiB
C
/*********************************************************/
|
|
/* 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),
|
|
m_sample_callback(NULL)
|
|
{
|
|
memset(m_data, 0, sizeof(UINT8)*0x10000);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void rf5c68_device::device_start()
|
|
{
|
|
const rf5c68_interface* intf = (const rf5c68_interface*)static_config();
|
|
|
|
/* allocate memory for the chip */
|
|
memset(m_data, 0xff, sizeof(m_data));
|
|
|
|
/* allocate the stream */
|
|
m_stream = stream_alloc(0, 2, clock() / 384);
|
|
|
|
/* set up callback */
|
|
if(intf != NULL)
|
|
m_sample_callback = intf->sample_end_callback;
|
|
else
|
|
m_sample_callback = NULL;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// 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_callback)
|
|
{
|
|
if(((chan->addr >> 11) & 0xfff) == 0xfff)
|
|
m_sample_callback(this, ((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;
|
|
}
|