cog/Frameworks/GME/vgmplay/chips/ymf278b.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;
}