summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/bsmt2000.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/bsmt2000.c')
-rw-r--r--src/emu/sound/bsmt2000.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/src/emu/sound/bsmt2000.c b/src/emu/sound/bsmt2000.c
new file mode 100644
index 00000000000..71cc6546fc5
--- /dev/null
+++ b/src/emu/sound/bsmt2000.c
@@ -0,0 +1,529 @@
+/***************************************************************************
+
+ bsmt2000.c
+
+ BSMT2000 sound emulator.
+
+ Copyright (c) Aaron Giles
+
+ Chip is actually a TMS320C15 DSP with embedded mask rom
+ Trivia: BSMT stands for "Brian Schmidt's Mouse Trap"
+
+***************************************************************************/
+
+#include <math.h>
+
+#include "sndintrf.h"
+#include "streams.h"
+#include "bsmt2000.h"
+
+
+/***************************************************************************
+ DEBUGGING/OPTIONS
+***************************************************************************/
+
+#define LOG_COMMANDS 0
+
+/* NOTE: the original chip did not support interpolation, but it sounds */
+/* nicer if you enable it. For accuracy's sake, we leave it off by default. */
+#define ENABLE_INTERPOLATION 0
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define MAX_VOICES (12+1)
+#define ADPCM_VOICE 12
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* struct describing a single playing voice */
+typedef struct _bsmt2000_voice bsmt2000_voice;
+struct _bsmt2000_voice
+{
+ UINT16 pos; /* current position */
+ UINT16 rate; /* stepping value */
+ UINT16 loopend; /* loop end value */
+ UINT16 loopstart; /* loop start value */
+ UINT16 bank; /* bank number */
+ UINT16 leftvol; /* left volume */
+ UINT16 rightvol; /* right volume */
+ UINT16 fraction; /* current fractional position */
+};
+
+typedef struct _bsmt2000_chip bsmt2000_chip;
+struct _bsmt2000_chip
+{
+ sound_stream *stream; /* which stream are we using */
+ UINT8 last_register; /* last register address written */
+
+ INT8 * region_base; /* pointer to the base of the region */
+ int total_banks; /* number of total banks in the region */
+
+ bsmt2000_voice voice[MAX_VOICES]; /* the voices */
+ UINT16 * regmap[128]; /* mapping of registers to voice params */
+
+ UINT32 clock; /* original clock on the chip */
+ UINT8 stereo; /* stereo output? */
+ UINT8 voices; /* number of voices */
+ UINT8 adpcm; /* adpcm enabled? */
+
+ INT32 adpcm_current; /* current ADPCM sample */
+ INT32 adpcm_delta_n; /* current ADPCM scale factor */
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+const struct BSMT2000interface bsmt2000_interface_region_1 = { REGION_SOUND1 };
+const struct BSMT2000interface bsmt2000_interface_region_2 = { REGION_SOUND2 };
+const struct BSMT2000interface bsmt2000_interface_region_3 = { REGION_SOUND3 };
+const struct BSMT2000interface bsmt2000_interface_region_4 = { REGION_SOUND4 };
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* core implementation */
+static void *bsmt2000_start(int sndindex, int clock, const void *config);
+static void bsmt2000_reset(void *_chip);
+static void bsmt2000_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length);
+
+/* read/write access */
+static void bsmt2000_reg_write(bsmt2000_chip *chip, offs_t offset, UINT16 data);
+
+/* local functions */
+static void set_mode(bsmt2000_chip *chip);
+static void set_regmap(bsmt2000_chip *chip, UINT8 posbase, UINT8 ratebase, UINT8 endbase, UINT8 loopbase, UINT8 bankbase, UINT8 rvolbase, UINT8 lvolbase);
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ bsmt2000_start - initialization callback
+-------------------------------------------------*/
+
+static void *bsmt2000_start(int sndindex, int clock, const void *config)
+{
+ const struct BSMT2000interface *intf = config;
+ bsmt2000_chip *chip;
+ int voicenum;
+
+ /* allocate the chip */
+ chip = auto_malloc(sizeof(*chip));
+ memset(chip, 0, sizeof(*chip));
+
+ /* create a stream at a nominal sample rate (real one specified later) */
+ chip->stream = stream_create(0, 2, clock / 1000, chip, bsmt2000_update);
+ chip->clock = clock;
+
+ /* initialize the regions */
+ chip->region_base = (INT8 *)memory_region(intf->region);
+ chip->total_banks = memory_region_length(intf->region) / 0x10000;
+
+ /* register chip-wide data for save states */
+ state_save_register_item("bsmt2000", sndindex * 16, chip->last_register);
+ state_save_register_item("bsmt2000", sndindex * 16, chip->stereo);
+ state_save_register_item("bsmt2000", sndindex * 16, chip->voices);
+ state_save_register_item("bsmt2000", sndindex * 16, chip->adpcm);
+ state_save_register_item("bsmt2000", sndindex * 16, chip->adpcm_current);
+ state_save_register_item("bsmt2000", sndindex * 16, chip->adpcm_delta_n);
+
+ /* register voice-specific data for save states */
+ for (voicenum = 0; voicenum < MAX_VOICES; voicenum++)
+ {
+ bsmt2000_voice *voice = &chip->voice[voicenum];
+ int index = sndindex * 16 + voicenum;
+
+ state_save_register_item("bsmt2000", index, voice->pos);
+ state_save_register_item("bsmt2000", index, voice->rate);
+ state_save_register_item("bsmt2000", index, voice->loopend);
+ state_save_register_item("bsmt2000", index, voice->loopstart);
+ state_save_register_item("bsmt2000", index, voice->bank);
+ state_save_register_item("bsmt2000", index, voice->leftvol);
+ state_save_register_item("bsmt2000", index, voice->rightvol);
+ state_save_register_item("bsmt2000", index, voice->fraction);
+ }
+
+ /* reset the chip -- this also configures the default mode */
+ bsmt2000_reset(chip);
+ return chip;
+}
+
+
+/*-------------------------------------------------
+ bsmt2000_reset - chip reset callback
+-------------------------------------------------*/
+
+static void bsmt2000_reset(void *_chip)
+{
+ bsmt2000_chip *chip = _chip;
+ int voicenum;
+
+ /* reset all the voice data */
+ for (voicenum = 0; voicenum < MAX_VOICES; voicenum++)
+ {
+ bsmt2000_voice *voice = &chip->voice[voicenum];
+ memset(voice, 0, sizeof(*voice));
+ voice->leftvol = 0x7fff;
+ voice->rightvol = 0x7fff;
+ }
+
+ /* recompute the mode */
+ set_mode(chip);
+}
+
+
+/*-------------------------------------------------
+ bsmt2000_update - update callback for
+ sample generation
+-------------------------------------------------*/
+
+static void bsmt2000_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length)
+{
+ stream_sample_t *left = buffer[0];
+ stream_sample_t *right = buffer[1];
+ bsmt2000_chip *chip = param;
+ bsmt2000_voice *voice;
+ int samp, voicenum;
+
+ /* clear out the accumulator */
+ memset(left, 0, length * sizeof(left[0]));
+ memset(right, 0, length * sizeof(right[0]));
+
+ /* loop over voices */
+ for (voicenum = 0; voicenum < chip->voices; voicenum++)
+ {
+ voice = &chip->voice[voicenum];
+
+ /* compute the region base */
+ if (voice->bank < chip->total_banks)
+ {
+ INT8 *base = &chip->region_base[voice->bank * 0x10000];
+ UINT32 rate = voice->rate;
+ INT32 rvol = voice->rightvol;
+ INT32 lvol = chip->stereo ? voice->leftvol : rvol;
+ UINT16 pos = voice->pos;
+ UINT16 frac = voice->fraction;
+
+ /* loop while we still have samples to generate */
+ for (samp = 0; samp < length; samp++)
+ {
+#if ENABLE_INTERPOLATION
+ INT32 sample = (base[pos] * (0x800 - frac) + (base[pos + 1] * frac)) >> 11;
+#else
+ INT32 sample = base[pos];
+#endif
+ /* apply volumes and add */
+ left[samp] += sample * lvol;
+ right[samp] += sample * rvol;
+
+ /* update position */
+ frac += rate;
+ pos += frac >> 11;
+ frac &= 0x7ff;
+
+ /* check for loop end */
+ if (pos >= voice->loopend)
+ pos += voice->loopstart - voice->loopend;
+ }
+
+ /* update the position */
+ voice->pos = pos;
+ voice->fraction = frac;
+ }
+ }
+
+ /* compressed voice (11-voice model only) */
+ voice = &chip->voice[ADPCM_VOICE];
+ if (chip->adpcm && voice->bank < chip->total_banks && voice->rate)
+ {
+ INT8 *base = &chip->region_base[voice->bank * 0x10000];
+ INT32 rvol = voice->rightvol;
+ INT32 lvol = chip->stereo ? voice->leftvol : rvol;
+ UINT32 pos = voice->pos;
+ UINT32 frac = voice->fraction;
+
+ /* loop while we still have samples to generate */
+ for (samp = 0; samp < length && pos < voice->loopend; samp++)
+ {
+ /* apply volumes and add */
+ left[samp] += (chip->adpcm_current * lvol) >> 8;
+ right[samp] += (chip->adpcm_current * rvol) >> 8;
+
+ /* update position */
+ frac++;
+ if (frac == 6)
+ {
+ pos++;
+ frac = 0;
+ }
+
+ /* every 3 samples, we update the ADPCM state */
+ if (frac == 1 || frac == 4)
+ {
+ static const UINT8 delta_tab[] = { 58,58,58,58,77,102,128,154 };
+ int nibble = base[pos] >> ((frac == 1) ? 4 : 0);
+ int value = (INT8)(nibble << 4) >> 4;
+ int delta;
+
+ /* compute the delta for this sample */
+ delta = chip->adpcm_delta_n * value;
+ if (value > 0)
+ delta += chip->adpcm_delta_n >> 1;
+ else
+ delta -= chip->adpcm_delta_n >> 1;
+
+ /* add and clamp against the sample */
+ chip->adpcm_current += delta;
+ if (chip->adpcm_current >= 32767)
+ chip->adpcm_current = 32767;
+ else if (chip->adpcm_current <= -32768)
+ chip->adpcm_current = -32768;
+
+ /* adjust the delta multiplier */
+ chip->adpcm_delta_n = (chip->adpcm_delta_n * delta_tab[abs(value)]) >> 6;
+ if (chip->adpcm_delta_n > 2000)
+ chip->adpcm_delta_n = 2000;
+ else if (chip->adpcm_delta_n < 1)
+ chip->adpcm_delta_n = 1;
+ }
+ }
+
+ /* update the position */
+ voice->pos = pos;
+ voice->fraction = frac;
+
+ /* "rate" is a control register; clear it to 0 when done */
+ if (pos >= voice->loopend)
+ voice->rate = 0;
+ }
+
+ /* reduce the overall gain */
+ for (samp = 0; samp < length; samp++)
+ {
+ left[samp] >>= 9;
+ right[samp] >>= 9;
+ }
+}
+
+
+
+/***************************************************************************
+ READ/WRITE ACCESS
+***************************************************************************/
+
+/*-------------------------------------------------
+ bsmt2000_reg_write - handle a register write
+-------------------------------------------------*/
+
+static void bsmt2000_reg_write(bsmt2000_chip *chip, offs_t offset, UINT16 data)
+{
+#if LOG_COMMANDS
+ mame_printf_debug("BSMT write: reg %02X = %04X\n", offset, data);
+#endif
+
+ /* remember the last write */
+ chip->last_register = offset;
+
+ /* update the register */
+ if (offset < 0x80 && chip->regmap[offset] != NULL)
+ {
+ UINT16 *dest = chip->regmap[offset];
+
+ /* force an update, then write the data */
+ stream_update(chip->stream);
+ *dest = data;
+
+ /* special case: reset ADPCM parameters when writing to the ADPCM position */
+ if (dest == &chip->voice[ADPCM_VOICE].rate)
+ {
+ chip->adpcm_current = 0;
+ chip->adpcm_delta_n = 10;
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ PER-CHIP READ/WRITE HANDLERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ BSMT2000_data_0_w - write to chip 0
+-------------------------------------------------*/
+
+WRITE16_HANDLER( BSMT2000_data_0_w )
+{
+ bsmt2000_reg_write(sndti_token(SOUND_BSMT2000, 0), offset, data);
+}
+
+
+
+/***************************************************************************
+ LOCAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ set_mode - set the mode after reset
+-------------------------------------------------*/
+
+static void set_mode(bsmt2000_chip *chip)
+{
+ int sample_rate;
+
+ /* force an update */
+ stream_update(chip->stream);
+
+ /* the mode comes from the address of the last register accessed */
+ switch (chip->last_register)
+ {
+ /* mode 0: 24kHz, 12 channel PCM, 1 channel ADPCM, mono */
+ default:
+ case 0:
+ sample_rate = chip->clock / 1000;
+ chip->stereo = FALSE;
+ chip->voices = 12;
+ chip->adpcm = TRUE;
+ set_regmap(chip, 0x00, 0x18, 0x24, 0x30, 0x3c, 0x48, 0);
+ break;
+
+ /* mode 1: 24kHz, 11 channel PCM, 1 channel ADPCM, stereo */
+ case 1:
+ sample_rate = chip->clock / 1000;
+ chip->stereo = TRUE;
+ chip->voices = 11;
+ chip->adpcm = TRUE;
+ set_regmap(chip, 0x00, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d);
+ break;
+
+ /* mode 5: 24kHz, 12 channel PCM, stereo */
+ case 5:
+ sample_rate = chip->clock / 1000;
+ chip->stereo = TRUE;
+ chip->voices = 12;
+ chip->adpcm = FALSE;
+ set_regmap(chip, 0x00, 0x18, 0x24, 0x30, 0x3c, 0x54, 0x60);
+ break;
+
+ /* mode 6: 34kHz, 8 channel PCM, stereo */
+ case 6:
+ sample_rate = chip->clock / 706;
+ chip->stereo = TRUE;
+ chip->voices = 8;
+ chip->adpcm = FALSE;
+ set_regmap(chip, 0x00, 0x10, 0x18, 0x20, 0x28, 0x38, 0x40);
+ break;
+
+ /* mode 7: 32kHz, 9 channel PCM, stereo */
+ case 7:
+ sample_rate = chip->clock / 750;
+ chip->stereo = TRUE;
+ chip->voices = 9;
+ chip->adpcm = FALSE;
+ set_regmap(chip, 0x00, 0x12, 0x1b, 0x24, 0x2d, 0x3f, 0x48);
+ break;
+ }
+
+ /* update the sample rate */
+ stream_set_sample_rate(chip->stream, sample_rate);
+}
+
+
+/*-------------------------------------------------
+ set_regmap - initialize the register mapping
+-------------------------------------------------*/
+
+static void set_regmap(bsmt2000_chip *chip, UINT8 posbase, UINT8 ratebase, UINT8 endbase, UINT8 loopbase, UINT8 bankbase, UINT8 rvolbase, UINT8 lvolbase)
+{
+ int voice;
+
+ /* reset the map */
+ memset(chip->regmap, 0, sizeof(chip->regmap));
+
+ /* iterate over voices */
+ for (voice = 0; voice < chip->voices; voice++)
+ {
+ chip->regmap[posbase + voice] = &chip->voice[voice].pos;
+ chip->regmap[ratebase + voice] = &chip->voice[voice].rate;
+ chip->regmap[endbase + voice] = &chip->voice[voice].loopend;
+ chip->regmap[loopbase + voice] = &chip->voice[voice].loopstart;
+ chip->regmap[bankbase + voice] = &chip->voice[voice].bank;
+ chip->regmap[rvolbase + voice] = &chip->voice[voice].rightvol;
+ if (chip->stereo)
+ chip->regmap[lvolbase + voice] = &chip->voice[voice].leftvol;
+ }
+
+ /* set the ADPCM register */
+ if (chip->adpcm)
+ {
+ chip->regmap[0x6d] = &chip->voice[ADPCM_VOICE].loopend;
+ chip->regmap[0x6f] = &chip->voice[ADPCM_VOICE].bank;
+ chip->regmap[0x73] = &chip->voice[ADPCM_VOICE].rate;
+ chip->regmap[0x74] = &chip->voice[ADPCM_VOICE].rightvol;
+ chip->regmap[0x75] = &chip->voice[ADPCM_VOICE].pos;
+ if (chip->stereo)
+ chip->regmap[0x76] = &chip->voice[ADPCM_VOICE].leftvol;
+ }
+}
+
+
+
+/***************************************************************************
+ GET/SET INFO CALLBACKS
+***************************************************************************/
+
+/*-------------------------------------------------
+ bsmt2000_set_info - callback for setting chip
+ information
+-------------------------------------------------*/
+
+static void bsmt2000_set_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* no parameters to set */
+ }
+}
+
+
+/*-------------------------------------------------
+ bsmt2000_get_info - callback for retrieving
+ chip information
+-------------------------------------------------*/
+
+void bsmt2000_get_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case SNDINFO_PTR_SET_INFO: info->set_info = bsmt2000_set_info; break;
+ case SNDINFO_PTR_START: info->start = bsmt2000_start; break;
+ case SNDINFO_PTR_STOP: /* nothing */ break;
+ case SNDINFO_PTR_RESET: info->reset = bsmt2000_reset; break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case SNDINFO_STR_NAME: info->s = "BSMT2000"; break;
+ case SNDINFO_STR_CORE_FAMILY: info->s = "Data East Wavetable"; break;
+ case SNDINFO_STR_CORE_VERSION: info->s = "1.0"; break;
+ case SNDINFO_STR_CORE_FILE: info->s = __FILE__; break;
+ case SNDINFO_STR_CORE_CREDITS: info->s = "Copyright (c) 2004, The MAME Team"; break;
+ }
+}