summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/msm5205.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/msm5205.c')
-rw-r--r--trunk/src/emu/sound/msm5205.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/msm5205.c b/trunk/src/emu/sound/msm5205.c
new file mode 100644
index 00000000000..8fa868f30b3
--- /dev/null
+++ b/trunk/src/emu/sound/msm5205.c
@@ -0,0 +1,338 @@
+/*
+ * streaming ADPCM driver
+ * by Aaron Giles
+ *
+ * Library to transcode from an ADPCM source to raw PCM.
+ * Written by Buffoni Mirko in 08/06/97
+ * References: various sources and documents.
+ *
+ * HJB 08/31/98
+ * modified to use an automatically selected oversampling factor
+ * for the current sample rate
+ *
+ * 01/06/99
+ * separate MSM5205 emulator form adpcm.c and some fix
+ */
+
+#include "emu.h"
+#include "msm5205.h"
+
+/*
+ *
+ * MSM 5205 ADPCM chip:
+ *
+ * Data is streamed from a CPU by means of a clock generated on the chip.
+ *
+ * A reset signal is set high or low to determine whether playback (and interrupts) are occuring
+ *
+ */
+
+typedef struct _msm5205_state msm5205_state;
+struct _msm5205_state
+{
+ const msm5205_interface *intf;
+ device_t *device;
+ sound_stream * stream; /* number of stream system */
+ INT32 clock; /* clock rate */
+ emu_timer *timer; /* VCLK callback timer */
+ INT32 data; /* next adpcm data */
+ INT32 vclk; /* vclk signal (external mode) */
+ INT32 reset; /* reset pin signal */
+ INT32 prescaler; /* prescaler selector S1 and S2 */
+ INT32 bitwidth; /* bit width selector -3B/4B */
+ INT32 signal; /* current ADPCM signal */
+ INT32 step; /* current ADPCM step */
+ int diff_lookup[49*16];
+};
+
+INLINE msm5205_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == MSM5205);
+ return (msm5205_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+static void msm5205_playmode(msm5205_state *voice,int select);
+
+/*
+ * ADPCM lockup tabe
+ */
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/*
+ * Compute the difference table
+ */
+
+static void ComputeTables (msm5205_state *voice)
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ voice->diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+}
+
+/* stream update callbacks */
+static STREAM_UPDATE( MSM5205_update )
+{
+ msm5205_state *voice = (msm5205_state *)param;
+ stream_sample_t *buffer = outputs[0];
+
+ /* if this voice is active */
+ if(voice->signal)
+ {
+ short val = voice->signal * 16;
+ while (samples)
+ {
+ *buffer++ = val;
+ samples--;
+ }
+ }
+ else
+ memset (buffer,0,samples*sizeof(*buffer));
+}
+
+/* timer callback at VCLK low eddge */
+static TIMER_CALLBACK( MSM5205_vclk_callback )
+{
+ msm5205_state *voice = (msm5205_state *)ptr;
+ int val;
+ int new_signal;
+ /* callback user handler and latch next data */
+ if(voice->intf->vclk_callback) (*voice->intf->vclk_callback)(voice->device);
+
+ /* reset check at last hieddge of VCLK */
+ if(voice->reset)
+ {
+ new_signal = 0;
+ voice->step = 0;
+ }
+ else
+ {
+ /* update signal */
+ /* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */
+ val = voice->data;
+ new_signal = voice->signal + voice->diff_lookup[voice->step * 16 + (val & 15)];
+ if (new_signal > 2047) new_signal = 2047;
+ else if (new_signal < -2048) new_signal = -2048;
+ voice->step += index_shift[val & 7];
+ if (voice->step > 48) voice->step = 48;
+ else if (voice->step < 0) voice->step = 0;
+ }
+ /* update when signal changed */
+ if( voice->signal != new_signal)
+ {
+ voice->stream->update();
+ voice->signal = new_signal;
+ }
+}
+
+/*
+ * Reset emulation of an MSM5205-compatible chip
+ */
+static DEVICE_RESET( msm5205 )
+{
+ msm5205_state *voice = get_safe_token(device);
+
+ /* initialize work */
+ voice->data = 0;
+ voice->vclk = 0;
+ voice->reset = 0;
+ voice->signal = 0;
+ voice->step = 0;
+ /* timer and bitwidth set */
+ msm5205_playmode(voice,voice->intf->select);
+}
+
+
+/*
+ * Start emulation of an MSM5205-compatible chip
+ */
+
+static DEVICE_START( msm5205 )
+{
+ msm5205_state *voice = get_safe_token(device);
+
+ /* save a global pointer to our interface */
+ voice->intf = (const msm5205_interface *)device->static_config();
+ voice->device = device;
+ voice->clock = device->clock();
+
+ /* compute the difference tables */
+ ComputeTables (voice);
+
+ /* stream system initialize */
+ voice->stream = device->machine().sound().stream_alloc(*device,0,1,device->clock(),voice,MSM5205_update);
+ voice->timer = device->machine().scheduler().timer_alloc(FUNC(MSM5205_vclk_callback), voice);
+
+ /* initialize */
+ DEVICE_RESET_CALL(msm5205);
+
+ /* register for save states */
+ device->save_item(NAME(voice->clock));
+ device->save_item(NAME(voice->data));
+ device->save_item(NAME(voice->vclk));
+ device->save_item(NAME(voice->reset));
+ device->save_item(NAME(voice->prescaler));
+ device->save_item(NAME(voice->bitwidth));
+ device->save_item(NAME(voice->signal));
+ device->save_item(NAME(voice->step));
+}
+
+/*
+ * Handle an update of the vclk status of a chip (1 is reset ON, 0 is reset OFF)
+ * This function can use selector = MSM5205_SEX only
+ */
+void msm5205_vclk_w (device_t *device, int vclk)
+{
+ msm5205_state *voice = get_safe_token(device);
+
+ if( voice->prescaler != 0 )
+ {
+ logerror("error: msm5205_vclk_w() called with chip = '%s', but VCLK selected master mode\n", device->tag());
+ }
+ else
+ {
+ if( voice->vclk != vclk)
+ {
+ voice->vclk = vclk;
+ if( !vclk ) MSM5205_vclk_callback(voice->device->machine(), voice, 0);
+ }
+ }
+}
+
+/*
+ * Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
+ */
+
+void msm5205_reset_w (device_t *device, int reset)
+{
+ msm5205_state *voice = get_safe_token(device);
+ voice->reset = reset;
+}
+
+/*
+ * Handle an update of the data to the chip
+ */
+
+void msm5205_data_w (device_t *device, int data)
+{
+ msm5205_state *voice = get_safe_token(device);
+ if( voice->bitwidth == 4)
+ voice->data = data & 0x0f;
+ else
+ voice->data = (data & 0x07)<<1; /* unknown */
+}
+
+/*
+ * Handle an change of the selector
+ */
+
+void msm5205_playmode_w(device_t *device, int select)
+{
+ msm5205_state *voice = get_safe_token(device);
+ msm5205_playmode(voice,select);
+}
+
+static void msm5205_playmode(msm5205_state *voice,int select)
+{
+ static const int prescaler_table[4] = {96,48,64,0};
+ int prescaler = prescaler_table[select & 3];
+ int bitwidth = (select & 4) ? 4 : 3;
+
+
+ if( voice->prescaler != prescaler )
+ {
+ voice->stream->update();
+
+ voice->prescaler = prescaler;
+ /* timer set */
+ if( prescaler )
+ {
+ attotime period = attotime::from_hz(voice->clock) * prescaler;
+ voice->timer->adjust(period, 0, period);
+ }
+ else
+ voice->timer->adjust(attotime::never);
+ }
+
+ if( voice->bitwidth != bitwidth )
+ {
+ voice->stream->update();
+
+ voice->bitwidth = bitwidth;
+ }
+}
+
+
+void msm5205_set_volume(device_t *device,int volume)
+{
+ msm5205_state *voice = get_safe_token(device);
+
+ voice->stream->set_output_gain(0,volume / 100.0);
+}
+
+void msm5205_change_clock_w(device_t *device, INT32 clock)
+{
+ msm5205_state *voice = get_safe_token(device);
+ attotime period;
+
+ voice->clock = clock;
+
+ period = attotime::from_hz(voice->clock) * voice->prescaler;
+ voice->timer->adjust(period, 0, period);
+}
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+DEVICE_GET_INFO( msm5205 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(msm5205_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( msm5205 ); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( msm5205 ); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "MSM5205"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "ADPCM"); 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(MSM5205, msm5205);