summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/ymf278b.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/ymf278b.c')
-rw-r--r--trunk/src/emu/sound/ymf278b.c1114
1 files changed, 1114 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/ymf278b.c b/trunk/src/emu/sound/ymf278b.c
new file mode 100644
index 00000000000..eaa2655cc2d
--- /dev/null
+++ b/trunk/src/emu/sound/ymf278b.c
@@ -0,0 +1,1114 @@
+/*
+
+ 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 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 octave 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.
+
+ With further improvements over the years by MAME team.
+
+ TODO:
+ - accurate timing of envelopes
+ - LFO (vibrato, tremolo)
+ - integrate YMF262 (used by Fuuki games, not used by Psikyo and Metro games)
+ - able to hook up "Moonsound", supporting mixed ROM+RAM (for MSX driver in MESS)
+*/
+
+#include "emu.h"
+#include "ymf278b.h"
+
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+typedef struct
+{
+ INT16 wave; /* wavetable number */
+ INT16 F_NUMBER; /* frequency */
+ INT8 octave; /* octave */
+ INT8 preverb; /* pseudo-reverb */
+ INT8 DAMP; /* damping */
+ INT8 CH; /* output channel */
+ INT8 LD; /* level direct */
+ INT8 TL; /* total level */
+ INT8 pan; /* panpot */
+ INT8 LFO; /* LFO */
+ INT8 VIB; /* vibrato */
+ INT8 AM; /* tremolo */
+
+ INT8 AR; /* attack rate */
+ INT8 D1R; /* decay 1 rate */
+ INT8 DL; /* decay level */
+ INT8 D2R; /* decay 2 rate */
+ INT8 RC; /* rate correction */
+ INT8 RR; /* release rate */
+
+ UINT32 step; /* fixed-point frequency step */
+ UINT64 stepptr; /* fixed-point pointer into the sample */
+
+ INT8 active; /* channel is playing */
+ INT8 KEY_ON; /* 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;
+ INT8 env_preverb;
+
+ int num; /* slot number (for debug only) */
+ struct _YMF278BChip *chip; /* pointer back to parent chip */
+} YMF278BSlot;
+
+typedef struct _YMF278BChip
+{
+ UINT8 pcmregs[256];
+ YMF278BSlot slots[24];
+ INT8 wavetblhdr;
+ INT8 memmode;
+ INT32 memadr;
+
+ UINT8 status_busy, status_ld;
+ emu_timer *timer_busy;
+ emu_timer *timer_ld;
+ UINT8 exp;
+
+ INT32 fm_l, fm_r;
+ INT32 pcm_l, pcm_r;
+
+ attotime timer_base;
+ UINT8 timer_a_count, timer_b_count;
+ UINT8 enable, current_irq;
+ emu_timer *timer_a, *timer_b;
+ int irq_line;
+
+ UINT8 port_C, port_AB, lastport;
+ void (*irq_callback)(device_t *, int);
+ device_t *device;
+
+ const UINT8 *rom;
+ UINT32 romsize;
+ int clock;
+
+ // precomputed tables
+ UINT32 lut_ar[64]; // attack rate
+ UINT32 lut_dr[64]; // decay rate
+ INT32 volume[256*4]; // precalculated attenuation values with some margin for envelope and pan levels
+ int pan_left[16],pan_right[16]; // pan volume offsets
+ INT32 mix_level[8];
+
+ sound_stream * stream;
+} YMF278BChip;
+
+INLINE YMF278BChip *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == YMF278B);
+ return (YMF278BChip *)downcast<legacy_device_base *>(device)->token();
+}
+
+static void ymf278b_write_memory(YMF278BChip *chip, UINT32 offset, UINT8 data)
+{
+ logerror("YMF278B: Memory write %02x to %x\n", data, offset);
+}
+
+INLINE UINT8 ymf278b_read_memory(YMF278BChip *chip, UINT32 offset)
+{
+ if (offset >= chip->romsize)
+ {
+ // logerror("YMF278B: Memory read overflow %x\n", offset);
+ return 0xff;
+ }
+ return chip->rom[offset];
+}
+
+
+/**************************************************************************/
+
+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->octave;
+ if (oct & 8)
+ oct |= -8;
+
+ res = (oct+slot->RC)*2 + (slot->F_NUMBER & 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_env_vol_step(YMF278BSlot *slot, int val)
+{
+ int rate;
+ UINT32 res;
+
+ // rate override with damping/pseudo reverb
+ if (slot->DAMP)
+ rate = 56; // approximate, datasheet says it's slightly curved though
+ else if (slot->preverb && slot->env_vol > ((6*8)<<23))
+ {
+ // pseudo reverb starts at -18dB (6 in voltab)
+ slot->env_preverb = 1;
+ rate = 5;
+ }
+ else
+ rate = ymf278b_compute_rate(slot, val);
+
+ if (rate < 4)
+ res = 0;
+ else
+ res = (256U<<23) / slot->chip->lut_dr[rate];
+
+ return res;
+}
+
+static void ymf278b_compute_freq_step(YMF278BSlot *slot)
+{
+ UINT32 step;
+ int oct;
+
+ oct = slot->octave;
+ if(oct & 8)
+ oct |= -8;
+
+ step = (slot->F_NUMBER | 1024) << (oct + 8);
+ slot->step = step >> 3;
+}
+
+static void ymf278b_compute_envelope(YMF278BSlot *slot)
+{
+ switch (slot->env_step)
+ {
+ // Attack
+ case 0:
+ {
+ // Attack
+ int rate = ymf278b_compute_rate(slot, slot->AR);
+ slot->env_vol = 256U<<23;
+ slot->env_vol_lim = (256U<<23) - 1;
+
+ if (rate==63)
+ {
+ // immediate
+ LOG(("YMF278B: Attack skipped - "));
+ slot->env_vol = 0;
+ slot->env_step++;
+ ymf278b_compute_envelope(slot);
+ }
+ else if (rate<4)
+ {
+ slot->env_vol_step = 0;
+ }
+ else
+ {
+ // NOTE: attack rate is linear here, but datasheet shows a smooth curve
+ LOG(("YMF278B: Attack, val = %d, rate = %d, delay = %g\n", slot->AR, rate, slot->chip->lut_ar[rate]*1000.0));
+ slot->env_vol_step = ~((256U<<23) / slot->chip->lut_ar[rate]);
+ }
+
+ break;
+ }
+
+ // Decay 1
+ case 1:
+ if(slot->DL)
+ {
+ LOG(("YMF278B: Decay step 1, dl=%d, val = %d rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->DL, slot->D1R, ymf278b_compute_rate(slot, slot->D1R), slot->chip->lut_dr[ymf278b_compute_rate(slot, slot->D1R)]*1000.0, slot->preverb, slot->DAMP));
+ slot->env_vol_step = ymf278_compute_decay_env_vol_step(slot, slot->D1R);
+ slot->env_vol_lim = (slot->DL*8)<<23;
+ }
+ else
+ {
+ LOG(("YMF278B: Decay 1 skipped - "));
+ slot->env_step++;
+ ymf278b_compute_envelope(slot);
+ }
+
+ break;
+
+ // Decay 2
+ case 2:
+ LOG(("YMF278B: Decay step 2, val = %d, rate = %d, delay = %g, , PRVB = %d, DAMP = %d, current vol = %d\n", slot->D2R, ymf278b_compute_rate(slot, slot->D2R), slot->chip->lut_dr[ymf278b_compute_rate(slot, slot->D2R)]*1000.0, slot->preverb, slot->DAMP, slot->env_vol >> 23));
+ slot->env_vol_step = ymf278_compute_decay_env_vol_step(slot, slot->D2R);
+ slot->env_vol_lim = 256U<<23;
+ break;
+
+ // Decay 2 reached -96dB
+ case 3:
+ LOG(("YMF278B: Voice cleared because of decay 2\n"));
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ break;
+
+ // Release
+ case 4:
+ LOG(("YMF278B: Release, val = %d, rate = %d, delay = %g, PRVB = %d, DAMP = %d\n", slot->RR, ymf278b_compute_rate(slot, slot->RR), slot->chip->lut_dr[ymf278b_compute_rate(slot, slot->RR)]*1000.0, slot->preverb, slot->DAMP));
+ slot->env_vol_step = ymf278_compute_decay_env_vol_step(slot, slot->RR);
+ slot->env_vol_lim = 256U<<23;
+ break;
+
+ // Release reached -96dB
+ case 5:
+ LOG(("YMF278B: Release ends\n"));
+ slot->env_vol = 256U<<23;
+ slot->env_vol_step = 0;
+ slot->env_vol_lim = 0;
+ slot->active = 0;
+ break;
+
+ default: break;
+ }
+}
+
+static STREAM_UPDATE( ymf278b_pcm_update )
+{
+ YMF278BChip *chip = (YMF278BChip *)param;
+ int i, j;
+ YMF278BSlot *slot = NULL;
+ INT16 sample = 0;
+ INT32 *mixp;
+ INT32 vl, vr;
+ INT32 mix[44100*2];
+
+ memset(mix, 0, sizeof(mix[0])*samples*2);
+
+ for (i = 0; i < 24; i++)
+ {
+ slot = &chip->slots[i];
+
+ if (slot->active)
+ {
+ mixp = mix;
+
+ for (j = 0; j < samples; j++)
+ {
+ if (slot->stepptr >= slot->endaddr)
+ {
+ slot->stepptr = slot->stepptr - slot->endaddr + slot->loopaddr;
+ if (slot->stepptr >= slot->endaddr)
+ slot->stepptr = slot->loopaddr; // loop overflow
+ }
+
+ switch (slot->bits)
+ {
+ // 8 bit
+ case 0:
+ sample = ymf278b_read_memory(chip, slot->startaddr + (slot->stepptr>>16))<<8;
+ break;
+
+ // 12 bit
+ case 1:
+ if (slot->stepptr & 0x10000)
+ sample = ymf278b_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3+2)<<8 |
+ (ymf278b_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3+1) << 4 & 0xf0);
+ else
+ sample = ymf278b_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3)<<8 |
+ (ymf278b_read_memory(chip, slot->startaddr + (slot->stepptr>>17)*3+1) & 0xf0);
+ break;
+
+ // 16 bit
+ case 2:
+ sample = ymf278b_read_memory(chip, slot->startaddr + ((slot->stepptr>>16)*2))<<8 |
+ ymf278b_read_memory(chip, slot->startaddr + ((slot->stepptr>>16)*2)+1);
+ break;
+
+ // ?? bit, effect is unknown, datasheet says it's prohibited
+ case 3:
+ sample = 0;
+ 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;
+
+ // update envelope
+ slot->env_vol += slot->env_vol_step;
+ if (((INT32)(slot->env_vol - slot->env_vol_lim)) >= 0)
+ {
+ slot->env_step++;
+ ymf278b_compute_envelope(slot);
+ }
+ else if (slot->preverb && !slot->env_preverb && slot->env_step && slot->env_vol > ((6*8)<<23))
+ ymf278b_compute_envelope(slot);
+ }
+ }
+ }
+
+ mixp = mix;
+ vl = chip->mix_level[chip->pcm_l];
+ vr = chip->mix_level[chip->pcm_r];
+ for (i = 0; i < samples; i++)
+ {
+ outputs[0][i] = (*mixp++ * vl) >> 16;
+ outputs[1][i] = (*mixp++ * vr) >> 16;
+ }
+}
+
+static void ymf278b_irq_check(running_machine &machine, 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->device, chip->irq_line);
+}
+
+static TIMER_CALLBACK( ymf278b_timer_a_tick )
+{
+ YMF278BChip *chip = (YMF278BChip *)ptr;
+ if(!(chip->enable & 0x40))
+ {
+ chip->current_irq |= 0x40;
+ ymf278b_irq_check(machine, chip);
+ }
+}
+
+static TIMER_CALLBACK( ymf278b_timer_b_tick )
+{
+ YMF278BChip *chip = (YMF278BChip *)ptr;
+ if(!(chip->enable & 0x20))
+ {
+ chip->current_irq |= 0x20;
+ ymf278b_irq_check(machine, chip);
+ }
+}
+
+
+/**************************************************************************/
+
+static void ymf278b_A_w(running_machine &machine, YMF278BChip *chip, UINT8 reg, UINT8 data)
+{
+ // FM register array 0 (compatible with YMF262)
+ switch(reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ // timer a count
+ case 0x02:
+ if (data != chip->timer_a_count)
+ {
+ chip->timer_a_count = data;
+
+ // change period, ~80.8us * t
+ if (chip->enable & 1)
+ chip->timer_a->adjust(chip->timer_a->remaining(), 0, chip->timer_base * (256-data) * 4);
+ }
+ break;
+
+ // timer b count
+ case 0x03:
+ if (data != chip->timer_b_count)
+ {
+ chip->timer_b_count = data;
+
+ // change period, ~323.1us * t
+ if (chip->enable & 2)
+ chip->timer_b->adjust(chip->timer_b->remaining(), 0, chip->timer_base * (256-data) * 16);
+ }
+ break;
+
+ // timer control
+ case 0x04:
+ if(data & 0x80)
+ chip->current_irq = 0;
+ else
+ {
+ // reset timers
+ if((chip->enable ^ data) & 1)
+ {
+ attotime period = (data & 1) ? chip->timer_base * (256-chip->timer_a_count) * 4 : attotime::never;
+ chip->timer_a->adjust(period, 0, period);
+ }
+ if((chip->enable ^ data) & 2)
+ {
+ attotime period = (data & 2) ? chip->timer_base * (256-chip->timer_b_count) * 16 : attotime::never;
+ chip->timer_b->adjust(period, 0, period);
+ }
+
+ chip->enable = data;
+ chip->current_irq &= ~data;
+ }
+ ymf278b_irq_check(machine, chip);
+ break;
+
+ default:
+ logerror("YMF278B: Port A write %02x, %02x\n", reg, data);
+ break;
+ }
+}
+
+static void ymf278b_B_w(YMF278BChip *chip, UINT8 reg, UINT8 data)
+{
+ // FM register array 1 (compatible with YMF262)
+ switch(reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ // expansion register (NEW2/NEW)
+ case 0x05:
+ chip->exp = data;
+ break;
+
+ default:
+ logerror("YMF278B: Port B write %02x, %02x\n", reg, data);
+ break;
+ }
+}
+
+static TIMER_CALLBACK( ymf278b_timer_ld_clear )
+{
+ YMF278BChip *chip = (YMF278BChip *)ptr;
+ chip->status_ld = 0;
+}
+
+static void ymf278b_retrigger_note(YMF278BSlot *slot)
+{
+ // activate channel
+ if (slot->octave != 8)
+ slot->active = 1;
+
+ // reset sample pos and go to attack stage
+ slot->stepptr = 0;
+ slot->env_step = 0;
+ slot->env_preverb = 0;
+
+ ymf278b_compute_freq_step(slot);
+ ymf278b_compute_envelope(slot);
+}
+
+static void ymf278b_C_w(YMF278BChip *chip, UINT8 reg, UINT8 data, int init)
+{
+ if (!init)
+ {
+ // PCM regs are only accessible if NEW2 is set
+ if (~chip->exp & 2)
+ return;
+
+ chip->stream->update();
+ }
+
+ // 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:
+ {
+ attotime period;
+ UINT32 offset;
+ UINT8 p[12];
+ int i;
+
+ slot->wave &= 0x100;
+ slot->wave |= data;
+
+ // load wavetable header
+ if(slot->wave < 384 || !chip->wavetblhdr)
+ offset = slot->wave * 12;
+ else
+ offset = chip->wavetblhdr*0x80000 + (slot->wave - 384) * 12;
+ for (i = 0; i < 12; i++)
+ p[i] = ymf278b_read_memory(chip, offset+i);
+
+ slot->bits = (p[0]&0xc0)>>6;
+ 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;
+
+ // copy internal registers data
+ for (i = 7; i < 12; i++)
+ ymf278b_C_w(chip, 8 + snum + (i-2) * 24, p[i], 1);
+
+ // status register LD bit is on for approx 300us
+ chip->status_ld = 1;
+ period = attotime::from_usec(300);
+ if (chip->clock != YMF278B_STD_CLOCK)
+ period = (period * chip->clock) / YMF278B_STD_CLOCK;
+ chip->timer_ld->adjust(period);
+
+ // retrigger if key is on
+ if (slot->KEY_ON)
+ ymf278b_retrigger_note(slot);
+ else if (slot->active)
+ {
+ // deactivate channel
+ slot->env_step = 5;
+ ymf278b_compute_envelope(slot);
+ }
+
+ break;
+ }
+
+ case 1:
+ slot->wave &= 0xff;
+ slot->wave |= ((data&0x1)<<8);
+ slot->F_NUMBER &= 0x380;
+ slot->F_NUMBER |= (data>>1);
+ if (slot->active && (data ^ chip->pcmregs[reg]) & 0xfe)
+ {
+ ymf278b_compute_freq_step(slot);
+ ymf278b_compute_envelope(slot);
+ }
+ break;
+
+ case 2:
+ slot->F_NUMBER &= 0x07f;
+ slot->F_NUMBER |= ((data&0x07)<<7);
+ slot->preverb = (data&0x8)>>3;
+ slot->octave = (data&0xf0)>>4;
+ if (data != chip->pcmregs[reg])
+ {
+ // channel goes off if octave is set to -8 (datasheet says it's prohibited)
+ // (it is ok if this activates the channel while it was off: ymf278b_compute_envelope will reset it again if needed)
+ slot->active = (slot->octave != 8);
+
+ if (slot->active)
+ {
+ slot->env_preverb = 0;
+ ymf278b_compute_freq_step(slot);
+ ymf278b_compute_envelope(slot);
+ }
+ }
+ break;
+
+ case 3:
+ slot->TL = data>>1;
+ slot->LD = data&0x1;
+ break;
+
+ case 4:
+ slot->CH = (data&0x10)>>4;
+ // CH bit note: output to DO1 pin (1) or DO2 pin (0), this may
+ // silence the channel depending on how it's wired up on the PCB.
+ // For now, it's always enabled.
+ // (bit 5 (LFO reset) is also not hooked up yet)
+
+ slot->pan = data&0xf;
+ slot->DAMP = (data&0x40)>>6;
+ if (data & 0x80)
+ {
+ // don't retrigger if key was already on
+ if (slot->KEY_ON)
+ {
+ if ((data ^ chip->pcmregs[reg]) & 0x40)
+ ymf278b_compute_envelope(slot);
+
+ break;
+ }
+
+ ymf278b_retrigger_note(slot);
+ }
+ else if (slot->active)
+ {
+ // release
+ slot->env_step = 4;
+ ymf278b_compute_envelope(slot);
+ }
+ slot->KEY_ON = (data&0x80)>>7;
+ break;
+
+ case 5:
+ // LFO and vibrato level, not hooked up yet
+ slot->LFO = (data>>3)&0x7;
+ slot->VIB = data&0x7;
+ break;
+
+ case 6:
+ slot->AR = data>>4;
+ slot->D1R = data&0xf;
+ if (slot->active && data != chip->pcmregs[reg])
+ ymf278b_compute_envelope(slot);
+ break;
+
+ case 7:
+ slot->DL = data>>4;
+ slot->D2R = data&0xf;
+ if (slot->active && data != chip->pcmregs[reg])
+ ymf278b_compute_envelope(slot);
+ break;
+
+ case 8:
+ slot->RC = data>>4;
+ slot->RR = data&0xf;
+ if (slot->active && data != chip->pcmregs[reg])
+ ymf278b_compute_envelope(slot);
+ break;
+
+ case 9:
+ // tremolo level, not hooked up yet
+ slot->AM = data & 0x7;
+ break;
+ }
+ }
+ else
+ {
+ // All non-slot registers
+ switch (reg)
+ {
+ // LSI TEST
+ case 0x00:
+ case 0x01:
+ break;
+
+ case 0x02:
+ chip->wavetblhdr = (data>>2)&0x7;
+ chip->memmode = data&3;
+ break;
+
+ case 0x03:
+ case 0x04:
+ break;
+ case 0x05:
+ // set memory address
+ chip->memadr = (chip->pcmregs[3] & 0x3f) << 16 | chip->pcmregs[4] << 8 | data;
+ break;
+
+ case 0x06:
+ // memory data (ignored, we don't support RAM)
+ ymf278b_write_memory(chip, chip->memadr, data);
+ chip->memadr = (chip->memadr + 1) & 0x3fffff;
+ break;
+
+ case 0x07:
+ break; // unused
+
+ 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;
+
+ default:
+ logerror("YMF278B: Port C write %02x, %02x\n", reg, data);
+ break;
+ }
+ }
+
+ chip->pcmregs[reg] = data;
+}
+
+static TIMER_CALLBACK( ymf278b_timer_busy_clear )
+{
+ YMF278BChip *chip = (YMF278BChip *)ptr;
+ chip->status_busy = 0;
+}
+
+static void ymf278b_timer_busy_start(YMF278BChip *chip, int is_pcm)
+{
+ // status register BUSY bit is on for 56(FM) or 88(PCM) cycles
+ chip->status_busy = 1;
+ chip->timer_busy->adjust(attotime::from_hz(chip->clock / (is_pcm ? 88 : 56)));
+}
+
+WRITE8_DEVICE_HANDLER( ymf278b_w )
+{
+ YMF278BChip *chip = get_safe_token(device);
+
+ switch (offset)
+ {
+ case 0:
+ case 2:
+ ymf278b_timer_busy_start(chip, 0);
+ chip->port_AB = data;
+ chip->lastport = offset>>1 & 1;
+ break;
+
+ case 1:
+ case 3:
+ ymf278b_timer_busy_start(chip, 0);
+ if (chip->lastport) ymf278b_B_w(chip, chip->port_AB, data);
+ else ymf278b_A_w(device->machine(), chip, chip->port_AB, data);
+ break;
+
+ case 4:
+ ymf278b_timer_busy_start(chip, 1);
+ chip->port_C = data;
+ break;
+
+ case 5:
+ ymf278b_timer_busy_start(chip, 1);
+ ymf278b_C_w(chip, chip->port_C, data, 0);
+ break;
+
+ default:
+ logerror("%s: unexpected write at offset %X to ymf278b = %02X\n", device->machine().describe_context(), offset, data);
+ break;
+ }
+}
+
+
+READ8_DEVICE_HANDLER( ymf278b_r )
+{
+ YMF278BChip *chip = get_safe_token(device);
+ UINT8 ret = 0;
+
+ switch (offset)
+ {
+ // status register
+ case 0:
+ {
+ // bits 0 and 1 are only valid if NEW2 is set
+ UINT8 newbits = 0;
+ if (chip->exp & 2)
+ newbits = (chip->status_ld << 1) | chip->status_busy;
+
+ ret = newbits | chip->current_irq | (chip->irq_line == ASSERT_LINE ? 0x80 : 0x00);
+ break;
+ }
+
+ // FM regs can be read too (on contrary to what the datasheet says)
+ case 1:
+ case 3:
+ // but they're not implemented here yet
+ break;
+
+ // PCM regs
+ case 5:
+ // only accessible if NEW2 is set
+ if (~chip->exp & 2)
+ break;
+
+ switch (chip->port_C)
+ {
+ // special cases
+ case 2:
+ ret = (chip->pcmregs[chip->port_C] & 0x1f) | 0x20; // device ID in upper bits
+ break;
+ case 6:
+ ret = ymf278b_read_memory(chip, chip->memadr);
+ chip->memadr = (chip->memadr + 1) & 0x3fffff;
+ break;
+
+ default:
+ ret = chip->pcmregs[chip->port_C];
+ break;
+ }
+ break;
+
+ default:
+ logerror("%s: unexpected read at offset %X from ymf278b\n", device->machine().describe_context(), offset);
+ break;
+ }
+
+ return ret;
+}
+
+
+/**************************************************************************/
+
+static DEVICE_RESET( ymf278b )
+{
+ YMF278BChip *chip = get_safe_token(device);
+ int i;
+
+ // clear registers
+ for (i = 0; i <= 4; i++)
+ ymf278b_A_w(device->machine(), chip, i, 0);
+ ymf278b_B_w(chip, 5, 0);
+ for (i = 0; i < 8; i++)
+ ymf278b_C_w(chip, i, 0, 1);
+ for (i = 0xff; i >= 8; i--)
+ ymf278b_C_w(chip, i, 0, 1);
+ ymf278b_C_w(chip, 0xf8, 0x1b, 1);
+
+ chip->port_AB = chip->port_C = 0;
+ chip->lastport = 0;
+ chip->memadr = 0;
+
+ // init/silence channels
+ for (i = 0; i < 24 ; i++)
+ {
+ YMF278BSlot *slot = &chip->slots[i];
+
+ slot->LFO = 0;
+ slot->VIB = 0;
+ slot->AR = 0;
+ slot->D1R = 0;
+ slot->DL = 0;
+ slot->D2R = 0;
+ slot->RC = 0;
+ slot->RR = 0;
+ slot->AM = 0;
+
+ slot->startaddr = 0;
+ slot->loopaddr = 0;
+ slot->endaddr = 0;
+
+ slot->env_step = 5;
+ ymf278b_compute_envelope(slot);
+ }
+
+ chip->timer_a->reset();
+ chip->timer_b->reset();
+ chip->timer_busy->reset(); chip->status_busy = 0;
+ chip->timer_ld->reset(); chip->status_ld = 0;
+
+ chip->irq_line = CLEAR_LINE;
+}
+
+static void ymf278b_init(device_t *device, YMF278BChip *chip, void (*cb)(device_t *, int))
+{
+ int i;
+
+ chip->rom = *device->region();
+ chip->romsize = device->region()->bytes();
+ chip->clock = device->clock();
+ chip->irq_callback = cb;
+
+ chip->timer_base = attotime::from_hz(chip->clock) * (19*36);
+ chip->timer_a = device->machine().scheduler().timer_alloc(FUNC(ymf278b_timer_a_tick), chip);
+ chip->timer_b = device->machine().scheduler().timer_alloc(FUNC(ymf278b_timer_b_tick), chip);
+ chip->timer_busy = device->machine().scheduler().timer_alloc(FUNC(ymf278b_timer_busy_clear), chip);
+ chip->timer_ld = device->machine().scheduler().timer_alloc(FUNC(ymf278b_timer_ld_clear), chip);
+
+ for (i = 0; i < 24; i++)
+ {
+ chip->slots[i].num = i;
+ chip->slots[i].chip = chip;
+ }
+}
+
+static void precompute_rate_tables(YMF278BChip *chip)
+{
+ int i;
+
+ // decay rate
+ for (i = 0; i < 64; i++)
+ {
+ if (i <= 3)
+ chip->lut_dr[i] = 0;
+ else if (i >= 60)
+ chip->lut_dr[i] = 15 << 4;
+ else
+ chip->lut_dr[i] = (15 << (21 - i / 4)) / (4 + i % 4);
+ }
+
+ // attack rate (manual shows curve instead of linear though, so this is not entirely accurate)
+ for (i = 0; i < 64; i++)
+ {
+ if (i <= 3 || i == 63)
+ chip->lut_ar[i] = 0;
+ else if (i >= 60)
+ chip->lut_ar[i] = 17;
+ else
+ chip->lut_ar[i] = (67 << (15 - i / 4)) / (4 + i % 4);
+ }
+}
+
+static void ymf278b_register_save_state(device_t *device, YMF278BChip *chip)
+{
+ int i;
+
+ device->save_item(NAME(chip->pcmregs));
+ device->save_item(NAME(chip->wavetblhdr));
+ device->save_item(NAME(chip->memmode));
+ device->save_item(NAME(chip->memadr));
+ device->save_item(NAME(chip->status_busy));
+ device->save_item(NAME(chip->status_ld));
+ device->save_item(NAME(chip->exp));
+ device->save_item(NAME(chip->fm_l));
+ device->save_item(NAME(chip->fm_r));
+ device->save_item(NAME(chip->pcm_l));
+ device->save_item(NAME(chip->pcm_r));
+ device->save_item(NAME(chip->timer_a_count));
+ device->save_item(NAME(chip->timer_b_count));
+ device->save_item(NAME(chip->enable));
+ device->save_item(NAME(chip->current_irq));
+ device->save_item(NAME(chip->irq_line));
+ device->save_item(NAME(chip->port_AB));
+ device->save_item(NAME(chip->port_C));
+ device->save_item(NAME(chip->lastport));
+
+ for (i = 0; i < 24; ++i)
+ {
+ device->save_item(NAME(chip->slots[i].wave), i);
+ device->save_item(NAME(chip->slots[i].F_NUMBER), i);
+ device->save_item(NAME(chip->slots[i].octave), i);
+ device->save_item(NAME(chip->slots[i].preverb), i);
+ device->save_item(NAME(chip->slots[i].DAMP), i);
+ device->save_item(NAME(chip->slots[i].CH), i);
+ device->save_item(NAME(chip->slots[i].LD), i);
+ device->save_item(NAME(chip->slots[i].TL), i);
+ device->save_item(NAME(chip->slots[i].pan), i);
+ device->save_item(NAME(chip->slots[i].LFO), i);
+ device->save_item(NAME(chip->slots[i].VIB), i);
+ device->save_item(NAME(chip->slots[i].AM), i);
+
+ device->save_item(NAME(chip->slots[i].AR), i);
+ device->save_item(NAME(chip->slots[i].D1R), i);
+ device->save_item(NAME(chip->slots[i].DL), i);
+ device->save_item(NAME(chip->slots[i].D2R), i);
+ device->save_item(NAME(chip->slots[i].RC), i);
+ device->save_item(NAME(chip->slots[i].RR), i);
+
+ device->save_item(NAME(chip->slots[i].step), i);
+ device->save_item(NAME(chip->slots[i].stepptr), i);
+
+ device->save_item(NAME(chip->slots[i].active), i);
+ device->save_item(NAME(chip->slots[i].KEY_ON), i);
+ device->save_item(NAME(chip->slots[i].bits), i);
+ device->save_item(NAME(chip->slots[i].startaddr), i);
+ device->save_item(NAME(chip->slots[i].loopaddr), i);
+ device->save_item(NAME(chip->slots[i].endaddr), i);
+
+ device->save_item(NAME(chip->slots[i].env_step), i);
+ device->save_item(NAME(chip->slots[i].env_vol), i);
+ device->save_item(NAME(chip->slots[i].env_vol_step), i);
+ device->save_item(NAME(chip->slots[i].env_vol_lim), i);
+ device->save_item(NAME(chip->slots[i].env_preverb), i);
+ }
+}
+
+static DEVICE_START( ymf278b )
+{
+ static const ymf278b_interface defintrf = { 0 };
+ const ymf278b_interface *intf;
+ int i;
+ YMF278BChip *chip = get_safe_token(device);
+
+ chip->device = device;
+ intf = (device->static_config() != NULL) ? (const ymf278b_interface *)device->static_config() : &defintrf;
+
+ ymf278b_init(device, chip, intf->irq_callback);
+ chip->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock()/768, chip, ymf278b_pcm_update);
+
+ // rate tables
+ precompute_rate_tables(chip);
+
+ // 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 margin to avoid clipping
+ for(i=0; i<7; i++)
+ chip->mix_level[i] = chip->volume[8*i+13];
+ chip->mix_level[7] = 0;
+
+ // Register state for saving
+ ymf278b_register_save_state(device, chip);
+}
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+DEVICE_GET_INFO( ymf278b )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(YMF278BChip); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ymf278b ); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->start = DEVICE_RESET_NAME( ymf278b ); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "YMF278B"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Yamaha FM"); 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(YMF278B, ymf278b);