summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/ymf278b.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/ymf278b.c')
-rw-r--r--src/emu/sound/ymf278b.c835
1 files changed, 835 insertions, 0 deletions
diff --git a/src/emu/sound/ymf278b.c b/src/emu/sound/ymf278b.c
new file mode 100644
index 00000000000..a8ef72015f4
--- /dev/null
+++ b/src/emu/sound/ymf278b.c
@@ -0,0 +1,835 @@
+/*
+
+ YMF278B FM + Wave table Synthesizer (OPL4)
+
+ Timer and PCM YMF278B. The FM will be shared with the ymf262, eventually.
+
+ This chip roughly splits the difference between the Sega 315-5560 MultiPCM
+ (Multi32, Model 1/2) and YMF 292-F SCSP (later Model 2, STV, Saturn, Model 3).
+
+ Features as listed in LSI-4MF2782 data sheet:
+ FM Synthesis (same as YMF262)
+ 1. Sound generation mode
+ Two-operater mode
+ Generates eighteen voices or fifteen voices plus five rhythm sounds simultaneously
+ Four-operator mode
+ Generates six voices in four-operator mode plus six voices in two-operator mode simultaneously,
+ or generates six voices in four-operator mode plus three voices in two-operator mode plus five
+ rhythm sounds simultaneously
+ 2. Eight selectable waveforms
+ 3. Stereo output
+ Wave Table Synthesis
+ 1. Generates twenty-four voices simultaneously
+ 2. 44.1kHz sampling rate for output sound data
+ 3. Selectable from 8-bit, 12-bit and 16-bit word lengths for wave data
+ 4. Stereo output (16-stage panpot for each voice)
+ Wave Data
+ 1. Accepts 32M bit external memory at maximum
+ 2. Up to 512 wave tables
+ 3. External ROM or SRAM can be connected. With SRAM connected, the CPU can download wave data
+ 4. Outputs chip select signals for 1Mbit, 4Mbit, 8Mbit or 16Mbit memory
+ 5. Can be directly connected to the Yamaha YRW801 (Wave data ROM)
+ Features of YRW801 as listed in LSI 4RW801A2
+ Built-in wave data of tones which comply with GM system Level 1
+ Melody tone ....... 128 tones
+ Percussion tone ... 47 tones
+ 16Mbit capacity (2,097,152word x 8)
+
+ By R. Belmont and O. Galibert.
+
+ Copyright (c) 2002-2003 R. Belmont and O. Galibert.
+
+ This software is dual-licensed: it may be used in MAME and properly licensed
+ MAME derivatives under the terms of the MAME license. For use outside of
+ MAME and properly licensed derivatives, it is available under the
+ terms of the GNU Lesser General Public License (LGPL), version 2.1.
+ You may read the LGPL at http://www.gnu.org/licenses/lgpl.html
+
+ Changelog:
+ Sep. 8, 2002 - fixed ymf278b_compute_rate when OCT is negative (RB)
+ Dec. 11, 2002 - added ability to set non-standard clock rates (RB)
+ fixed envelope target for release (fixes missing
+ instruments in hotdebut).
+ Thanks to Team Japump! for MP3s from a real PCB.
+ fixed crash if MAME is run with no sound.
+ June 4, 2003 - Changed to dual-license with LGPL for use in OpenMSX.
+ OpenMSX contributed a bugfix where looped samples were
+ not being addressed properly, causing pitch fluctuation.
+*/
+
+#include <math.h>
+#include "sndintrf.h"
+#include "streams.h"
+#include "cpuintrf.h"
+#include "ymf278b.h"
+
+#undef VERBOSE
+
+typedef struct
+{
+ INT16 wave; /* wavetable number */
+ INT16 FN; /* f-number */
+ INT8 OCT; /* octave */
+ INT8 PRVB; /* pseudo-reverb */
+ INT8 LD; /* level direct */
+ INT8 TL; /* total level */
+ INT8 pan; /* panpot */
+ INT8 lfo; /* LFO */
+ INT8 vib; /* vibrato */
+ INT8 AM; /* AM level */
+
+ INT8 AR;
+ INT8 D1R;
+ INT8 DL;
+ INT8 D2R;
+ INT8 RC; /* rate correction */
+ INT8 RR;
+
+ UINT32 step; /* fixed-point frequency step */
+ UINT32 stepptr; /* fixed-point pointer into the sample */
+
+ INT8 active; /* slot keyed on */
+ INT8 bits; /* width of the samples */
+ UINT32 startaddr;
+ UINT32 loopaddr;
+ UINT32 endaddr;
+
+ int env_step;
+ UINT32 env_vol;
+ UINT32 env_vol_step;
+ UINT32 env_vol_lim;
+} YMF278BSlot;
+
+typedef struct
+{
+ YMF278BSlot slots[24];
+ INT8 lsitest0;
+ INT8 lsitest1;
+ INT8 wavetblhdr;
+ INT8 memmode;
+ INT32 memadr;
+
+ INT32 fm_l, fm_r;
+ INT32 pcm_l, pcm_r;
+
+ UINT8 timer_a_count, timer_b_count, enable, current_irq;
+ emu_timer *timer_a, *timer_b;
+ int irq_line;
+
+ UINT8 port_A, port_B, port_C;
+ void (*irq_callback)(int);
+
+ const UINT8 *rom;
+ int clock;
+
+ INT32 volume[256*4]; // precalculated attenuation values with some marging for enveloppe and pan levels
+ int pan_left[16], pan_right[16]; // pan volume offsets
+ INT32 mix_level[8];
+
+ sound_stream * stream;
+ int index;
+} YMF278BChip;
+
+static INT32 *mix;
+
+static int ymf278b_compute_rate(YMF278BSlot *slot, int val)
+{
+ int res, oct;
+
+ if(val == 0)
+ return 0;
+ if(val == 15)
+ return 63;
+ if(slot->RC != 15)
+ {
+ oct = slot->OCT;
+ if (oct & 8) oct |= -8;
+
+ res = (oct+slot->RC)*2 + (slot->FN & 0x200 ? 1 : 0) + val*4;
+ }
+ else
+ res = val * 4;
+ if(res < 0)
+ res = 0;
+ else if(res > 63)
+ res = 63;
+ return res;
+}
+
+static UINT32 ymf278_compute_decay_rate(int num)
+{
+ int samples;
+
+ if (num <= 3)
+ samples = 0;
+ else if (num >= 60)
+ samples = 15 << 4;
+ else
+ {
+ samples = (15 << (21 - num / 4)) / (4 + num % 4);
+ if (num % 4 && num / 4 <= 11)
+ samples += 2;
+ else if (num == 51)
+ samples += 2;
+ }
+
+ return samples;
+}
+
+static void ymf278b_envelope_next(YMF278BSlot *slot)
+{
+ if(slot->env_step == 0)
+ {
+ // Attack
+ slot->env_vol = (256U << 23) - 1;
+ slot->env_vol_lim = 256U<<23;
+#ifdef VERBOSE
+ logerror("YMF278B: Skipping attack (rate = %d)\n", slot->AR);
+#endif
+ slot->env_step++;
+ }
+ if(slot->env_step == 1)
+ {
+ // Decay 1
+ slot->env_vol = 0;
+ slot->env_step++;
+ if(slot->DL)
+ {
+ int rate = ymf278b_compute_rate(slot, slot->D1R);
+#ifdef VERBOSE
+ logerror("YMF278B: Decay step 1, dl=%d, val = %d rate = %d, delay = %g\n", slot->DL, slot->D1R, rate, ymf278_compute_decay_rate(rate)*1000.0);
+#endif
+ if(rate<4)
+ slot->env_vol_step = 0;
+ else
+ slot->env_vol_step = ((slot->DL*8)<<23) / ymf278_compute_decay_rate(rate);
+ slot->env_vol_lim = (slot->DL*8)<<23;
+ return;
+ }
+ }
+ if(slot->env_step == 2)
+ {
+ // Decay 2
+ int rate = ymf278b_compute_rate(slot, slot->D2R);
+#ifdef VERBOSE
+ logerror("YMF278B: Decay step 2, val = %d, rate = %d, delay = %g, current vol = %d\n", slot->D2R, rate, ymf278_compute_decay_rate(rate)*1000.0, slot->env_vol >> 23);
+#endif
+ if(rate<4)
+ slot->env_vol_step = 0;
+ else
+ slot->env_vol_step = ((256U-slot->DL*8)<<23) / ymf278_compute_decay_rate(rate);
+ slot->env_vol_lim = 256U<<23;
+ slot->env_step++;
+ return;
+ }
+ if(slot->env_step == 3)
+ {
+ // Decay 2 reached -96dB
+#ifdef VERBOSE
+ logerror("YMF278B: Voice cleared because of decay 2\n");
+#endif
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ return;
+ }
+ if(slot->env_step == 4)
+ {
+ // Release
+ int rate = ymf278b_compute_rate(slot, slot->RR);
+#ifdef VERBOSE
+ logerror("YMF278B: Release, val = %d, rate = %d, delay = %g\n", slot->RR, rate, ymf278_compute_decay_rate(rate)*1000.0);
+#endif
+ if(rate<4)
+ slot->env_vol_step = 0;
+ else
+ slot->env_vol_step = ((256U<<23)-slot->env_vol) / ymf278_compute_decay_rate(rate);
+ slot->env_vol_lim = 256U<<23;
+ slot->env_step++;
+ return;
+ }
+ if(slot->env_step == 5)
+ {
+ // Release reached -96dB
+#ifdef VERBOSE
+ logerror("YMF278B: Release ends\n");
+#endif
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ return;
+ }
+}
+
+static void ymf278b_pcm_update(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length)
+{
+ YMF278BChip *chip = param;
+ int i, j;
+ YMF278BSlot *slot = NULL;
+ INT16 sample = 0;
+ const UINT8 *rombase;
+ INT32 *mixp;
+ INT32 vl, vr;
+
+ memset(mix, 0, sizeof(mix[0])*length*2);
+
+ rombase = chip->rom;
+
+ for (i = 0; i < 24; i++)
+ {
+ slot = &chip->slots[i];
+
+ if (slot->active)
+ {
+ mixp = mix;
+
+ for (j = 0; j < length; j++)
+ {
+ switch (slot->bits)
+ {
+ case 8: // 8 bit
+ sample = rombase[slot->startaddr + (slot->stepptr>>16)]<<8;
+ break;
+
+ case 12: // 12 bit
+ if (slot->stepptr & 1)
+ sample = rombase[slot->startaddr + (slot->stepptr>>17)*3 + 2]<<8 | ((rombase[slot->startaddr + (slot->stepptr>>17)*3 + 1] << 4) & 0xf0);
+ else
+ sample = rombase[slot->startaddr + (slot->stepptr>>17)*3]<<8 | (rombase[slot->startaddr + (slot->stepptr>>17)*3 + 1] & 0xf0);
+ break;
+
+ case 16: // 16 bit
+ sample = rombase[slot->startaddr + ((slot->stepptr>>16)*2)]<<8;
+ sample |= rombase[slot->startaddr + ((slot->stepptr>>16)*2) + 1];
+ break;
+ }
+
+ *mixp++ += (sample * chip->volume[slot->TL+chip->pan_left [slot->pan]+(slot->env_vol>>23)])>>17;
+ *mixp++ += (sample * chip->volume[slot->TL+chip->pan_right[slot->pan]+(slot->env_vol>>23)])>>17;
+
+ // update frequency
+ slot->stepptr += slot->step;
+ if(slot->stepptr >= slot->endaddr)
+ {
+ slot->stepptr = slot->stepptr - slot->endaddr + slot->loopaddr;
+ // If the step is bigger than the loop, finish the sample forcibly
+ if(slot->stepptr >= slot->endaddr)
+ {
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ slot->stepptr = 0;
+ slot->step = 0;
+ }
+ }
+
+ // update envelope
+ slot->env_vol += slot->env_vol_step;
+ if(((INT32)(slot->env_vol - slot->env_vol_lim)) >= 0)
+ ymf278b_envelope_next(slot);
+ }
+ }
+ }
+
+ mixp = mix;
+ vl = chip->mix_level[chip->pcm_l];
+ vr = chip->mix_level[chip->pcm_r];
+ for (i = 0; i < length; i++)
+ {
+ outputs[0][i] = (*mixp++ * vl) >> 16;
+ outputs[1][i] = (*mixp++ * vr) >> 16;
+ }
+}
+
+static void ymf278b_irq_check(YMF278BChip *chip)
+{
+ int prev_line = chip->irq_line;
+ chip->irq_line = chip->current_irq ? ASSERT_LINE : CLEAR_LINE;
+ if(chip->irq_line != prev_line && chip->irq_callback)
+ chip->irq_callback(chip->irq_line);
+}
+
+static TIMER_CALLBACK_PTR( ymf278b_timer_a_tick )
+{
+ YMF278BChip *chip = param;
+ if(!(chip->enable & 0x40))
+ {
+ chip->current_irq |= 0x40;
+ ymf278b_irq_check(chip);
+ }
+}
+
+static TIMER_CALLBACK_PTR( ymf278b_timer_b_tick )
+{
+ YMF278BChip *chip = param;
+ if(!(chip->enable & 0x20))
+ {
+ chip->current_irq |= 0x20;
+ ymf278b_irq_check(chip);
+ }
+}
+
+static void ymf278b_timer_a_reset(YMF278BChip *chip)
+{
+ if(chip->enable & 1)
+ {
+ attotime period = ATTOTIME_IN_NSEC((256-chip->timer_a_count) * 80800);
+
+ if (chip->clock != YMF278B_STD_CLOCK)
+ period = attotime_div(attotime_mul(period, chip->clock), YMF278B_STD_CLOCK);
+
+ timer_adjust_ptr(chip->timer_a, period, period);
+ }
+ else
+ timer_adjust_ptr(chip->timer_a, attotime_never, attotime_zero);
+}
+
+static void ymf278b_timer_b_reset(YMF278BChip *chip)
+{
+ if(chip->enable & 2)
+ {
+ attotime period = ATTOTIME_IN_NSEC((256-chip->timer_b_count) * 323100);
+
+ if (chip->clock != YMF278B_STD_CLOCK)
+ period = attotime_div(attotime_mul(period, chip->clock), YMF278B_STD_CLOCK);
+
+ timer_adjust_ptr(chip->timer_a, period, period);
+ }
+ else
+ timer_adjust_ptr(chip->timer_b, attotime_never, attotime_zero);
+}
+
+static void ymf278b_A_w(YMF278BChip *chip, UINT8 reg, UINT8 data)
+{
+ switch(reg)
+ {
+ case 0x02:
+ chip->timer_a_count = data;
+ ymf278b_timer_a_reset(chip);
+ break;
+ case 0x03:
+ chip->timer_b_count = data;
+ ymf278b_timer_b_reset(chip);
+ break;
+ case 0x04:
+ if(data & 0x80)
+ chip->current_irq = 0;
+ else
+ {
+ UINT8 old_enable = chip->enable;
+ chip->enable = data;
+ chip->current_irq &= ~data;
+ if((old_enable ^ data) & 1)
+ ymf278b_timer_a_reset(chip);
+ if((old_enable ^ data) & 2)
+ ymf278b_timer_b_reset(chip);
+ }
+ ymf278b_irq_check(chip);
+ break;
+ default:
+ logerror("YMF278B: Port A write %02x, %02x\n", reg, data);
+ }
+}
+
+static void ymf278b_B_w(YMF278BChip *chip, UINT8 reg, UINT8 data)
+{
+ logerror("YMF278B: Port B write %02x, %02x\n", reg, data);
+}
+
+static void ymf278b_C_w(YMF278BChip *chip, UINT8 reg, UINT8 data)
+{
+ // Handle slot registers specifically
+ if (reg >= 0x08 && reg <= 0xf7)
+ {
+ YMF278BSlot *slot = NULL;
+ int snum;
+ snum = (reg-8) % 24;
+ slot = &chip->slots[snum];
+ switch((reg-8) / 24)
+ {
+ case 0:
+ {
+ const UINT8 *p;
+
+ slot->wave &= 0x100;
+ slot->wave |= data;
+
+ if(slot->wave < 384 || !chip->wavetblhdr)
+ p = chip->rom + (slot->wave * 12);
+ else
+ p = chip->rom + chip->wavetblhdr*0x80000 + ((slot->wave - 384) * 12);
+
+ switch (p[0]&0xc0)
+ {
+ case 0:
+ slot->bits = 8;
+ break;
+ case 0x40:
+ slot->bits = 12;
+ break;
+ case 0x80:
+ slot->bits = 16;
+ break;
+ }
+
+ slot->lfo = (p[7] >> 2) & 7;
+ slot->vib = p[7] & 7;
+ slot->AR = p[8] >> 4;
+ slot->D1R = p[8] & 0xf;
+ slot->DL = p[9] >> 4;
+ slot->D2R = p[9] & 0xf;
+ slot->RC = p[10] >> 4;
+ slot->RR = p[10] & 0xf;
+ slot->AM = p[11] & 7;
+
+ slot->startaddr = (p[2] | (p[1]<<8) | ((p[0]&0x3f)<<16));
+ slot->loopaddr = (p[4]<<16) | (p[3]<<24);
+ slot->endaddr = (p[6]<<16) | (p[5]<<24);
+ slot->endaddr -= 0x00010000U;
+ slot->endaddr ^= 0xffff0000U;
+ break;
+ }
+ case 1:
+ slot->wave &= 0xff;
+ slot->wave |= ((data&0x1)<<8);
+ slot->FN &= 0x380;
+ slot->FN |= (data>>1);
+ break;
+ case 2:
+ slot->FN &= 0x07f;
+ slot->FN |= ((data&0x07)<<7);
+ slot->PRVB = ((data&0x4)>>3);
+ slot->OCT = ((data&0xf0)>>4);
+ break;
+ case 3:
+ slot->TL = (data>>1);
+ slot->LD = data&0x1;
+ break;
+ case 4:
+ slot->pan = data&0xf;
+ if (data & 0x80)
+ {
+ unsigned int step;
+ int oct;
+
+ slot->active = 1;
+
+ oct = slot->OCT;
+ if(oct & 8)
+ oct |= -8;
+
+ slot->env_step = 0;
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 256U<<23;
+ slot->stepptr = 0;
+ slot->step = 0;
+
+ step = (slot->FN | 1024) << (oct + 7);
+ slot->step = step / 4;
+
+ ymf278b_envelope_next(slot);
+
+#ifdef VERBOSE
+ logerror("YMF278B: slot %2d wave %3d lfo=%d vib=%d ar=%d d1r=%d dl=%d d2r=%d rc=%d rr=%d am=%d\n", snum, slot->wave,
+ slot->lfo, slot->vib, slot->AR, slot->D1R, slot->DL, slot->D2R, slot->RC, slot->RR, slot->AM);
+ logerror(" b=%d, start=%x, loop=%x, end=%x, oct=%d, fn=%d, step=%x\n", slot->bits, slot->startaddr, slot->loopaddr>>16, slot->endaddr>>16, oct, slot->FN, slot->step);
+#endif
+ }
+ else
+ {
+#ifdef VERBOSE
+ logerror("YMF278B: slot %2d off\n", snum);
+#endif
+ if(slot->active)
+ {
+ slot->env_step = 4;
+ ymf278b_envelope_next(slot);
+ }
+ }
+ break;
+ case 5:
+ slot->vib = data&0x7;
+ slot->lfo = (data>>3)&0x7;
+ break;
+ case 6:
+ slot->AR = data>>4;
+ slot->D1R = data&0xf;
+ break;
+ case 7:
+ slot->DL = data>>4;
+ slot->D2R = data&0xf;
+ break;
+ case 8:
+ slot->RC = data>>4;
+ slot->RR = data&0xf;
+ break;
+ case 9:
+ slot->AM = data & 0x7;
+ break;
+ }
+ }
+ else
+ {
+ // All non-slot registers
+ switch (reg)
+ {
+ case 0x00: // TEST
+ case 0x01:
+ break;
+
+ case 0x02:
+ chip->wavetblhdr = (data>>2)&0x7;
+ chip->memmode = data&1;
+ break;
+
+ case 0x03:
+ chip->memadr &= 0xffff;
+ chip->memadr |= (data<<16);
+ break;
+
+ case 0x04:
+ chip->memadr &= 0xff00ff;
+ chip->memadr |= (data<<8);
+ break;
+
+ case 0x05:
+ chip->memadr &= 0xffff00;
+ chip->memadr |= data;
+ break;
+
+ case 0x06: // memory data (ignored, we don't support RAM)
+ case 0x07: // unused
+ break;
+
+ case 0xf8:
+ chip->fm_l = data & 0x7;
+ chip->fm_r = (data>>3)&0x7;
+ break;
+
+ case 0xf9:
+ chip->pcm_l = data & 0x7;
+ chip->pcm_r = (data>>3)&0x7;
+ break;
+ }
+ }
+}
+
+static UINT8 ymf278b_status_port_r(int num)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ return chip->current_irq | (chip->irq_line == ASSERT_LINE ? 0x80 : 0x00);
+}
+
+// Not implemented yet
+static UINT8 ymf278b_data_port_r(int num)
+{
+ return 0;
+}
+
+static void ymf278b_control_port_A_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ chip->port_A = data;
+}
+
+static void ymf278b_data_port_A_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ ymf278b_A_w(chip, chip->port_A, data);
+}
+
+static void ymf278b_control_port_B_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ chip->port_B = data;
+}
+
+static void ymf278b_data_port_B_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ ymf278b_B_w(chip, chip->port_B, data);
+}
+
+static void ymf278b_control_port_C_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ chip->port_C = data;
+}
+
+static void ymf278b_data_port_C_w(int num, UINT8 data)
+{
+ YMF278BChip *chip = sndti_token(SOUND_YMF278B, num);
+ ymf278b_C_w(chip, chip->port_C, data);
+}
+
+static void ymf278b_init(YMF278BChip *chip, UINT8 *rom, void (*cb)(int), int clock)
+{
+ chip->rom = rom;
+ chip->irq_callback = cb;
+ chip->timer_a = timer_alloc_ptr(ymf278b_timer_a_tick, chip);
+ chip->timer_b = timer_alloc_ptr(ymf278b_timer_b_tick, chip);
+ chip->irq_line = CLEAR_LINE;
+ chip->clock = clock;
+
+ mix = auto_malloc(44100*2*sizeof(*mix));
+}
+
+static void *ymf278b_start(int sndindex, int clock, const void *config)
+{
+ const struct YMF278B_interface *intf;
+ int i;
+ YMF278BChip *chip;
+
+ chip = auto_malloc(sizeof(*chip));
+ memset(chip, 0, sizeof(*chip));
+ chip->index = sndindex;
+
+ intf = config;
+
+ ymf278b_init(chip, memory_region(intf->region), intf->irq_callback, clock);
+ chip->stream = stream_create(0, 2, clock/768, chip, ymf278b_pcm_update);
+
+ // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB
+ for(i = 0; i < 256; i++)
+ chip->volume[i] = 65536*pow(2.0, (-0.375/6)*i);
+ for(i = 256; i < 256*4; i++)
+ chip->volume[i] = 0;
+
+ // Pan values, units are -3dB, i.e. 8.
+ for(i = 0; i < 16; i++)
+ {
+ chip->pan_left[i] = i < 7 ? i*8 : i < 9 ? 256 : 0;
+ chip->pan_right[i] = i < 8 ? 0 : i < 10 ? 256 : (16-i)*8;
+ }
+
+ // Mixing levels, units are -3dB, and add some marging to avoid clipping
+ for(i=0; i<7; i++)
+ chip->mix_level[i] = chip->volume[8*i+8];
+ chip->mix_level[7] = 0;
+
+ return chip;
+}
+
+
+READ8_HANDLER( YMF278B_status_port_0_r )
+{
+ return ymf278b_status_port_r(0);
+}
+
+READ8_HANDLER( YMF278B_data_port_0_r )
+{
+ return ymf278b_data_port_r(0);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_0_A_w )
+{
+ ymf278b_control_port_A_w(0, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_0_A_w )
+{
+ ymf278b_data_port_A_w(0, data);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_0_B_w )
+{
+ ymf278b_control_port_B_w(0, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_0_B_w )
+{
+ ymf278b_data_port_B_w(0, data);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_0_C_w )
+{
+ ymf278b_control_port_C_w(0, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_0_C_w )
+{
+ ymf278b_data_port_C_w(0, data);
+}
+
+
+READ8_HANDLER( YMF278B_status_port_1_r )
+{
+ return ymf278b_status_port_r(1);
+}
+
+READ8_HANDLER( YMF278B_data_port_1_r )
+{
+ return ymf278b_data_port_r(1);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_1_A_w )
+{
+ ymf278b_control_port_A_w(1, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_1_A_w )
+{
+ ymf278b_data_port_A_w(1, data);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_1_B_w )
+{
+ ymf278b_control_port_B_w(1, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_1_B_w )
+{
+ ymf278b_data_port_B_w(1, data);
+}
+
+WRITE8_HANDLER( YMF278B_control_port_1_C_w )
+{
+ ymf278b_control_port_C_w(1, data);
+}
+
+WRITE8_HANDLER( YMF278B_data_port_1_C_w )
+{
+ ymf278b_data_port_C_w(1, data);
+}
+
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+static void ymf278b_set_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* no parameters to set */
+ }
+}
+
+
+void ymf278b_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 = ymf278b_set_info; break;
+ case SNDINFO_PTR_START: info->start = ymf278b_start; break;
+ case SNDINFO_PTR_STOP: /* Nothing */ break;
+ case SNDINFO_PTR_RESET: /* Nothing */ break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case SNDINFO_STR_NAME: info->s = "YMF278B"; break;
+ case SNDINFO_STR_CORE_FAMILY: info->s = "Yamaha FM"; 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;
+ }
+}
+