summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/c6280.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/c6280.c')
-rw-r--r--trunk/src/emu/sound/c6280.c375
1 files changed, 375 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/c6280.c b/trunk/src/emu/sound/c6280.c
new file mode 100644
index 00000000000..27b6812aa6f
--- /dev/null
+++ b/trunk/src/emu/sound/c6280.c
@@ -0,0 +1,375 @@
+/*
+ HuC6280 sound chip emulator
+ by Charles MacDonald
+ E-mail: cgfm2@hotmail.com
+ WWW: http://cgfm2.emuviews.com
+
+ Thanks to:
+
+ - Paul Clifford for his PSG documentation.
+ - Richard Bannister for the TGEmu-specific sound updating code.
+ - http://www.uspto.gov for the PSG patents.
+ - All contributors to the tghack-list.
+
+ Changes:
+
+ (03/30/2003)
+ - Removed TGEmu specific code and added support functions for MAME.
+ - Modified setup code to handle multiple chips with different clock and
+ volume settings.
+
+ Missing features / things to do:
+
+ - Add LFO support. But do any games actually use it?
+
+ - Add shared index for waveform playback and sample writes. Almost every
+ game will reset the index prior to playback so this isn't an issue.
+
+ - While the noise emulation is complete, the data for the pseudo-random
+ bitstream is calculated by machine.rand() and is not a representation of what
+ the actual hardware does.
+
+ For some background on Hudson Soft's C62 chipset:
+
+ - http://www.hudsonsoft.net/ww/about/about.html
+ - http://www.hudson.co.jp/corp/eng/coinfo/history.html
+
+ Legal information:
+
+ Copyright Charles MacDonald
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "emu.h"
+#include "c6280.h"
+
+typedef struct {
+ UINT16 frequency;
+ UINT8 control;
+ UINT8 balance;
+ UINT8 waveform[32];
+ UINT8 index;
+ INT16 dda;
+ UINT8 noise_control;
+ UINT32 noise_counter;
+ UINT32 counter;
+} t_channel;
+
+typedef struct {
+ sound_stream *stream;
+ device_t *device;
+ device_t *cpudevice;
+ UINT8 select;
+ UINT8 balance;
+ UINT8 lfo_frequency;
+ UINT8 lfo_control;
+ t_channel channel[8];
+ INT16 volume_table[32];
+ UINT32 noise_freq_tab[32];
+ UINT32 wave_freq_tab[4096];
+} c6280_t;
+
+INLINE c6280_t *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == C6280);
+ return (c6280_t *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/* only needed for io_buffer */
+#include "cpu/h6280/h6280.h"
+
+
+static void c6280_init(device_t *device, c6280_t *p, double clk, double rate)
+{
+ const c6280_interface *intf = (const c6280_interface *)device->static_config();
+ int i;
+ double step;
+
+ /* Loudest volume level for table */
+ double level = 65535.0 / 6.0 / 32.0;
+
+ /* Clear context */
+ memset(p, 0, sizeof(c6280_t));
+
+ p->device = device;
+ p->cpudevice = device->machine().device(intf->cpu);
+ if (p->cpudevice == NULL)
+ fatalerror("c6280_init: no CPU found with tag of '%s'\n", device->tag());
+
+ /* Make waveform frequency table */
+ for(i = 0; i < 4096; i += 1)
+ {
+ step = ((clk / rate) * 4096) / (i+1);
+ p->wave_freq_tab[(1 + i) & 0xFFF] = (UINT32)step;
+ }
+
+ /* Make noise frequency table */
+ for(i = 0; i < 32; i += 1)
+ {
+ step = ((clk / rate) * 32) / (i+1);
+ p->noise_freq_tab[i] = (UINT32)step;
+ }
+
+ /* Make volume table */
+ /* PSG has 48dB volume range spread over 32 steps */
+ step = 48.0 / 32.0;
+ for(i = 0; i < 31; i++)
+ {
+ p->volume_table[i] = (UINT16)level;
+ level /= pow(10.0, step / 20.0);
+ }
+ p->volume_table[31] = 0;
+}
+
+
+static void c6280_write(c6280_t *p, int offset, int data)
+{
+ t_channel *q = &p->channel[p->select];
+
+ /* Update stream */
+ p->stream->update();
+
+ switch(offset & 0x0F)
+ {
+ case 0x00: /* Channel select */
+ p->select = data & 0x07;
+ break;
+
+ case 0x01: /* Global balance */
+ p->balance = data;
+ break;
+
+ case 0x02: /* Channel frequency (LSB) */
+ q->frequency = (q->frequency & 0x0F00) | data;
+ q->frequency &= 0x0FFF;
+ break;
+
+ case 0x03: /* Channel frequency (MSB) */
+ q->frequency = (q->frequency & 0x00FF) | (data << 8);
+ q->frequency &= 0x0FFF;
+ break;
+
+ case 0x04: /* Channel control (key-on, DDA mode, volume) */
+
+ /* 1-to-0 transition of DDA bit resets waveform index */
+ if((q->control & 0x40) && ((data & 0x40) == 0))
+ {
+ q->index = 0;
+ }
+ q->control = data;
+ break;
+
+ case 0x05: /* Channel balance */
+ q->balance = data;
+ break;
+
+ case 0x06: /* Channel waveform data */
+
+ switch(q->control & 0xC0)
+ {
+ case 0x00:
+ q->waveform[q->index & 0x1F] = data & 0x1F;
+ q->index = (q->index + 1) & 0x1F;
+ break;
+
+ case 0x40:
+ break;
+
+ case 0x80:
+ q->waveform[q->index & 0x1F] = data & 0x1F;
+ q->index = (q->index + 1) & 0x1F;
+ break;
+
+ case 0xC0:
+ q->dda = data & 0x1F;
+ break;
+ }
+
+ break;
+
+ case 0x07: /* Noise control (enable, frequency) */
+ q->noise_control = data;
+ break;
+
+ case 0x08: /* LFO frequency */
+ p->lfo_frequency = data;
+ break;
+
+ case 0x09: /* LFO control (enable, mode) */
+ p->lfo_control = data;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static STREAM_UPDATE( c6280_update )
+{
+ static const int scale_tab[] = {
+ 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
+ 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F
+ };
+ int ch;
+ int i;
+ c6280_t *p = (c6280_t *)param;
+
+ int lmal = (p->balance >> 4) & 0x0F;
+ int rmal = (p->balance >> 0) & 0x0F;
+ int vll, vlr;
+
+ lmal = scale_tab[lmal];
+ rmal = scale_tab[rmal];
+
+ /* Clear buffer */
+ for(i = 0; i < samples; i++)
+ {
+ outputs[0][i] = 0;
+ outputs[1][i] = 0;
+ }
+
+ for(ch = 0; ch < 6; ch++)
+ {
+ /* Only look at enabled channels */
+ if(p->channel[ch].control & 0x80)
+ {
+ int lal = (p->channel[ch].balance >> 4) & 0x0F;
+ int ral = (p->channel[ch].balance >> 0) & 0x0F;
+ int al = p->channel[ch].control & 0x1F;
+
+ lal = scale_tab[lal];
+ ral = scale_tab[ral];
+
+ /* Calculate volume just as the patent says */
+ vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal);
+ if(vll > 0x1F) vll = 0x1F;
+
+ vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal);
+ if(vlr > 0x1F) vlr = 0x1F;
+
+ vll = p->volume_table[vll];
+ vlr = p->volume_table[vlr];
+
+ /* Check channel mode */
+ if((ch >= 4) && (p->channel[ch].noise_control & 0x80))
+ {
+ /* Noise mode */
+ UINT32 step = p->noise_freq_tab[(p->channel[ch].noise_control & 0x1F) ^ 0x1F];
+ for(i = 0; i < samples; i += 1)
+ {
+ static int data = 0;
+ p->channel[ch].noise_counter += step;
+ if(p->channel[ch].noise_counter >= 0x800)
+ {
+ data = (p->device->machine().rand() & 1) ? 0x1F : 0;
+ }
+ p->channel[ch].noise_counter &= 0x7FF;
+ outputs[0][i] += (INT16)(vll * (data - 16));
+ outputs[1][i] += (INT16)(vlr * (data - 16));
+ }
+ }
+ else
+ if(p->channel[ch].control & 0x40)
+ {
+ /* DDA mode */
+ for(i = 0; i < samples; i++)
+ {
+ outputs[0][i] += (INT16)(vll * (p->channel[ch].dda - 16));
+ outputs[1][i] += (INT16)(vlr * (p->channel[ch].dda - 16));
+ }
+ }
+ else
+ {
+ /* Waveform mode */
+ UINT32 step = p->wave_freq_tab[p->channel[ch].frequency];
+ for(i = 0; i < samples; i += 1)
+ {
+ int offset;
+ INT16 data;
+ offset = (p->channel[ch].counter >> 12) & 0x1F;
+ p->channel[ch].counter += step;
+ p->channel[ch].counter &= 0x1FFFF;
+ data = p->channel[ch].waveform[offset];
+ outputs[0][i] += (INT16)(vll * (data - 16));
+ outputs[1][i] += (INT16)(vlr * (data - 16));
+ }
+ }
+ }
+ }
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* MAME specific code */
+/*--------------------------------------------------------------------------*/
+
+static DEVICE_START( c6280 )
+{
+ int rate = device->clock()/16;
+ c6280_t *info = get_safe_token(device);
+
+ /* Initialize PSG emulator */
+ c6280_init(device, info, device->clock(), rate);
+
+ /* Create stereo stream */
+ info->stream = device->machine().sound().stream_alloc(*device, 0, 2, rate, info, c6280_update);
+}
+
+READ8_DEVICE_HANDLER( c6280_r )
+{
+ c6280_t *info = get_safe_token(device);
+ return h6280io_get_buffer(info->cpudevice);
+}
+
+WRITE8_DEVICE_HANDLER( c6280_w )
+{
+ c6280_t *info = get_safe_token(device);
+ h6280io_set_buffer(info->cpudevice, data);
+ c6280_write(info, offset, data);
+}
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+DEVICE_GET_INFO( c6280 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c6280_t); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c6280 ); break;
+ case DEVINFO_FCT_STOP: /* nothing */ break;
+ case DEVINFO_FCT_RESET: /* nothing */ break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "HuC6280"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "????"); 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(C6280, c6280);