summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/k051649.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/k051649.cpp')
-rw-r--r--src/devices/sound/k051649.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/devices/sound/k051649.cpp b/src/devices/sound/k051649.cpp
new file mode 100644
index 00000000000..d69b9f25445
--- /dev/null
+++ b/src/devices/sound/k051649.cpp
@@ -0,0 +1,288 @@
+// license:BSD-3-Clause
+// copyright-holders:Bryan McPhail
+/***************************************************************************
+
+ Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber
+
+ This file is pieced together by Bryan McPhail from a combination of
+ Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first
+ figured out SCC!
+
+ The 051649 is a 5 channel sound generator, each channel gets its
+ waveform from RAM (32 bytes per waveform, 8 bit signed data).
+
+ This sound chip is the same as the sound chip in some Konami
+ megaROM cartridges for the MSX. It is actually well researched
+ and documented:
+
+ http://bifi.msxnet.org/msxnet/tech/scc.html
+
+ Thanks to Sean Young (sean@mess.org) for some bugfixes.
+
+ K052539 is more or less equivalent to this chip except channel 5
+ does not share waveram with channel 4.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "k051649.h"
+
+#define FREQ_BITS 16
+#define DEF_GAIN 8
+
+
+// device type definition
+const device_type K051649 = &device_creator<k051649_device>;
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// k051649_device - constructor
+//-------------------------------------------------
+
+k051649_device::k051649_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, K051649, "K051649 SCC1", tag, owner, clock, "k051649", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_stream(NULL),
+ m_mclock(0),
+ m_rate(0),
+ m_mixer_table(NULL),
+ m_mixer_lookup(NULL),
+ m_mixer_buffer(NULL),
+ m_test(0)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void k051649_device::device_start()
+{
+ // get stream channels
+ m_rate = clock()/16;
+ m_stream = stream_alloc(0, 1, m_rate);
+ m_mclock = clock();
+
+ // allocate a buffer to mix into - 1 second's worth should be more than enough
+ m_mixer_buffer = auto_alloc_array(machine(), short, 2 * m_rate);
+
+ // build the mixer table
+ make_mixer_table(5);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void k051649_device::device_reset()
+{
+ k051649_sound_channel *voice = m_channel_list;
+ int i;
+
+ // reset all the voices
+ for (i = 0; i < 5; i++)
+ {
+ voice[i].frequency = 0;
+ voice[i].volume = 0xf;
+ voice[i].counter = 0;
+ voice[i].key = 0;
+ }
+
+ // other parameters
+ m_test = 0;
+}
+
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void k051649_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ k051649_sound_channel *voice=m_channel_list;
+ stream_sample_t *buffer = outputs[0];
+ short *mix;
+ int i,j;
+
+ // zap the contents of the mixer buffer
+ memset(m_mixer_buffer, 0, samples * sizeof(short));
+
+ for (j = 0; j < 5; j++)
+ {
+ // channel is halted for freq < 9
+ if (voice[j].frequency > 8)
+ {
+ const signed char *w = voice[j].waveram;
+ int v=voice[j].volume * voice[j].key;
+ int c=voice[j].counter;
+ int step = ((INT64)m_mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (m_rate / 32)) + 0.5f;
+
+ mix = m_mixer_buffer;
+
+ // add our contribution
+ for (i = 0; i < samples; i++)
+ {
+ int offs;
+
+ c += step;
+ offs = (c >> FREQ_BITS) & 0x1f;
+ *mix++ += (w[offs] * v)>>3;
+ }
+
+ // update the counter for this voice
+ voice[j].counter = c;
+ }
+ }
+
+ // mix it down
+ mix = m_mixer_buffer;
+ for (i = 0; i < samples; i++)
+ *buffer++ = m_mixer_lookup[*mix++];
+}
+
+
+/********************************************************************************/
+
+
+WRITE8_MEMBER( k051649_device::k051649_waveform_w )
+{
+ // waveram is read-only?
+ if (m_test & 0x40 || (m_test & 0x80 && offset >= 0x60))
+ return;
+
+ m_stream->update();
+
+ if (offset >= 0x60)
+ {
+ // channel 5 shares waveram with channel 4
+ m_channel_list[3].waveram[offset&0x1f]=data;
+ m_channel_list[4].waveram[offset&0x1f]=data;
+ }
+ else
+ m_channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+
+READ8_MEMBER ( k051649_device::k051649_waveform_r )
+{
+ // test-register bits 6/7 expose the internal counter
+ if (m_test & 0xc0)
+ {
+ m_stream->update();
+
+ if (offset >= 0x60)
+ offset += (m_channel_list[3 + (m_test >> 6 & 1)].counter >> FREQ_BITS);
+ else if (m_test & 0x40)
+ offset += (m_channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return m_channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+
+WRITE8_MEMBER( k051649_device::k052539_waveform_w )
+{
+ // waveram is read-only?
+ if (m_test & 0x40)
+ return;
+
+ m_stream->update();
+ m_channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+
+READ8_MEMBER ( k051649_device::k052539_waveform_r )
+{
+ // test-register bit 6 exposes the internal counter
+ if (m_test & 0x40)
+ {
+ m_stream->update();
+ offset += (m_channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return m_channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_volume_w )
+{
+ m_stream->update();
+ m_channel_list[offset&0x7].volume=data&0xf;
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_frequency_w )
+{
+ int freq_hi = offset & 1;
+ offset >>= 1;
+
+ m_stream->update();
+
+ // test-register bit 5 resets the internal counter
+ if (m_test & 0x20)
+ m_channel_list[offset].counter = ~0;
+ else if (m_channel_list[offset].frequency < 9)
+ m_channel_list[offset].counter |= ((1 << FREQ_BITS) - 1);
+
+ // update frequency
+ if (freq_hi)
+ m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00);
+ else
+ m_channel_list[offset].frequency = (m_channel_list[offset].frequency & 0xf00) | data;
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_keyonoff_w )
+{
+ int i;
+ m_stream->update();
+
+ for (i = 0; i < 5; i++)
+ {
+ m_channel_list[i].key=data&1;
+ data >>= 1;
+ }
+}
+
+
+WRITE8_MEMBER( k051649_device::k051649_test_w )
+{
+ m_test = data;
+}
+
+
+READ8_MEMBER ( k051649_device::k051649_test_r )
+{
+ // reading the test register sets it to $ff!
+ k051649_test_w(space, offset, 0xff);
+ return 0xff;
+}
+
+
+//-------------------------------------------------
+// build a table to divide by the number of voices
+//-------------------------------------------------
+
+void k051649_device::make_mixer_table(int voices)
+{
+ int i;
+
+ // allocate memory
+ m_mixer_table = auto_alloc_array(machine(), INT16, 512 * voices);
+
+ // find the middle of the table
+ m_mixer_lookup = m_mixer_table + (256 * voices);
+
+ // fill in the table - 16 bit case
+ for (i = 0; i < (voices * 256); i++)
+ {
+ int val = i * DEF_GAIN * 16 / voices;
+ if (val > 32767) val = 32767;
+ m_mixer_lookup[ i] = val;
+ m_mixer_lookup[-i] = -val;
+ }
+}