summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/k051649.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/k051649.c')
-rw-r--r--trunk/src/emu/sound/k051649.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/k051649.c b/trunk/src/emu/sound/k051649.c
new file mode 100644
index 00000000000..fd764f4d140
--- /dev/null
+++ b/trunk/src/emu/sound/k051649.c
@@ -0,0 +1,313 @@
+/***************************************************************************
+
+ 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
+
+
+/* this structure defines the parameters for a channel */
+typedef struct
+{
+ unsigned long counter;
+ int frequency;
+ int volume;
+ int key;
+ signed char waveram[32];
+} k051649_sound_channel;
+
+typedef struct _k051649_state k051649_state;
+struct _k051649_state
+{
+ k051649_sound_channel channel_list[5];
+
+ /* global sound parameters */
+ sound_stream * stream;
+ int mclock,rate;
+
+ /* mixer tables and internal buffers */
+ INT16 *mixer_table;
+ INT16 *mixer_lookup;
+ short *mixer_buffer;
+
+ /* chip registers */
+ UINT8 test;
+};
+
+INLINE k051649_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == K051649);
+ return (k051649_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+/* build a table to divide by the number of voices */
+static void make_mixer_table(running_machine &machine, k051649_state *info, int voices)
+{
+ int i;
+
+ /* allocate memory */
+ info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices);
+
+ /* find the middle of the table */
+ info->mixer_lookup = info->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;
+ info->mixer_lookup[ i] = val;
+ info->mixer_lookup[-i] = -val;
+ }
+}
+
+
+/* generate sound to the mix buffer */
+static STREAM_UPDATE( k051649_update )
+{
+ k051649_state *info = (k051649_state *)param;
+ k051649_sound_channel *voice=info->channel_list;
+ stream_sample_t *buffer = outputs[0];
+ short *mix;
+ int i,j;
+
+ /* zap the contents of the mixer buffer */
+ memset(info->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)info->mclock * (1 << FREQ_BITS)) / (float)((voice[j].frequency + 1) * 16 * (info->rate / 32)) + 0.5;
+
+ mix = info->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 = info->mixer_buffer;
+ for (i = 0; i < samples; i++)
+ *buffer++ = info->mixer_lookup[*mix++];
+}
+
+static DEVICE_START( k051649 )
+{
+ k051649_state *info = get_safe_token(device);
+
+ /* get stream channels */
+ info->rate = device->clock()/16;
+ info->stream = device->machine().sound().stream_alloc(*device, 0, 1, info->rate, info, k051649_update);
+ info->mclock = device->clock();
+
+ /* allocate a buffer to mix into - 1 second's worth should be more than enough */
+ info->mixer_buffer = auto_alloc_array(device->machine(), short, 2 * info->rate);
+
+ /* build the mixer table */
+ make_mixer_table(device->machine(), info, 5);
+}
+
+static DEVICE_RESET( k051649 )
+{
+ k051649_state *info = get_safe_token(device);
+ k051649_sound_channel *voice = info->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 */
+ info->test = 0;
+}
+
+/********************************************************************************/
+
+WRITE8_DEVICE_HANDLER( k051649_waveform_w )
+{
+ k051649_state *info = get_safe_token(device);
+
+ /* waveram is read-only? */
+ if (info->test & 0x40 || (info->test & 0x80 && offset >= 0x60))
+ return;
+
+ info->stream->update();
+
+ if (offset >= 0x60)
+ {
+ /* channel 5 shares waveram with channel 4 */
+ info->channel_list[3].waveram[offset&0x1f]=data;
+ info->channel_list[4].waveram[offset&0x1f]=data;
+ }
+ else
+ info->channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+READ8_DEVICE_HANDLER ( k051649_waveform_r )
+{
+ k051649_state *info = get_safe_token(device);
+
+ /* test-register bits 6/7 expose the internal counter */
+ if (info->test & 0xc0)
+ {
+ info->stream->update();
+
+ if (offset >= 0x60)
+ offset += (info->channel_list[3 + (info->test >> 6 & 1)].counter >> FREQ_BITS);
+ else if (info->test & 0x40)
+ offset += (info->channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return info->channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+WRITE8_DEVICE_HANDLER( k052539_waveform_w )
+{
+ k051649_state *info = get_safe_token(device);
+
+ /* waveram is read-only? */
+ if (info->test & 0x40)
+ return;
+
+ info->stream->update();
+ info->channel_list[offset>>5].waveram[offset&0x1f]=data;
+}
+
+READ8_DEVICE_HANDLER ( k052539_waveform_r )
+{
+ k051649_state *info = get_safe_token(device);
+
+ /* test-register bit 6 exposes the internal counter */
+ if (info->test & 0x40)
+ {
+ info->stream->update();
+ offset += (info->channel_list[offset>>5].counter >> FREQ_BITS);
+ }
+ return info->channel_list[offset>>5].waveram[offset&0x1f];
+}
+
+WRITE8_DEVICE_HANDLER( k051649_volume_w )
+{
+ k051649_state *info = get_safe_token(device);
+ info->stream->update();
+ info->channel_list[offset&0x7].volume=data&0xf;
+}
+
+WRITE8_DEVICE_HANDLER( k051649_frequency_w )
+{
+ k051649_state *info = get_safe_token(device);
+ int freq_hi = offset & 1;
+ offset >>= 1;
+
+ info->stream->update();
+
+ /* test-register bit 5 resets the internal counter */
+ if (info->test & 0x20)
+ info->channel_list[offset].counter = ~0;
+ else if (info->channel_list[offset].frequency < 9)
+ info->channel_list[offset].counter |= ((1 << FREQ_BITS) - 1);
+
+ /* update frequency */
+ if (freq_hi)
+ info->channel_list[offset].frequency = (info->channel_list[offset].frequency & 0x0ff) | (data << 8 & 0xf00);
+ else
+ info->channel_list[offset].frequency = (info->channel_list[offset].frequency & 0xf00) | data;
+}
+
+WRITE8_DEVICE_HANDLER( k051649_keyonoff_w )
+{
+ int i;
+ k051649_state *info = get_safe_token(device);
+ info->stream->update();
+
+ for (i = 0; i < 5; i++) {
+ info->channel_list[i].key=data&1;
+ data >>= 1;
+ }
+}
+
+WRITE8_DEVICE_HANDLER( k051649_test_w )
+{
+ k051649_state *info = get_safe_token(device);
+ info->test = data;
+}
+
+READ8_DEVICE_HANDLER ( k051649_test_r )
+{
+ /* reading the test register sets it to $ff! */
+ k051649_test_w(device, offset, 0xff);
+ return 0xff;
+}
+
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+DEVICE_GET_INFO( k051649 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k051649_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k051649 ); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k051649 ); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "K051649"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami custom"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}
+
+
+DEFINE_LEGACY_SOUND_DEVICE(K051649, k051649);