1221 lines
28 KiB
C
1221 lines
28 KiB
C
/*
|
|
|
|
YMF278B FM + Wave table Synthesizer (OPL4)
|
|
|
|
Timer and PCM YMF278B. The FM is shared with the ymf262.
|
|
|
|
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 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.
|
|
August 15, 2010 - Backport to MAME-style C from OpenMSX
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include "mamedef.h"
|
|
//#include "sndintrf.h"
|
|
//#include "streams.h"
|
|
//#include "cpuintrf.h"
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "ymf262.h"
|
|
#include "ymf278b.h"
|
|
|
|
#define NULL ((void *)0)
|
|
|
|
typedef struct
|
|
{
|
|
UINT32 startaddr;
|
|
UINT32 loopaddr;
|
|
UINT32 endaddr;
|
|
UINT32 step; /* fixed-point frequency step */
|
|
UINT32 stepptr; /* fixed-point pointer into the sample */
|
|
UINT32 pos;
|
|
INT16 sample1, sample2;
|
|
|
|
INT32 env_vol;
|
|
|
|
INT32 lfo_cnt;
|
|
INT32 lfo_step;
|
|
INT32 lfo_max;
|
|
|
|
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;
|
|
INT32 DL;
|
|
INT8 D2R;
|
|
INT8 RC; /* rate correction */
|
|
INT8 RR;
|
|
|
|
INT8 bits; /* width of the samples */
|
|
INT8 active; /* slot keyed on */
|
|
|
|
INT8 state;
|
|
INT8 lfo_active;
|
|
|
|
UINT8 Muted;
|
|
} YMF278BSlot;
|
|
|
|
typedef struct
|
|
{
|
|
YMF278BSlot slots[24];
|
|
|
|
UINT32 eg_cnt; /* Global envelope generator counter. */
|
|
|
|
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)(const device_config *, int);
|
|
void (*irq_callback)(int);
|
|
//const device_config *device;
|
|
|
|
UINT32 ROMSize;
|
|
UINT8 *rom;
|
|
UINT32 RAMSize;
|
|
UINT8 *ram;
|
|
int clock;
|
|
|
|
INT32 volume[256*4]; // precalculated attenuation values with some marging for enveloppe and pan levels
|
|
|
|
UINT8 regs[256];
|
|
|
|
void *fmchip;
|
|
UINT8 FMEnabled; // that saves a whole lot of CPU
|
|
//sound_stream * stream;
|
|
} YMF278BChip;
|
|
|
|
#define EG_SH 16 // 16.16 fixed point (EG timing)
|
|
#define EG_TIMER_OVERFLOW (1 << EG_SH)
|
|
|
|
// envelope output entries
|
|
#define ENV_BITS 10
|
|
#define ENV_LEN (1 << ENV_BITS)
|
|
#define ENV_STEP (128.0 / ENV_LEN)
|
|
#define MAX_ATT_INDEX ((1 << (ENV_BITS - 1)) - 1) // 511
|
|
#define MIN_ATT_INDEX 0
|
|
|
|
// Envelope Generator phases
|
|
#define EG_ATT 4
|
|
#define EG_DEC 3
|
|
#define EG_SUS 2
|
|
#define EG_REL 1
|
|
#define EG_OFF 0
|
|
|
|
#define EG_REV 5 // pseudo reverb
|
|
#define EG_DMP 6 // damp
|
|
|
|
// Pan values, units are -3dB, i.e. 8.
|
|
const INT32 pan_left[16] = {
|
|
0, 8, 16, 24, 32, 40, 48, 256, 256, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
const INT32 pan_right[16] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 256, 256, 48, 40, 32, 24, 16, 8
|
|
};
|
|
|
|
// Mixing levels, units are -3dB, and add some marging to avoid clipping
|
|
const INT32 mix_level[8] = {
|
|
8, 16, 24, 32, 40, 48, 56, 256
|
|
};
|
|
|
|
// decay level table (3dB per step)
|
|
// 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)
|
|
#define SC(db) (db * (2.0 / ENV_STEP))
|
|
const UINT32 dl_tab[16] = {
|
|
SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7),
|
|
SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31)
|
|
};
|
|
#undef SC
|
|
|
|
#define RATE_STEPS 8
|
|
const UINT8 eg_inc[15 * RATE_STEPS] = {
|
|
//cycle:0 1 2 3 4 5 6 7
|
|
0, 1, 0, 1, 0, 1, 0, 1, // 0 rates 00..12 0 (increment by 0 or 1)
|
|
0, 1, 0, 1, 1, 1, 0, 1, // 1 rates 00..12 1
|
|
0, 1, 1, 1, 0, 1, 1, 1, // 2 rates 00..12 2
|
|
0, 1, 1, 1, 1, 1, 1, 1, // 3 rates 00..12 3
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, // 4 rate 13 0 (increment by 1)
|
|
1, 1, 1, 2, 1, 1, 1, 2, // 5 rate 13 1
|
|
1, 2, 1, 2, 1, 2, 1, 2, // 6 rate 13 2
|
|
1, 2, 2, 2, 1, 2, 2, 2, // 7 rate 13 3
|
|
|
|
2, 2, 2, 2, 2, 2, 2, 2, // 8 rate 14 0 (increment by 2)
|
|
2, 2, 2, 4, 2, 2, 2, 4, // 9 rate 14 1
|
|
2, 4, 2, 4, 2, 4, 2, 4, // 10 rate 14 2
|
|
2, 4, 4, 4, 2, 4, 4, 4, // 11 rate 14 3
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay
|
|
8, 8, 8, 8, 8, 8, 8, 8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time)
|
|
0, 0, 0, 0, 0, 0, 0, 0, // 14 infinity rates for attack and decay(s)
|
|
};
|
|
|
|
#define O(a) (a * RATE_STEPS)
|
|
const UINT8 eg_rate_select[64] = {
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 0),O( 1),O( 2),O( 3),
|
|
O( 4),O( 5),O( 6),O( 7),
|
|
O( 8),O( 9),O(10),O(11),
|
|
O(12),O(12),O(12),O(12),
|
|
};
|
|
#undef O
|
|
|
|
// rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
|
// shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0
|
|
// mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0
|
|
#define O(a) (a)
|
|
const UINT8 eg_rate_shift[64] = {
|
|
O(12),O(12),O(12),O(12),
|
|
O(11),O(11),O(11),O(11),
|
|
O(10),O(10),O(10),O(10),
|
|
O( 9),O( 9),O( 9),O( 9),
|
|
O( 8),O( 8),O( 8),O( 8),
|
|
O( 7),O( 7),O( 7),O( 7),
|
|
O( 6),O( 6),O( 6),O( 6),
|
|
O( 5),O( 5),O( 5),O( 5),
|
|
O( 4),O( 4),O( 4),O( 4),
|
|
O( 3),O( 3),O( 3),O( 3),
|
|
O( 2),O( 2),O( 2),O( 2),
|
|
O( 1),O( 1),O( 1),O( 1),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
O( 0),O( 0),O( 0),O( 0),
|
|
};
|
|
#undef O
|
|
|
|
|
|
// number of steps to take in quarter of lfo frequency
|
|
// TODO check if frequency matches real chip
|
|
#define O(a) ((EG_TIMER_OVERFLOW / a) / 6)
|
|
const INT32 lfo_period[8] = {
|
|
O(0.168), O(2.019), O(3.196), O(4.206),
|
|
O(5.215), O(5.888), O(6.224), O(7.066)
|
|
};
|
|
#undef O
|
|
|
|
|
|
#define O(a) (a * 65536)
|
|
const INT32 vib_depth[8] = {
|
|
O(0), O(3.378), O(5.065), O(6.750),
|
|
O(10.114), O(20.170), O(40.106), O(79.307)
|
|
};
|
|
#undef O
|
|
|
|
|
|
#define SC(db) (INT32)(db * (2.0 / ENV_STEP))
|
|
const INT32 am_depth[8] = {
|
|
SC(0), SC(1.781), SC(2.906), SC(3.656),
|
|
SC(4.406), SC(5.906), SC(7.406), SC(11.91)
|
|
};
|
|
#undef SC
|
|
|
|
void ymf278b_slot_reset(YMF278BSlot* slot)
|
|
{
|
|
slot->wave = slot->FN = slot->OCT = slot->PRVB = slot->LD = slot->TL = slot->pan =
|
|
slot->lfo = slot->vib = slot->AM = 0;
|
|
slot->AR = slot->D1R = slot->DL = slot->D2R = slot->RC = slot->RR = 0;
|
|
slot->step = slot->stepptr = 0;
|
|
slot->bits = slot->startaddr = slot->loopaddr = slot->endaddr = 0;
|
|
slot->env_vol = MAX_ATT_INDEX;
|
|
|
|
slot->lfo_active = 0;
|
|
slot->lfo_cnt = slot->lfo_step = 0;
|
|
slot->lfo_max = lfo_period[0];
|
|
|
|
slot->state = EG_OFF;
|
|
slot->active = 0;
|
|
|
|
// not strictly needed, but avoid UMR on savestate
|
|
slot->pos = slot->sample1 = slot->sample2 = 0;
|
|
}
|
|
|
|
INLINE int ymf278b_slot_compute_rate(YMF278BSlot* slot, int val)
|
|
{
|
|
int res;
|
|
int oct;
|
|
|
|
if (val == 0)
|
|
return 0;
|
|
else 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;
|
|
}
|
|
|
|
INLINE int ymf278b_slot_compute_vib(YMF278BSlot* slot)
|
|
{
|
|
return (((slot->lfo_step << 8) / slot->lfo_max) * vib_depth[slot->vib]) >> 24;
|
|
}
|
|
|
|
|
|
INLINE int ymf278b_slot_compute_am(YMF278BSlot* slot)
|
|
{
|
|
if (slot->lfo_active && slot->AM)
|
|
return (((slot->lfo_step << 8) / slot->lfo_max) * am_depth[slot->AM]) >> 12;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
INLINE void ymf278b_slot_set_lfo(YMF278BSlot* slot, int newlfo)
|
|
{
|
|
slot->lfo_step = (((slot->lfo_step << 8) / slot->lfo_max) * newlfo) >> 8;
|
|
slot->lfo_cnt = (((slot->lfo_cnt << 8) / slot->lfo_max) * newlfo) >> 8;
|
|
|
|
slot->lfo = newlfo;
|
|
slot->lfo_max = lfo_period[slot->lfo];
|
|
}
|
|
|
|
INLINE void ymf278b_advance(YMF278BChip* chip)
|
|
{
|
|
YMF278BSlot* op;
|
|
int i;
|
|
UINT8 rate;
|
|
UINT8 shift;
|
|
UINT8 select;
|
|
|
|
chip->eg_cnt ++;
|
|
for (i = 0; i < 24; i ++)
|
|
{
|
|
op = &chip->slots[i];
|
|
|
|
if (op->lfo_active)
|
|
{
|
|
op->lfo_cnt ++;
|
|
if (op->lfo_cnt < op->lfo_max)
|
|
{
|
|
op->lfo_step ++;
|
|
}
|
|
else if (op->lfo_cnt < (op->lfo_max * 3))
|
|
{
|
|
op->lfo_step --;
|
|
}
|
|
else
|
|
{
|
|
op->lfo_step ++;
|
|
if (op->lfo_cnt == (op->lfo_max * 4))
|
|
op->lfo_cnt = 0;
|
|
}
|
|
}
|
|
|
|
// Envelope Generator
|
|
switch(op->state)
|
|
{
|
|
case EG_ATT: // attack phase
|
|
rate = ymf278b_slot_compute_rate(op, op->AR);
|
|
if (rate < 4)
|
|
break;
|
|
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += (~op->env_vol * eg_inc[select + ((chip->eg_cnt >> shift) & 7)]) >> 3;
|
|
if (op->env_vol <= MIN_ATT_INDEX)
|
|
{
|
|
op->env_vol = MIN_ATT_INDEX;
|
|
if (op->DL)
|
|
op->state = EG_DEC;
|
|
else
|
|
op->state = EG_SUS;
|
|
}
|
|
}
|
|
break;
|
|
case EG_DEC: // decay phase
|
|
rate = ymf278b_slot_compute_rate(op, op->D1R);
|
|
if (rate < 4)
|
|
break;
|
|
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)];
|
|
|
|
if ((op->env_vol > dl_tab[6]) && op->PRVB)
|
|
op->state = EG_REV;
|
|
else
|
|
{
|
|
if (op->env_vol >= op->DL)
|
|
op->state = EG_SUS;
|
|
}
|
|
}
|
|
break;
|
|
case EG_SUS: // sustain phase
|
|
rate = ymf278b_slot_compute_rate(op, op->D2R);
|
|
if (rate < 4)
|
|
break;
|
|
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)];
|
|
|
|
if ((op->env_vol > dl_tab[6]) && op->PRVB)
|
|
op->state = EG_REV;
|
|
else
|
|
{
|
|
if (op->env_vol >= MAX_ATT_INDEX)
|
|
{
|
|
op->env_vol = MAX_ATT_INDEX;
|
|
op->active = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EG_REL: // release phase
|
|
rate = ymf278b_slot_compute_rate(op, op->RR);
|
|
if (rate < 4)
|
|
break;
|
|
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)];
|
|
|
|
if ((op->env_vol > dl_tab[6]) && op->PRVB)
|
|
op->state = EG_REV;
|
|
else
|
|
{
|
|
if (op->env_vol >= MAX_ATT_INDEX)
|
|
{
|
|
op->env_vol = MAX_ATT_INDEX;
|
|
op->active = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case EG_REV: // pseudo reverb
|
|
// TODO improve env_vol update
|
|
rate = ymf278b_slot_compute_rate(op, 5);
|
|
//if (rate < 4)
|
|
// break;
|
|
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)];
|
|
|
|
if (op->env_vol >= MAX_ATT_INDEX)
|
|
{
|
|
op->env_vol = MAX_ATT_INDEX;
|
|
op->active = 0;
|
|
}
|
|
}
|
|
break;
|
|
case EG_DMP: // damping
|
|
// TODO improve env_vol update, damp is just fastest decay now
|
|
rate = 56;
|
|
shift = eg_rate_shift[rate];
|
|
if (! (chip->eg_cnt & ((1 << shift) - 1)))
|
|
{
|
|
select = eg_rate_select[rate];
|
|
op->env_vol += eg_inc[select + ((chip->eg_cnt >> shift) & 7)];
|
|
|
|
if (op->env_vol >= MAX_ATT_INDEX)
|
|
{
|
|
op->env_vol = MAX_ATT_INDEX;
|
|
op->active = 0;
|
|
}
|
|
}
|
|
break;
|
|
case EG_OFF:
|
|
// nothing
|
|
break;
|
|
|
|
default:
|
|
#ifdef _DEBUG
|
|
//logerror(...);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
INLINE UINT8 ymf278b_readMem(YMF278BChip* chip, offs_t address)
|
|
{
|
|
if (address < chip->ROMSize)
|
|
return chip->rom[address];
|
|
else if (address < chip->ROMSize + chip->RAMSize)
|
|
return chip->ram[address - chip->ROMSize];
|
|
else
|
|
return 255; // TODO check
|
|
}
|
|
|
|
INLINE UINT8* ymf278b_readMemAddr(YMF278BChip* chip, offs_t address)
|
|
{
|
|
if (address < chip->ROMSize)
|
|
return &chip->rom[address];
|
|
else if (address < chip->ROMSize + chip->RAMSize)
|
|
return &chip->ram[address - chip->ROMSize];
|
|
else
|
|
return NULL; // TODO check
|
|
}
|
|
|
|
INLINE void ymf278b_writeMem(YMF278BChip* chip, offs_t address, UINT8 value)
|
|
{
|
|
if (address < chip->ROMSize)
|
|
return; // can't write to ROM
|
|
else if (address < chip->ROMSize + chip->RAMSize)
|
|
chip->ram[address - chip->ROMSize] = value;
|
|
else
|
|
return; // can't write to unmapped memory
|
|
|
|
return;
|
|
}
|
|
|
|
INLINE INT16 ymf278b_getSample(YMF278BChip* chip, YMF278BSlot* op)
|
|
{
|
|
INT16 sample;
|
|
UINT32 addr;
|
|
UINT8* addrp;
|
|
|
|
switch (op->bits)
|
|
{
|
|
case 0:
|
|
// 8 bit
|
|
sample = ymf278b_readMem(chip, op->startaddr + op->pos) << 8;
|
|
break;
|
|
case 1:
|
|
// 12 bit
|
|
addr = op->startaddr + ((op->pos / 2) * 3);
|
|
addrp = ymf278b_readMemAddr(chip, addr);
|
|
if (op->pos & 1)
|
|
sample = (addrp[2] << 8) | ((addrp[1] << 4) & 0xF0);
|
|
else
|
|
sample = (addrp[0] << 8) | (addrp[1] & 0xF0);
|
|
break;
|
|
case 2:
|
|
// 16 bit
|
|
addr = op->startaddr + (op->pos * 2);
|
|
addrp = ymf278b_readMemAddr(chip, addr);
|
|
sample = (addrp[0] << 8) | addrp[1];
|
|
break;
|
|
default:
|
|
// TODO unspecified
|
|
sample = 0;
|
|
break;
|
|
}
|
|
return sample;
|
|
}
|
|
|
|
int ymf278b_anyActive(YMF278BChip* chip)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 24; i ++)
|
|
{
|
|
if (chip->slots[i].active)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ymf278b_pcm_update(void *_info, stream_sample_t** outputs, int samples)
|
|
{
|
|
YMF278BChip* chip = (YMF278BChip *)_info;
|
|
int i;
|
|
unsigned int j;
|
|
INT32 vl;
|
|
INT32 vr;
|
|
|
|
if (chip->FMEnabled)
|
|
{
|
|
/* memset is done by ymf262_update */
|
|
ymf262_update_one(chip->fmchip, outputs, samples);
|
|
}
|
|
else
|
|
{
|
|
memset(outputs[0], 0x00, samples * sizeof(stream_sample_t));
|
|
memset(outputs[1], 0x00, samples * sizeof(stream_sample_t));
|
|
}
|
|
|
|
if (! ymf278b_anyActive(chip))
|
|
{
|
|
// TODO update internal state, even if muted
|
|
// TODO also mute individual channels
|
|
return;
|
|
}
|
|
|
|
vl = mix_level[chip->pcm_l];
|
|
vr = mix_level[chip->pcm_r];
|
|
for (j = 0; j < samples; j ++)
|
|
{
|
|
for (i = 0; i < 24; i ++)
|
|
{
|
|
YMF278BSlot* sl;
|
|
INT16 sample;
|
|
int vol;
|
|
int volLeft;
|
|
int volRight;
|
|
|
|
sl = &chip->slots[i];
|
|
if (! sl->active || sl->Muted)
|
|
{
|
|
//outputs[0][j] += 0;
|
|
//outputs[1][j] += 0;
|
|
continue;
|
|
}
|
|
|
|
sample = (sl->sample1 * (0x10000 - sl->stepptr) +
|
|
sl->sample2 * sl->stepptr) >> 16;
|
|
vol = sl->TL + (sl->env_vol >> 2) + ymf278b_slot_compute_am(sl);
|
|
|
|
volLeft = vol + pan_left [sl->pan] + vl;
|
|
volRight = vol + pan_right[sl->pan] + vr;
|
|
// TODO prob doesn't happen in real chip
|
|
//volLeft = std::max(0, volLeft);
|
|
//volRight = std::max(0, volRight);
|
|
volLeft &= 0x3FF; // catch negative Volume values in a hardware-like way
|
|
volRight &= 0x3FF; // (anything beyond 0x100 results in *0)
|
|
|
|
outputs[0][j] += (sample * chip->volume[volLeft] ) >> 17;
|
|
outputs[1][j] += (sample * chip->volume[volRight]) >> 17;
|
|
|
|
if (sl->lfo_active && sl->vib)
|
|
{
|
|
int oct;
|
|
unsigned int step;
|
|
|
|
oct = sl->OCT;
|
|
if (oct & 8)
|
|
oct |= -8;
|
|
oct += 5;
|
|
step = (sl->FN | 1024) + ymf278b_slot_compute_vib(sl);
|
|
if (oct >= 0)
|
|
step <<= oct;
|
|
else
|
|
step >>= -oct;
|
|
sl->stepptr += step;
|
|
}
|
|
else
|
|
sl->stepptr += sl->step;
|
|
|
|
while (sl->stepptr >= 0x10000)
|
|
{
|
|
sl->stepptr -= 0x10000;
|
|
sl->sample1 = sl->sample2;
|
|
sl->pos ++;
|
|
if (sl->pos >= sl->endaddr)
|
|
sl->pos = sl->loopaddr;
|
|
sl->sample2 = ymf278b_getSample(chip, sl);
|
|
}
|
|
}
|
|
ymf278b_advance(chip);
|
|
}
|
|
}
|
|
|
|
INLINE void ymf278b_keyOnHelper(YMF278BChip* chip, YMF278BSlot* slot)
|
|
{
|
|
int oct;
|
|
unsigned int step;
|
|
|
|
slot->active = 1;
|
|
|
|
oct = slot->OCT;
|
|
if (oct & 8)
|
|
oct |= -8;
|
|
oct += 5;
|
|
step = slot->FN | 1024;
|
|
if (oct >= 0)
|
|
step <<= oct;
|
|
else
|
|
step >>= -oct;
|
|
slot->step = step;
|
|
slot->state = EG_ATT;
|
|
slot->stepptr = 0;
|
|
slot->pos = 0;
|
|
slot->sample1 = ymf278b_getSample(chip, slot);
|
|
slot->pos = 1;
|
|
slot->sample2 = ymf278b_getSample(chip, slot);
|
|
}
|
|
|
|
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:
|
|
//#ifdef _DEBUG
|
|
// logerror("YMF278B: Port A write %02x, %02x\n", reg, data);
|
|
//#endif
|
|
ymf262_write(chip->fmchip, 1, data);
|
|
if ((reg & 0xF0) == 0xB0 && (data & 0x20)) // Key On set
|
|
chip->FMEnabled = 0x01;
|
|
else if (reg == 0xBD && (data & 0x1F)) // one of the Rhythm bits set
|
|
chip->FMEnabled = 0x01;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ymf278b_B_w(YMF278BChip *chip, UINT8 reg, UINT8 data)
|
|
{
|
|
switch(reg)
|
|
{
|
|
case 0x05: // OPL3/OPL4 Enable
|
|
// actually Bit 1 enables OPL4 WaveTable Synth
|
|
ymf262_write(chip->fmchip, 3, data & ~0x02);
|
|
break;
|
|
default:
|
|
ymf262_write(chip->fmchip, 3, data);
|
|
if ((reg & 0xF0) == 0xB0 && (data & 0x20))
|
|
chip->FMEnabled = 0x01;
|
|
break;
|
|
}
|
|
//#ifdef _DEBUG
|
|
// logerror("YMF278B: Port B write %02x, %02x\n", reg, data);
|
|
//#endif
|
|
}
|
|
|
|
void ymf278b_C_w(YMF278BChip* chip, UINT8 reg, UINT8 data)
|
|
{
|
|
// Handle slot registers specifically
|
|
if (reg >= 0x08 && reg <= 0xF7)
|
|
{
|
|
int snum = (reg - 8) % 24;
|
|
YMF278BSlot* slot = &chip->slots[snum];
|
|
int base;
|
|
UINT8* buf;
|
|
int oct;
|
|
unsigned int step;
|
|
|
|
switch((reg - 8) / 24)
|
|
{
|
|
case 0:
|
|
//loadTime = time + LOAD_DELAY;
|
|
|
|
slot->wave = (slot->wave & 0x100) | data;
|
|
base = (slot->wave < 384 || ! chip->wavetblhdr) ?
|
|
(slot->wave * 12) :
|
|
(chip->wavetblhdr * 0x80000 + ((slot->wave - 384) * 12));
|
|
buf = ymf278b_readMemAddr(chip, base);
|
|
|
|
slot->bits = (buf[0] & 0xC0) >> 6;
|
|
ymf278b_slot_set_lfo(slot, (buf[7] >> 3) & 7);
|
|
slot->vib = buf[7] & 7;
|
|
slot->AR = buf[8] >> 4;
|
|
slot->D1R = buf[8] & 0xF;
|
|
slot->DL = dl_tab[buf[9] >> 4];
|
|
slot->D2R = buf[9] & 0xF;
|
|
slot->RC = buf[10] >> 4;
|
|
slot->RR = buf[10] & 0xF;
|
|
slot->AM = buf[11] & 7;
|
|
slot->startaddr = buf[2] | (buf[1] << 8) |
|
|
((buf[0] & 0x3F) << 16);
|
|
slot->loopaddr = buf[4] + (buf[3] << 8);
|
|
slot->endaddr = (((buf[6] + (buf[5] << 8)) ^ 0xFFFF) + 1);
|
|
|
|
if (chip->regs[reg + 4] & 0x080)
|
|
ymf278b_keyOnHelper(chip, slot);
|
|
break;
|
|
case 1:
|
|
slot->wave = (slot->wave & 0xFF) | ((data & 0x1) << 8);
|
|
slot->FN = (slot->FN & 0x380) | (data >> 1);
|
|
|
|
oct = slot->OCT;
|
|
if (oct & 8)
|
|
oct |= -8;
|
|
oct += 5;
|
|
step = slot->FN | 1024;
|
|
if (oct >= 0)
|
|
step <<= oct;
|
|
else
|
|
step >>= -oct;
|
|
slot->step = step;
|
|
break;
|
|
case 2:
|
|
slot->FN = (slot->FN & 0x07F) | ((data & 0x07) << 7);
|
|
slot->PRVB = ((data & 0x08) >> 3);
|
|
slot->OCT = ((data & 0xF0) >> 4);
|
|
|
|
oct = slot->OCT;
|
|
if (oct & 8)
|
|
oct |= -8;
|
|
oct += 5;
|
|
step = slot->FN | 1024;
|
|
if (oct >= 0)
|
|
step <<= oct;
|
|
else
|
|
step >>= -oct;
|
|
slot->step = step;
|
|
break;
|
|
case 3:
|
|
slot->TL = data >> 1;
|
|
slot->LD = data & 0x1;
|
|
|
|
// TODO
|
|
if (slot->LD) {
|
|
// directly change volume
|
|
} else {
|
|
// interpolate volume
|
|
}
|
|
break;
|
|
case 4:
|
|
if (data & 0x10)
|
|
{
|
|
// output to DO1 pin:
|
|
// this pin is not used in moonsound
|
|
// we emulate this by muting the sound
|
|
slot->pan = 8; // both left/right -inf dB
|
|
}
|
|
else
|
|
slot->pan = data & 0x0F;
|
|
|
|
if (data & 0x020)
|
|
{
|
|
// LFO reset
|
|
slot->lfo_active = 0;
|
|
slot->lfo_cnt = 0;
|
|
slot->lfo_max = lfo_period[slot->vib];
|
|
slot->lfo_step = 0;
|
|
}
|
|
else
|
|
{
|
|
// LFO activate
|
|
slot->lfo_active = 1;
|
|
}
|
|
|
|
switch (data >> 6)
|
|
{
|
|
case 0: // tone off, no damp
|
|
if (slot->active && (slot->state != EG_REV))
|
|
slot->state = EG_REL;
|
|
break;
|
|
case 2: // tone on, no damp
|
|
if (! (chip->regs[reg] & 0x080))
|
|
ymf278b_keyOnHelper(chip, slot);
|
|
break;
|
|
case 1: // tone off, damp
|
|
case 3: // tone on, damp
|
|
slot->state = EG_DMP;
|
|
break;
|
|
}
|
|
break;
|
|
case 5:
|
|
slot->vib = data & 0x7;
|
|
ymf278b_slot_set_lfo(slot, (data >> 3) & 0x7);
|
|
break;
|
|
case 6:
|
|
slot->AR = data >> 4;
|
|
slot->D1R = data & 0xF;
|
|
break;
|
|
case 7:
|
|
slot->DL = dl_tab[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 = (chip->memadr & 0x00FFFF) | (data << 16);
|
|
break;
|
|
|
|
case 0x04:
|
|
chip->memadr = (chip->memadr & 0xFF00FF) | (data << 8);
|
|
break;
|
|
|
|
case 0x05:
|
|
chip->memadr = (chip->memadr & 0xFFFF00) | data;
|
|
break;
|
|
|
|
case 0x06: // memory data
|
|
//busyTime = time + MEM_WRITE_DELAY;
|
|
ymf278b_writeMem(chip, chip->memadr, data);
|
|
chip->memadr = (chip->memadr + 1) & 0xFFFFFF;
|
|
break;
|
|
|
|
case 0xF8:
|
|
// TODO use these
|
|
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;
|
|
}
|
|
}
|
|
|
|
chip->regs[reg] = data;
|
|
}
|
|
|
|
UINT8 ymf278b_readReg(YMF278BChip* chip, UINT8 reg)
|
|
{
|
|
// no need to call updateStream(time)
|
|
UINT8 result;
|
|
switch(reg)
|
|
{
|
|
case 2: // 3 upper bits are device ID
|
|
result = (chip->regs[2] & 0x1F) | 0x20;
|
|
break;
|
|
|
|
case 6: // Memory Data Register
|
|
//busyTime = time + MEM_READ_DELAY;
|
|
result = ymf278b_readMem(chip, chip->memadr);
|
|
chip->memadr = (chip->memadr + 1) & 0xFFFFFF;
|
|
break;
|
|
|
|
default:
|
|
result = chip->regs[reg];
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
UINT8 ymf278b_peekReg(YMF278BChip* chip, UINT8 reg)
|
|
{
|
|
UINT8 result;
|
|
|
|
switch(reg)
|
|
{
|
|
case 2: // 3 upper bits are device ID
|
|
result = (chip->regs[2] & 0x1F) | 0x20;
|
|
break;
|
|
|
|
case 6: // Memory Data Register
|
|
result = ymf278b_readMem(chip, chip->memadr);
|
|
break;
|
|
|
|
default:
|
|
result = chip->regs[reg];
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
UINT8 ymf278b_readStatus(YMF278BChip* chip)
|
|
{
|
|
UINT8 result = 0;
|
|
//if (time < busyTime)
|
|
// result |= 0x01;
|
|
//if (time < loadTime)
|
|
// result |= 0x02;
|
|
return result;
|
|
}
|
|
|
|
//WRITE8_DEVICE_HANDLER( ymf278b_w )
|
|
void ymf278b_w(void *_info, offs_t offset, UINT8 data)
|
|
{
|
|
//YMF278BChip *chip = get_safe_token(device);
|
|
YMF278BChip *chip = (YMF278BChip *)_info;
|
|
|
|
switch (offset)
|
|
{
|
|
case 0:
|
|
chip->port_A = data;
|
|
ymf262_write(chip->fmchip, offset, data);
|
|
break;
|
|
|
|
case 1:
|
|
ymf278b_A_w(chip, chip->port_A, data);
|
|
break;
|
|
|
|
case 2:
|
|
chip->port_B = data;
|
|
ymf262_write(chip->fmchip, offset, data);
|
|
break;
|
|
|
|
case 3:
|
|
ymf278b_B_w(chip, chip->port_B, data);
|
|
break;
|
|
|
|
case 4:
|
|
chip->port_C = data;
|
|
break;
|
|
|
|
case 5:
|
|
ymf278b_C_w(chip, chip->port_C, data);
|
|
break;
|
|
|
|
default:
|
|
#ifdef _DEBUG
|
|
logerror("YMF278B: unexpected write at offset %X to ymf278b = %02X\n", offset, data);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ymf278b_clearRam(YMF278BChip* chip)
|
|
{
|
|
memset(chip->ram, 0, chip->RAMSize);
|
|
}
|
|
|
|
static void ymf278b_load_rom(YMF278BChip *chip)
|
|
{
|
|
chip->ROMSize = 0x00200000;
|
|
chip->rom = (UINT8*)malloc(chip->ROMSize);
|
|
memset(chip->rom, 0xFF, chip->ROMSize);
|
|
|
|
return;
|
|
}
|
|
|
|
static int ymf278b_init(YMF278BChip *chip, int clock, void (*cb)(int))
|
|
{
|
|
int rate;
|
|
|
|
rate = clock / 768;
|
|
//if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) ||
|
|
// CHIP_SAMPLING_MODE == 0x02)
|
|
// rate = CHIP_SAMPLE_RATE;
|
|
chip->fmchip = ymf262_init(clock * 8 / 19, rate);
|
|
chip->FMEnabled = 0x00;
|
|
|
|
chip->rom = NULL;
|
|
chip->irq_callback = cb;
|
|
//chip->timer_a = timer_alloc(device->machine, ymf278b_timer_a_tick, chip);
|
|
//chip->timer_b = timer_alloc(device->machine, ymf278b_timer_b_tick, chip);
|
|
chip->clock = clock;
|
|
|
|
ymf278b_load_rom(chip);
|
|
chip->RAMSize = 0x00080000;
|
|
chip->ram = (UINT8*)malloc(chip->RAMSize);
|
|
ymf278b_clearRam(chip);
|
|
|
|
return rate;
|
|
}
|
|
|
|
//static DEVICE_START( ymf278b )
|
|
int device_start_ymf278b(void **_info, int clock)
|
|
{
|
|
static const ymf278b_interface defintrf = { 0 };
|
|
const ymf278b_interface *intf;
|
|
int i;
|
|
YMF278BChip *chip;
|
|
int rate;
|
|
|
|
chip = (YMF278BChip *) calloc(1, sizeof(YMF278BChip));
|
|
*_info = (void *) chip;
|
|
|
|
//chip->device = device;
|
|
//intf = (device->static_config != NULL) ? (const ymf278b_interface *)device->static_config : &defintrf;
|
|
intf = &defintrf;
|
|
|
|
rate = ymf278b_init(chip, clock, intf->irq_callback);
|
|
//chip->stream = stream_create(device, 0, 2, device->clock/768, chip, ymf278b_pcm_update);
|
|
|
|
chip->memadr = 0; // avoid UMR
|
|
|
|
// Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB
|
|
for (i = 0; i < 256; i ++)
|
|
chip->volume[i] = 32768 * pow(2.0, (-0.375 / 6) * i);
|
|
for (i = 256; i < 256 * 4; i ++)
|
|
chip->volume[i] = 0;
|
|
for (i = 0; i < 24; i ++)
|
|
chip->slots[i].Muted = 0x00;;
|
|
|
|
return rate;
|
|
}
|
|
|
|
//static DEVICE_STOP( ymf278 )
|
|
void device_stop_ymf278b(void *_info)
|
|
{
|
|
YMF278BChip* chip = (YMF278BChip *)_info;
|
|
|
|
ymf262_shutdown(chip->fmchip);
|
|
free(chip->rom); chip->rom = NULL;
|
|
|
|
free(chip);
|
|
|
|
return;
|
|
}
|
|
|
|
void device_reset_ymf278b(void *_info)
|
|
{
|
|
YMF278BChip* chip = (YMF278BChip *)_info;
|
|
int i;
|
|
|
|
ymf262_reset_chip(chip->fmchip);
|
|
chip->FMEnabled = 0x00;
|
|
|
|
chip->eg_cnt = 0;
|
|
|
|
for (i = 0; i < 24; i ++)
|
|
ymf278b_slot_reset(&chip->slots[i]);
|
|
for (i = 255; i >= 0; i --) // reverse order to avoid UMR
|
|
ymf278b_C_w(chip, i, 0);
|
|
|
|
chip->wavetblhdr = chip->memmode = chip->memadr = 0;
|
|
chip->fm_l = chip->fm_r = chip->pcm_l = chip->pcm_r = 0;
|
|
//busyTime = time;
|
|
//loadTime = time;
|
|
}
|
|
|
|
void ymf278b_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
|
|
const UINT8* ROMData)
|
|
{
|
|
YMF278BChip *chip = (YMF278BChip *)_info;
|
|
|
|
if (chip->ROMSize != ROMSize)
|
|
{
|
|
chip->rom = (UINT8*)realloc(chip->rom, ROMSize);
|
|
chip->ROMSize = ROMSize;
|
|
memset(chip->rom, 0xFF, ROMSize);
|
|
}
|
|
if (DataStart > ROMSize)
|
|
return;
|
|
if (DataStart + DataLength > ROMSize)
|
|
DataLength = ROMSize - DataStart;
|
|
|
|
memcpy(chip->rom + DataStart, ROMData, DataLength);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void ymf278b_set_mute_mask(void *_info, UINT32 MuteMaskFM, UINT32 MuteMaskWT)
|
|
{
|
|
YMF278BChip *chip = (YMF278BChip *)_info;
|
|
UINT8 CurChn;
|
|
|
|
ymf262_set_mutemask(chip->fmchip, MuteMaskFM);
|
|
for (CurChn = 0; CurChn < 24; CurChn ++)
|
|
chip->slots[CurChn].Muted = (MuteMaskWT >> CurChn) & 0x01;
|
|
|
|
return;
|
|
}
|