// license:BSD-3-Clause // copyright-holders:Nicola Salmoria /*************************************************************************** SNK Wave sound driver. This is a very simple single-voice generator with a programmable waveform. ***************************************************************************/ #include "emu.h" #include "snkwave.h" DEFINE_DEVICE_TYPE(SNKWAVE, snkwave_device, "snkwave", "SNK Wave") //------------------------------------------------- // snkwave_device - constructor //------------------------------------------------- snkwave_device::snkwave_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SNKWAVE, tag, owner, clock), device_sound_interface(mconfig, *this), m_stream(nullptr), m_external_clock(0), m_sample_rate(0), m_frequency(0), m_counter(0), m_waveform_position(0) { std::fill(std::begin(m_waveform), std::end(m_waveform), 0); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void snkwave_device::device_start() { /* adjust internal clock */ m_external_clock = clock(); /* adjust output clock */ m_sample_rate = m_external_clock >> CLOCK_SHIFT; /* get stream channels */ m_stream = stream_alloc(0, 1, m_sample_rate); /* reset all the voices */ m_frequency = 0; m_counter = 0; m_waveform_position = 0; /* register with the save state system */ save_item(NAME(m_frequency)); save_item(NAME(m_counter)); save_item(NAME(m_waveform_position)); save_pointer(NAME(m_waveform), WAVEFORM_LENGTH); } //------------------------------------------------- // sound_stream_update - handle update requests // for our sound stream //------------------------------------------------- void snkwave_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { stream_sample_t *buffer = outputs[0]; /* zap the contents of the buffer */ memset(buffer, 0, samples * sizeof(*buffer)); assert(m_counter < 0x1000); assert(m_frequency < 0x1000); /* if no sound, we're done */ if (m_frequency == 0xfff) return; /* generate sound into buffer while updating the counter */ while (samples-- > 0) { int loops; int16_t out = 0; loops = 1 << CLOCK_SHIFT; while (loops > 0) { int steps = 0x1000 - m_counter; if (steps <= loops) { out += m_waveform[m_waveform_position] * steps; m_counter = m_frequency; m_waveform_position = (m_waveform_position + 1) & (WAVEFORM_LENGTH-1); loops -= steps; } else { out += m_waveform[m_waveform_position] * loops; m_counter += loops; loops = 0; } } *buffer++ = out; } } /* SNK wave register map all registers are 6-bit 0-1 frequency (12-bit) 2-5 waveform (8 3-bit nibbles) */ WRITE8_MEMBER( snkwave_device::snkwave_w ) { m_stream->update(); // all registers are 6-bit data &= 0x3f; if (offset == 0) m_frequency = (m_frequency & 0x03f) | (data << 6); else if (offset == 1) m_frequency = (m_frequency & 0xfc0) | data; else if (offset <= 5) update_waveform(offset - 2, data); } /* update the decoded waveform data */ /* The programmable waveform consists of 8 3-bit nibbles. The waveform goes to a 4-bit DAC and is played alternatingly forwards and backwards. When going forwards, bit 3 is 1. When going backwards, it's 0. So the sequence 01234567 will play as 89ABCDEF76543210 */ void snkwave_device::update_waveform(unsigned int offset, uint8_t data) { assert(offset < WAVEFORM_LENGTH/4); m_waveform[offset * 2] = ((data & 0x38) >> 3) << (12-CLOCK_SHIFT); m_waveform[offset * 2 + 1] = ((data & 0x07) >> 0) << (12-CLOCK_SHIFT); m_waveform[WAVEFORM_LENGTH-2 - offset * 2] = ~m_waveform[offset * 2 + 1]; m_waveform[WAVEFORM_LENGTH-1 - offset * 2] = ~m_waveform[offset * 2]; }