// license:GPL-2.0+
// copyright-holders:Peter Trauner
/***************************************************************************
supervision sound hardware
PeT mess@utanet.at
***************************************************************************/
#include "emu.h"
#include "svis_snd.h"
// device type definition
DEFINE_DEVICE_TYPE(SVISION_SND, svision_sound_device, "svision_sound", "Super Vision Custom Sound")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// svision_sound_device - constructor
//-------------------------------------------------
svision_sound_device::svision_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, SVISION_SND, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, m_irq_cb(*this)
, m_maincpu(*this, finder_base::DUMMY_TAG)
, m_cartrom(*this, finder_base::DUMMY_TAG)
, m_mixer_channel(nullptr)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void svision_sound_device::device_start()
{
m_irq_cb.resolve_safe();
memset(&m_dma, 0, sizeof(m_dma));
memset(&m_noise, 0, sizeof(m_noise));
memset(m_channel, 0, sizeof(m_channel));
m_mixer_channel = stream_alloc(0, 2, machine().sample_rate());
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void svision_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
auto &left=outputs[0];
auto &right=outputs[1];
for (int i = 0; i < left.samples(); i++)
{
s32 lsum = 0;
s32 rsum = 0;
for (int j = 0; j < ARRAY_LENGTH(m_channel); j++)
{
CHANNEL &channel(m_channel[j]);
if (channel.size != 0)
{
if (channel.on||channel.count)
{
bool on = false;
switch (channel.waveform)
{
case 0:
on = channel.pos <= (28 * channel.size) >> 5;
break;
case 1:
on = channel.pos <= (24 * channel.size) >> 5;
break;
default:
case 2:
on = channel.pos <= channel.size / 2;
break;
case 3:
on = channel.pos <= (9 * channel.size) >> 5;
break;
}
{
int16_t s = on ? channel.volume << 8 : 0;
if (j == 0)
rsum += s;
else
lsum += s;
}
}
channel.pos++;
if (channel.pos >= channel.size)
channel.pos = 0;
}
}
if (m_noise.on && (m_noise.play || m_noise.count))
{
int16_t s = (m_noise.value ? 1 << 8: 0) * m_noise.volume;
int b1, b2;
if (m_noise.left)
lsum += s;
if (m_noise.right)
rsum += s;
m_noise.pos += m_noise.step;
if (m_noise.pos >= 1.0)
{
switch (m_noise.type)
{
case NOISE::Type::Type7Bit:
m_noise.value = m_noise.state & 0x40 ? 1 : 0;
b1 = (m_noise.state & 0x40) != 0;
b2 = (m_noise.state & 0x20) != 0;
m_noise.state=(m_noise.state<<1)+(b1!=b2?1:0);
break;
case NOISE::Type::Type14Bit:
default:
m_noise.value = m_noise.state & 0x2000 ? 1 : 0;
b1 = (m_noise.state & 0x2000) != 0;
b2 = (m_noise.state & 0x1000) != 0;
m_noise.state = (m_noise.state << 1) + (b1 != b2 ? 1 : 0);
}
m_noise.pos -= 1;
}
}
if (m_dma.on)
{
uint8_t sample;
int16_t s;
uint16_t addr = m_dma.start + (unsigned) m_dma.pos / 2;
if (addr >= 0x8000 && addr < 0xc000)
{
sample = ((uint8_t*)m_cartrom->base())[(addr & 0x3fff) | m_dma.ca14to16];
}
else
{
sample = m_maincpu->space(AS_PROGRAM).read_byte(addr);
}
if (((unsigned)m_dma.pos) & 1)
s = (sample & 0xf);
else
s = (sample & 0xf0) >> 4;
s <<= 8;
if (m_dma.left)
lsum += s;
if (m_dma.right)
rsum += s;
m_dma.pos += m_dma.step;
if (m_dma.pos >= m_dma.size)
{
m_dma.finished = true;
m_dma.on = false;
m_irq_cb(1);
}
}
left.put_int(i, lsum, 32768);
right.put_int(i, rsum, 32768);
}
}
void svision_sound_device::sounddma_w(offs_t offset, uint8_t data)
{
logerror("%.6f svision snddma write %04x %02x\n", machine().time().as_double(),offset+0x18,data);
m_dma.reg[offset] = data;
switch (offset)
{
case 0:
case 1:
m_dma.start = (m_dma.reg[0] | (m_dma.reg[1] << 8));
break;
case 2:
m_dma.size = (data ? data : 0x100) * 32;
break;
case 3:
m_dma.step = unscaled_clock() / (256.0 * machine().sample_rate() * (1 + (data & 3)));
m_dma.right = data & 4;
m_dma.left = data & 8;
m_dma.ca14to16 = ((data & 0x70) >> 4) << 14;
break;
case 4:
m_dma.on = data & 0x80;
if (m_dma.on)
{
m_dma.pos = 0.0;
}
break;
}
}
void svision_sound_device::noise_w(offs_t offset, uint8_t data)
{
// logerror("%.6f svision noise write %04x %02x\n",machine.time(),offset+0x28,data);
m_noise.reg[offset]=data;
switch (offset)
{
case 0:
m_noise.volume=data&0xf;
m_noise.step= unscaled_clock() / (256.0*machine().sample_rate()*(1+(data>>4)));
break;
case 1:
m_noise.count = data + 1;
break;
case 2:
m_noise.type = NOISE::Type(data & 1);
m_noise.play = data & 2;
m_noise.right = data & 4;
m_noise.left = data & 8;
m_noise.on = data & 0x10; /* honey bee start */
m_noise.state = 1;
break;
}
m_noise.pos=0.0;
}
void svision_sound_device::sound_decrement()
{
if (m_channel[0].count > 0)
m_channel[0].count--;
if (m_channel[1].count > 0)
m_channel[1].count--;
if (m_noise.count > 0)
m_noise.count--;
}
void svision_sound_device::soundport_w(int which, int offset, int data)
{
CHANNEL &channel(m_channel[which]);
uint16_t size;
m_mixer_channel->update();
channel.reg[offset] = data;
switch (offset)
{
case 0:
case 1:
size = channel.reg[0] | ((channel.reg[1] & 7) << 8);
if (size)
{
// channel.size=(int)(device->machine().sample_rate()*(size<<5)/4e6);
channel.size = (int) (machine().sample_rate() * (size << 5) / unscaled_clock());
}
else
{
channel.size = 0;
}
channel.pos = 0;
break;
case 2:
channel.on = data & 0x40;
channel.waveform = (data & 0x30) >> 4;
channel.volume = data & 0xf;
break;
case 3:
channel.count = data + 1;
break;
}
}