/*
    Yamaha YMF271-F "OPX" emulator v0.1
    By R. Belmont.
    Based in part on YMF278B emulator by R. Belmont and O. Galibert.
    12June04 update by Toshiaki Nijiura
    Copyright (c) 2003 R. Belmont.

    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
*/

#include <math.h>
#include "sndintrf.h"
#include "streams.h"
#include "ymf271.h"

#define VERBOSE		(1)

#define MAXOUT		(+32767)
#define MINOUT		(-32768)

#define SIN_BITS		10
#define SIN_LEN			(1<<SIN_BITS)
#define SIN_MASK		(SIN_LEN-1)

#define LFO_LENGTH		256
#define LFO_SHIFT		8
#define PLFO_MAX		(+1.0)
#define PLFO_MIN		(-1.0)
#define ALFO_MAX		(+65536)
#define ALFO_MIN		(0)

#define log2(n) (log((float) n)/log((float) 2))

typedef struct
{
	INT8  extout;
	UINT8 lfoFreq;
	INT8  lfowave;
	INT8  pms, ams;
	INT8  detune;
	INT8  multiple;
	INT8  tl;
	INT8  keyscale;
	INT8  ar;
	INT8  decay1rate, decay2rate;
	INT8  decay1lvl;
	INT8  relrate;
	INT32 fns;
	INT8  block;
	INT8  feedback;
	INT8  waveform;
	INT8  accon;
	INT8  algorithm;
	INT8  ch0_level, ch1_level, ch2_level, ch3_level;

	UINT32 startaddr;
	UINT32 loopaddr;
	UINT32 endaddr;
	INT8   fs, srcnote, srcb;

	INT64 step;
	INT64 stepptr;

	INT8 active;
	INT8 bits;

	// envelope generator
	INT32 volume;
	INT32 env_state;
	INT32 env_attack_step;		// volume increase step in attack state
	INT32 env_decay1_step;
	INT32 env_decay2_step;
	INT32 env_release_step;

	INT64 feedback_modulation0;
	INT64 feedback_modulation1;

	INT32 lfo_phase, lfo_step;
	INT32 lfo_amplitude;
	double lfo_phasemod;
} YMF271Slot;

typedef struct
{
	INT8 sync, pfm;
} YMF271Group;

typedef struct
{
	YMF271Slot slots[48];
	YMF271Group groups[12];

	INT32 timerA, timerB;
	INT32 timerAVal, timerBVal;
	INT32 irqstate;
	INT8  status;
	INT8  enable;

	mame_timer *timA, *timB;

	INT8  reg0, reg1, reg2, reg3, pcmreg, timerreg;
	UINT32 ext_address;
	UINT8 ext_read;

	const UINT8 *rom;
	read8_handler ext_mem_read;
	write8_handler ext_mem_write;
	void (*irq_callback)(int);

	int index;
	UINT32 clock;
	sound_stream * stream;
} YMF271Chip;

// slot mapping assists
static const int fm_tab[] = { 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8, -1, 9, 10, 11, -1 };
static const int pcm_tab[] = { 0, 4, 8, -1, 12, 16, 20, -1, 24, 28, 32, -1, 36, 40, 44, -1 };

static INT16 *wavetable[8];
static double plfo_table[4][8][LFO_LENGTH];
static int alfo_table[4][LFO_LENGTH];
static INT32 *mix;

#define ENV_ATTACK		0
#define ENV_DECAY1		1
#define ENV_DECAY2		2
#define ENV_RELEASE		3

#define ENV_VOLUME_SHIFT	16

#define INF		100000000.0

static const double ARTime[] =
{
	INF,		INF,		INF,		INF,		6188.12,	4980.68,	4144.76,	3541.04,
	3094.06,	2490.34,	2072.38,	1770.52,	1547.03,	1245.17,	1036.19,	885.26,
	773.51,		622.59,		518.10,		441.63,		386.76,		311.29,		259.05,		221.32,
	193.38,		155.65,		129.52,		110.66,		96.69,		77.82,		64.76,		55.33,
	48.34,		38.91,		32.38,		27.66,		24.17,		19.46,		16.19,		13.83,
	12.09,		9.73,		8.10,		6.92,		6.04,		4.86,		4.05,		3.46,
	3.02,		2.47,		2.14,		1.88,		1.70,		1.38,		1.16,		1.02,
	0.88,		0.70,		0.57,		0.48,		0.43,		0.43,		0.43,		0.07
};

static const double DCTime[] =
{
	INF,		INF,		INF,		INF,		93599.64,	74837.91,	62392.02,	53475.56,
	46799.82,	37418.96,	31196.01,	26737.78,	23399.91,	18709.48,	15598.00,	13368.89,
	11699.95,	9354.74,	7799.00,	6684.44,	5849.98,	4677.37,	3899.50,	3342.22,
	2924.99,	2338.68,	1949.75,	1671.11,	1462.49,	1169.34,	974.88,		835.56,
	731.25,		584.67,		487.44,		417.78,		365.62,		292.34,		243.72,		208.89,
	182.81,		146.17,		121.86,		104.44,		91.41,		73.08,		60.93,		52.22,
	45.69,		36.55,		33.85,		26.09,		22.83,		18.28,		15.22,		13.03,
	11.41,		9.12,		7.60,		6.51,		5.69,		5.69,		5.69,		5.69
};

static const double LFO_frequency_table[256] =
{
	0.00066,	0.00068,	0.00070,	0.00073,	0.00075,	0.00078,	0.00081,	0.00084,
	0.00088,	0.00091,	0.00096,	0.00100,	0.00105,	0.00111,	0.00117,	0.00124,
	0.00131,	0.00136,	0.00140,	0.00145,	0.00150,	0.00156,	0.00162,	0.00168,
	0.00175,	0.00183,	0.00191,	0.00200,	0.00210,	0.00221,	0.00234,	0.00247,
	0.00263,	0.00271,	0.00280,	0.00290,	0.00300,	0.00312,	0.00324,	0.00336,
	0.00350,	0.00366,	0.00382,	0.00401,	0.00421,	0.00443,	0.00467,	0.00495,
	0.00526,	0.00543,	0.00561,	0.00580,	0.00601,	0.00623,	0.00647,	0.00673,
	0.00701,	0.00731,	0.00765,	0.00801,	0.00841,	0.00885,	0.00935,	0.00990,
	0.01051,	0.01085,	0.01122,	0.01160,	0.01202,	0.01246,	0.01294,	0.01346,
	0.01402,	0.01463,	0.01529,	0.01602,	0.01682,	0.01771,	0.01869,	0.01979,
	0.02103,	0.02171,	0.02243,	0.02320,	0.02403,	0.02492,	0.02588,	0.02692,
	0.02804,	0.02926,	0.03059,	0.03204,	0.03365,	0.03542,	0.03738,	0.03958,
	0.04206,	0.04341,	0.04486,	0.04641,	0.04807,	0.04985,	0.05176,	0.05383,
	0.05608,	0.05851,	0.06117,	0.06409,	0.06729,	0.07083,	0.07477,	0.07917,
	0.08411,	0.08683,	0.08972,	0.09282,	0.09613,	0.09969,	0.10353,	0.10767,
	0.11215,	0.11703,	0.12235,	0.12817,	0.13458,	0.14167,	0.14954,	0.15833,
	0.16823,	0.17365,	0.17944,	0.18563,	0.19226,	0.19938,	0.20705,	0.21533,
	0.22430,	0.23406,	0.24470,	0.25635,	0.26917,	0.28333,	0.29907,	0.31666,
	0.33646,	0.34731,	0.35889,	0.37126,	0.38452,	0.39876,	0.41410,	0.43066,
	0.44861,	0.46811,	0.48939,	0.51270,	0.53833,	0.56666,	0.59814,	0.63333,
	0.67291,	0.69462,	0.71777,	0.74252,	0.76904,	0.79753,	0.82820,	0.86133,
	0.89722,	0.93623,	0.97878,	1.02539,	1.07666,	1.13333,	1.19629,	1.26666,
	1.34583,	1.38924,	1.43555,	1.48505,	1.53809,	1.59509,	1.65640,	1.72266,
	1.79443,	1.87245,	1.95756,	2.05078,	2.15332,	2.26665,	2.39258,	2.53332,
	2.69165,	2.77848,	2.87109,	2.97010,	3.07617,	3.19010,	3.31280,	3.44531,
	3.58887,	3.74490,	3.92000,	4.10156,	4.30664,	4.53331,	4.78516,	5.06664,
	5.38330,	5.55696,	5.74219,	5.94019,	6.15234,	6.38021,	6.62560,	6.89062,
	7.17773,	7.48981,	7.83026,	8.20312,	8.61328,	9.06661,	9.57031,	10.13327,
	10.76660,	11.11391,	11.48438,	11.88039,	12.30469,	12.76042,	13.25120,	13.78125,
	13.35547,	14.97962,	15.66051,	16.40625,	17.22656,	18.13322,	19.14062,	20.26654,
	21.53320,	22.96875,	24.60938,	26.50240,	28.71094,	31.32102,	34.45312,	38.28125,
	43.06641,	49.21875,	57.42188,	68.90625,	86.13281,	114.84375,	172.26562,	344.53125
};

static const int RKS_Table[32][8] =
{
	{  0,  0,  0,  0,  0,  2,  4,  8 },
	{  0,  0,  0,  0,  1,  3,  5,  9 },
	{  0,  0,  0,  1,  2,  4,  6, 10 },
	{  0,  0,  0,  1,  3,  5,  7, 11 },
	{  0,  0,  1,  2,  4,  6,  8, 12 },
	{  0,  0,  1,  2,  5,  7,  9, 13 },
	{  0,  0,  1,  3,  6,  8, 10, 14 },
	{  0,  0,  1,  3,  7,  9, 11, 15 },
	{  0,  1,  2,  4,  8, 10, 12, 16 },
	{  0,  1,  2,  4,  9, 11, 13, 17 },
	{  0,  1,  2,  5, 10, 12, 14, 18 },
	{  0,  1,  2,  5, 11, 13, 15, 19 },
	{  0,  1,  3,  6, 12, 14, 16, 20 },
	{  0,  1,  3,  6, 13, 15, 17, 21 },
	{  0,  1,  3,  7, 14, 16, 18, 22 },
	{  0,  1,  3,  7, 15, 17, 19, 23 },
	{  0,  2,  4,  8, 16, 18, 20, 24 },
	{  0,  2,  4,  8, 17, 19, 21, 25 },
	{  0,  2,  4,  9, 18, 20, 22, 26 },
	{  0,  2,  4,  9, 19, 21, 23, 27 },
	{  0,  2,  5, 10, 20, 22, 24, 28 },
	{  0,  2,  5, 10, 21, 23, 25, 29 },
	{  0,  2,  5, 11, 22, 24, 26, 30 },
	{  0,  2,  5, 11, 23, 25, 27, 31 },
	{  0,  3,  6, 12, 24, 26, 28, 31 },
	{  0,  3,  6, 12, 25, 27, 29, 31 },
	{  0,  3,  6, 13, 26, 28, 30, 31 },
	{  0,  3,  6, 13, 27, 29, 31, 31 },
	{  0,  3,  7, 14, 28, 30, 31, 31 },
	{  0,  3,  7, 14, 29, 31, 31, 31 },
	{  0,  3,  7, 15, 30, 31, 31, 31 },
	{  0,  3,  7, 15, 31, 31, 31, 31 },
};

static const double channel_attenuation_table[16] =
{
	0.0, 2.5, 6.0, 8.5, 12.0, 14.5, 18.1, 20.6, 24.1, 26.6, 30.1, 32.6, 36.1, 96.1, 96.1, 96.1
};

static const int modulation_level[8] = { 16, 8, 4, 2, 1, 32, 64, 128 };

// feedback_level * 16
static const int feedback_level[8] = { 0, 1, 2, 4, 8, 16, 32, 64 };

static int channel_attenuation[16];
static int total_level[128];
static int env_volume_table[256];

INLINE int GET_KEYSCALED_RATE(int rate, int keycode, int keyscale)
{
	int newrate = rate + RKS_Table[keycode][keyscale];

	if (newrate > 63)
	{
		newrate = 63;
	}
	if (newrate < 0)
	{
		newrate = 0;
	}
	return newrate;
}

INLINE int GET_INTERNAL_KEYCODE(int block, int fns)
{
	int n43;
	if (fns < 0x780)
	{
		n43 = 0;
	}
	else if (fns < 0x900)
	{
		n43 = 1;
	}
	else if (fns < 0xa80)
	{
		n43 = 2;
	}
	else
	{
		n43 = 3;
	}

	return ((block & 7) * 4) + n43;
}

INLINE int GET_EXTERNAL_KEYCODE(int block, int fns)
{
	/* TODO: SrcB and SrcNote !? */
	int n43;
	if (fns < 0x100)
	{
		n43 = 0;
	}
	else if (fns < 0x300)
	{
		n43 = 1;
	}
	else if (fns < 0x500)
	{
		n43 = 2;
	}
	else
	{
		n43 = 3;
	}

	return ((block & 7) * 4) + n43;
}

static const double multiple_table[16] = { 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

static const double pow_table[16] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0.5, 1, 2, 4, 8, 16, 32, 64 };

static const double fs_frequency[4] = { 1.0/1.0, 1.0/2.0, 1.0/4.0, 1.0/8.0 };

INLINE void calculate_step(YMF271Slot *slot)
{
	double st;

	if (slot->waveform == 7)	// external waveform (PCM)
	{
		st = (double)(2 * (slot->fns | 2048)) * pow_table[slot->block] * fs_frequency[slot->fs];
		st = st * multiple_table[slot->multiple];

		// LFO phase modulation
		st *= slot->lfo_phasemod;

		st /= (double)(524288/65536);		// pre-multiply with 65536

		slot->step = (UINT64)st;
	}
	else						// internal waveform (FM)
	{
		st = (double)(2 * slot->fns) * pow_table[slot->block];
		st = st * multiple_table[slot->multiple] * (double)(SIN_LEN);

		// LFO phase modulation
		st *= slot->lfo_phasemod;

		st /= (double)(536870912/65536);	// pre-multiply with 65536

		slot->step = (UINT64)st;
	}
}

static void update_envelope(YMF271Slot *slot)
{
	switch (slot->env_state)
	{
		case ENV_ATTACK:
		{
			slot->volume += slot->env_attack_step;

			if (slot->volume >= (255 << ENV_VOLUME_SHIFT))
			{
				slot->volume = (255 << ENV_VOLUME_SHIFT);
				slot->env_state = ENV_DECAY1;
			}
			break;
		}

		case ENV_DECAY1:
		{
			int decay_level = 255 - (slot->decay1lvl << 4);
			slot->volume -= slot->env_decay1_step;

			if ((slot->volume >> (ENV_VOLUME_SHIFT)) <= decay_level)
			{
				slot->env_state = ENV_DECAY2;
			}
			break;
		}

		case ENV_DECAY2:
		{
			slot->volume -= slot->env_decay2_step;

			if (slot->volume < 0)
			{
				slot->volume = 0;
			}
			break;
		}

		case ENV_RELEASE:
		{
			slot->volume -= slot->env_release_step;

			if (slot->volume <= (0 << ENV_VOLUME_SHIFT))
			{
				slot->active = 0;
				slot->volume = 0;
			}
			break;
		}
	}
}

static void init_envelope(YMF271Slot *slot)
{
	int keycode, rate;
	int attack_length, decay1_length, decay2_length, release_length;
	int decay_level = 255 - (slot->decay1lvl << 4);

	double time;

	if (slot->waveform != 7)
	{
		keycode = GET_INTERNAL_KEYCODE(slot->block, slot->fns);
	}
	else
	{
		keycode = GET_EXTERNAL_KEYCODE(slot->block, slot->fns);
	}

	// init attack state
	rate = GET_KEYSCALED_RATE(slot->ar * 2, keycode, slot->keyscale);
	time = ARTime[rate];

	attack_length = (UINT32)((time * 44100.0) / 1000.0);		// attack end time in samples
	slot->env_attack_step = (int)(((double)(160-0) / (double)(attack_length)) * 65536.0);

	// init decay1 state
	rate = GET_KEYSCALED_RATE(slot->decay1rate * 2, keycode, slot->keyscale);
	time = DCTime[rate];

	decay1_length = (UINT32)((time * 44100.0) / 1000.0);
	slot->env_decay1_step = (int)(((double)(255-decay_level) / (double)(decay1_length)) * 65536.0);

	// init decay2 state
	rate = GET_KEYSCALED_RATE(slot->decay2rate * 2, keycode, slot->keyscale);
	time = DCTime[rate];

	decay2_length = (UINT32)((time * 44100.0) / 1000.0);
	slot->env_decay2_step = (int)(((double)(255-0) / (double)(decay2_length)) * 65536.0);

	// init release state
	rate = GET_KEYSCALED_RATE(slot->relrate * 4, keycode, slot->keyscale);
	time = ARTime[rate];

	release_length = (UINT32)((time * 44100.0) / 1000.0);
	slot->env_release_step = (int)(((double)(255-0) / (double)(release_length)) * 65536.0);

	slot->volume = (255-160) << ENV_VOLUME_SHIFT;		// -60db
	slot->env_state = ENV_ATTACK;
}

static void init_lfo(YMF271Slot *slot)
{
	slot->lfo_phase = 0;
	slot->lfo_amplitude = 0;
	slot->lfo_phasemod = 0;

	slot->lfo_step = (int)((((double)LFO_LENGTH * LFO_frequency_table[slot->lfoFreq]) / 44100.0) * 256.0);
}

INLINE void update_lfo(YMF271Slot *slot)
{
	slot->lfo_phase += slot->lfo_step;

	slot->lfo_amplitude = alfo_table[slot->lfowave][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)];
	slot->lfo_phasemod = plfo_table[slot->lfowave][slot->pms][(slot->lfo_phase >> LFO_SHIFT) & (LFO_LENGTH-1)];

	calculate_step(slot);
}

INLINE int calculate_slot_volume(YMF271Slot *slot)
{
	UINT64 volume;
	UINT64 env_volume;
	UINT64 lfo_volume = 65536;

	switch (slot->ams)
	{
		case 0: lfo_volume = 65536; break;	// 0dB
		case 1: lfo_volume = 65536 - (((UINT64)slot->lfo_amplitude * 33124) >> 16); break;	// 5.90625dB
		case 2: lfo_volume = 65536 - (((UINT64)slot->lfo_amplitude * 16742) >> 16); break;	// 11.8125dB
		case 3: lfo_volume = 65536 - (((UINT64)slot->lfo_amplitude * 4277) >> 16); break;	// 23.625dB
	}

	env_volume = ((UINT64)env_volume_table[255 - (slot->volume >> ENV_VOLUME_SHIFT)] * (UINT64)lfo_volume) >> 16;

	volume = ((UINT64)env_volume * (UINT64)total_level[slot->tl]) >> 16;

	return volume;
}

static void update_pcm(YMF271Chip *chip, int slotnum, INT32 *mixp, int length)
{
	int i;
	int final_volume;
	INT16 sample;
	INT64 ch0_vol, ch1_vol, ch2_vol, ch3_vol;
	const UINT8 *rombase;

	YMF271Slot *slot = &chip->slots[slotnum];
	rombase = chip->rom;

	if (!slot->active)
	{
		return;
	}

	if (slot->waveform != 7)
	{
		fatalerror("Waveform %d in update_pcm !!!", slot->waveform);
	}

	for (i = 0; i < length; i++)
	{
		if (slot->bits == 8)
		{
			sample = rombase[slot->startaddr + (slot->stepptr>>16)]<<8;
		}
		else
		{
			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);
		}

		update_envelope(slot);
		update_lfo(slot);

		final_volume = calculate_slot_volume(slot);

		ch0_vol = ((UINT64)final_volume * (UINT64)channel_attenuation[slot->ch0_level]) >> 16;
		ch1_vol = ((UINT64)final_volume * (UINT64)channel_attenuation[slot->ch1_level]) >> 16;
		ch2_vol = ((UINT64)final_volume * (UINT64)channel_attenuation[slot->ch2_level]) >> 16;
		ch3_vol = ((UINT64)final_volume * (UINT64)channel_attenuation[slot->ch3_level]) >> 16;

		if (ch0_vol > 65536) ch0_vol = 65536;
		if (ch1_vol > 65536) ch1_vol = 65536;

		*mixp++ += (sample * ch0_vol) >> 16;
		*mixp++ += (sample * ch1_vol) >> 16;

		slot->stepptr += slot->step;
		if ((slot->stepptr>>16) > slot->endaddr)
		{
			// kill non-frac
			slot->stepptr &= 0xffff;
			slot->stepptr |= (slot->loopaddr<<16);
		}
	}
}

// calculates 2 operator FM using algorithm 0
// <--------|
// +--[S1]--+--[S3]-->
INLINE INT32 calculate_2op_fm_0(YMF271Chip *chip, int slotnum1, int slotnum2)
{
	YMF271Slot *slot1 = &chip->slots[slotnum1];
	YMF271Slot *slot2 = &chip->slots[slotnum2];
	INT64 env1, env2;
	INT64 slot1_output, slot2_output;
	INT64 phase_mod;
	INT64 feedback;

	update_envelope(slot1);
	update_lfo(slot1);
	env1 = calculate_slot_volume(slot1);
	update_envelope(slot2);
	update_lfo(slot2);
	env2 = calculate_slot_volume(slot2);

	feedback = (slot1->feedback_modulation0 + slot1->feedback_modulation1) / 2;
	slot1->feedback_modulation0 = slot1->feedback_modulation1;

	slot1_output = wavetable[slot1->waveform][((slot1->stepptr + feedback) >> 16) & SIN_MASK];
	slot1_output = (slot1_output * env1) >> 16;

	phase_mod = ((slot1_output << (SIN_BITS-2)) * modulation_level[slot2->feedback]);
	slot2_output = wavetable[slot2->waveform][((slot2->stepptr + phase_mod) >> 16) & SIN_MASK];
	slot2_output = (slot2_output * env2) >> 16;

	slot1->feedback_modulation1 = (((slot1_output << (SIN_BITS-2)) * feedback_level[slot1->feedback]) / 16);

	slot1->stepptr += slot1->step;
	slot2->stepptr += slot2->step;

	return slot2_output;
}

// calculates 2 operator FM using algorithm 1
// <-----------------|
// +--[S1]--+--[S3]--|-->
INLINE INT32 calculate_2op_fm_1(YMF271Chip *chip, int slotnum1, int slotnum2)
{
	YMF271Slot *slot1 = &chip->slots[slotnum1];
	YMF271Slot *slot2 = &chip->slots[slotnum2];
	INT64 env1, env2;
	INT64 slot1_output, slot2_output;
	INT64 phase_mod;
	INT64 feedback;

	update_envelope(slot1);
	update_lfo(slot1);
	env1 = calculate_slot_volume(slot1);
	update_envelope(slot2);
	update_lfo(slot2);
	env2 = calculate_slot_volume(slot2);

	feedback = (slot1->feedback_modulation0 + slot1->feedback_modulation1) / 2;
	slot1->feedback_modulation0 = slot1->feedback_modulation1;

	slot1_output = wavetable[slot1->waveform][((slot1->stepptr + feedback) >> 16) & SIN_MASK];
	slot1_output = (slot1_output * env1) >> 16;

	phase_mod = ((slot1_output << (SIN_BITS-2)) * modulation_level[slot2->feedback]);
	slot2_output = wavetable[slot2->waveform][((slot2->stepptr + phase_mod) >> 16) & SIN_MASK];
	slot2_output = (slot2_output * env2) >> 16;

	slot1->feedback_modulation1 = (((slot2_output << (SIN_BITS-2)) * feedback_level[slot1->feedback]) / 16);

	slot1->stepptr += slot1->step;
	slot2->stepptr += slot2->step;

	return slot2_output;
}

// calculates the output of one FM operator
INLINE INT32 calculate_1op_fm_0(YMF271Chip *chip, int slotnum, int phase_modulation)
{
	YMF271Slot *slot = &chip->slots[slotnum];
	INT64 env;
	INT64 slot_output;
	INT64 phase_mod = phase_modulation;

	update_envelope(slot);
	update_lfo(slot);
	env = calculate_slot_volume(slot);

	phase_mod = ((phase_mod << (SIN_BITS-2)) * modulation_level[slot->feedback]);

	slot_output = wavetable[slot->waveform][((slot->stepptr + phase_mod) >> 16) & SIN_MASK];
	slot->stepptr += slot->step;

	slot_output = (slot_output * env) >> 16;

	return slot_output;
}

// calculates the output of one FM operator with feedback modulation
// <--------|
// +--[S1]--|
INLINE INT32 calculate_1op_fm_1(YMF271Chip *chip, int slotnum)
{
	YMF271Slot *slot = &chip->slots[slotnum];
	INT64 env;
	INT64 slot_output;
	INT64 feedback;

	update_envelope(slot);
	update_lfo(slot);
	env = calculate_slot_volume(slot);

	feedback = slot->feedback_modulation0 + slot->feedback_modulation1;
	slot->feedback_modulation0 = slot->feedback_modulation1;

	slot_output = wavetable[slot->waveform][((slot->stepptr + feedback) >> 16) & SIN_MASK];
	slot_output = (slot_output * env) >> 16;

	slot->feedback_modulation1 = (((slot_output << (SIN_BITS-2)) * feedback_level[slot->feedback]) / 16);
	slot->stepptr += slot->step;

	return slot_output;
}

static void ymf271_update(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length)
{
	int i, j;
	int op;
	INT32 *mixp;
	YMF271Chip *chip = param;

	memset(mix, 0, sizeof(mix[0])*length*2);

	for (j = 0; j < 12; j++)
	{
		YMF271Group *slot_group = &chip->groups[j];
		mixp = &mix[0];

		if (slot_group->pfm && slot_group->sync != 3)
		{
			mame_printf_debug("Group %d: PFM, Sync = %d, Waveform Slot1 = %d, Slot2 = %d, Slot3 = %d, Slot4 = %d\n",
				j, slot_group->sync, chip->slots[j+0].waveform, chip->slots[j+12].waveform, chip->slots[j+24].waveform, chip->slots[j+36].waveform);
		}

		switch (slot_group->sync)
		{
			case 0:		// 4 operator FM
			{
				int slot1 = j + (0*12);
				int slot2 = j + (1*12);
				int slot3 = j + (2*12);
				int slot4 = j + (3*12);
				mixp = &mix[0];

				if (chip->slots[slot1].active)
				{
					for (i = 0; i < length; i++)
					{
						INT64 output1 = 0, output2 = 0, output3 = 0, output4 = 0, phase_mod1 = 0, phase_mod2 = 0;
						switch (chip->slots[slot1].algorithm)
						{
							// <--------|
							// +--[S1]--+--[S3]--+--[S2]--+--[S4]-->
							case 0:
								phase_mod1 = calculate_2op_fm_0(chip, slot1, slot3);
								phase_mod2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							// <-----------------|
							// +--[S1]--+--[S3]--+--[S2]--+--[S4]-->
							case 1:
								phase_mod1 = calculate_2op_fm_1(chip, slot1, slot3);
								phase_mod2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							// <--------|
							// +--[S1]--|
							// ---[S3]--+--[S2]--+--[S4]-->
							case 2:
								phase_mod1 = (calculate_1op_fm_1(chip, slot1) + calculate_1op_fm_0(chip, slot3, 0)) / 2;
								phase_mod2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							//          <--------|
							//          +--[S1]--|
							// ---[S3]--+--[S2]--+--[S4]-->
							case 3:
								phase_mod1 = calculate_1op_fm_0(chip, slot3, 0);
								phase_mod2 = (calculate_1op_fm_0(chip, slot2, phase_mod1) + calculate_1op_fm_1(chip, slot1)) / 2;
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							// <--------|  --[S2]--|
							// ---[S1]--|-+--[S3]--+--[S4]-->
							case 4:
								phase_mod1 = (calculate_2op_fm_0(chip, slot1, slot3) + calculate_1op_fm_0(chip, slot2, 0)) / 2;
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								break;

							//           --[S2]-----|
							// <-----------------|  |
							// ---[S1]--+--[S3]--|--+--[S4]-->
							case 5:
								phase_mod1 = (calculate_2op_fm_1(chip, slot1, slot3) + calculate_1op_fm_0(chip, slot2, 0)) / 2;
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								break;

							// ---[S2]-----+--[S4]--|
							//                      |
							// <--------|           |
							// +--[S1]--|--+--[S3]--+-->
							case 6:
								output3 = calculate_2op_fm_0(chip, slot1, slot3);
								phase_mod1 = calculate_1op_fm_0(chip, slot2, 0);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								break;

							// ---[S2]--+--[S4]-----|
							//                      |
							// <-----------------|  |
							// +--[S1]--+--[S3]--|--+-->
							case 7:
								output3 = calculate_2op_fm_1(chip, slot1, slot3);
								phase_mod1 = calculate_1op_fm_0(chip, slot2, 0);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								break;

							// ---[S3]--+--[S2]--+--[S4]--|
							//                            |
							// <--------|                 |
							// +--[S1]--|-----------------+-->
							case 8:
								output1 = calculate_1op_fm_1(chip, slot1);
								phase_mod1 = calculate_1op_fm_0(chip, slot3, 0);
								phase_mod2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							//         <--------|
							// -----------[S1]--|
							//                  |
							// --[S3]--|        |
							// --[S2]--+--[S4]--+-->
							case 9:
								phase_mod1 = (calculate_1op_fm_0(chip, slot2, 0) + calculate_1op_fm_0(chip, slot3, 0)) / 2;
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								output1 = calculate_1op_fm_1(chip, slot1);
								break;

							//           --[S4]--|
							//           --[S2]--+
							// <--------|        |
							// +--[S1]--+--[S3]--+-->
							case 10:
								output3 = calculate_2op_fm_0(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								output4 = calculate_1op_fm_0(chip, slot4, 0);
								break;

							//           --[S4]-----|
							//           --[S2]-----+
							// <-----------------|  |
							// +--[S1]--+--[S3]--|--+-->
							case 11:
								output3 = calculate_2op_fm_1(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								output4 = calculate_1op_fm_0(chip, slot4, 0);
								break;

							//            |--+--[S4]--+
							// <--------| |--+--[S3]--+
							// +--[S1]--+-|--+--[S2]--+-->
							case 12:
								phase_mod1 = calculate_1op_fm_1(chip, slot1);
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output3 = calculate_1op_fm_0(chip, slot3, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod1);
								break;

							// ---[S3]--+--[S2]--+
							//                   |
							// ---[S4]-----------+
							// <--------|        |
							// +--[S1]--|--------+-->
							case 13:
								output1 = calculate_1op_fm_1(chip, slot1);
								phase_mod1 = calculate_1op_fm_0(chip, slot3, 0);
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod1);
								output4 = calculate_1op_fm_0(chip, slot4, 0);
								break;

							// ---[S2]----+--[S4]--+
							//                     |
							// <--------| +--[S3]--|
							// +--[S1]--+-|--------+-->
							case 14:
								output1 = calculate_1op_fm_1(chip, slot1);
								phase_mod1 = output1;
								output3 = calculate_1op_fm_0(chip, slot3, phase_mod1);
								phase_mod2 = calculate_1op_fm_0(chip, slot2, 0);
								output4 = calculate_1op_fm_0(chip, slot4, phase_mod2);
								break;

							//  --[S4]-----+
							//  --[S2]-----+
							//  --[S3]-----+
							// <--------|  |
							// +--[S1]--|--+-->
							case 15:
								output1 = calculate_1op_fm_1(chip, slot1);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								output3 = calculate_1op_fm_0(chip, slot3, 0);
								output4 = calculate_1op_fm_0(chip, slot4, 0);
								break;
						}

						*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch0_level]) +
									(output2 * channel_attenuation[chip->slots[slot2].ch0_level]) +
									(output3 * channel_attenuation[chip->slots[slot3].ch0_level]) +
									(output4 * channel_attenuation[chip->slots[slot4].ch0_level])) >> 16;
						*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch1_level]) +
									(output2 * channel_attenuation[chip->slots[slot2].ch1_level]) +
									(output3 * channel_attenuation[chip->slots[slot3].ch1_level]) +
									(output4 * channel_attenuation[chip->slots[slot4].ch1_level])) >> 16;
					}
				}
				break;
			}

			case 1:		// 2x 2 operator FM
			{
				for (op = 0; op < 2; op++)
				{
					int slot1 = j + ((op + 0) * 12);
					int slot2 = j + ((op + 2) * 12);

					mixp = &mix[0];
					if (chip->slots[slot1].active)
					{
						for (i = 0; i < length; i++)
						{
							INT64 output1 = 0, output2 = 0, phase_mod = 0;
							switch (chip->slots[slot1].algorithm & 3)
							{
								// <--------|
								// +--[S1]--+--[S3]-->
								case 0:
									output2 = calculate_2op_fm_0(chip, slot1, slot2);
									break;

								// <-----------------|
								// +--[S1]--+--[S3]--|-->
								case 1:
									output2 = calculate_2op_fm_1(chip, slot1, slot2);
									break;

								// ---[S3]-----|
								// <--------|  |
								// +--[S1]--|--+-->
								case 2:
									output1 = calculate_1op_fm_1(chip, slot1);
									output2 = calculate_1op_fm_0(chip, slot2, 0);
									break;
								//
								// <--------| +--[S3]--|
								// +--[S1]--|-|--------+-->
								case 3:
									output1 = calculate_1op_fm_1(chip, slot1);
									phase_mod = output1;
									output2 = calculate_1op_fm_0(chip, slot2, phase_mod);
									break;
							}

							*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch0_level]) +
										(output2 * channel_attenuation[chip->slots[slot2].ch0_level])) >> 16;
							*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch1_level]) +
										(output2 * channel_attenuation[chip->slots[slot2].ch1_level])) >> 16;
						}
					}
				}
				break;
			}

			case 2:		// 3 operator FM + PCM
			{
				int slot1 = j + (0*12);
				int slot2 = j + (1*12);
				int slot3 = j + (2*12);
				mixp = &mix[0];

				if (chip->slots[slot1].active)
				{
					for (i = 0; i < length; i++)
					{
						INT64 output1 = 0, output2 = 0, output3 = 0, phase_mod = 0;
						switch (chip->slots[slot1].algorithm & 7)
						{
							// <--------|
							// +--[S1]--+--[S3]--+--[S2]-->
							case 0:
								phase_mod = calculate_2op_fm_0(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod);
								break;

							// <-----------------|
							// +--[S1]--+--[S3]--+--[S2]-->
							case 1:
								phase_mod = calculate_2op_fm_1(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod);
								break;

							// ---[S3]-----|
							// <--------|  |
							// +--[S1]--+--+--[S2]-->
							case 2:
								phase_mod = (calculate_1op_fm_1(chip, slot1) + calculate_1op_fm_0(chip, slot3, 0)) / 2;
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod);
								break;

							// ---[S3]--+--[S2]--|
							// <--------|        |
							// +--[S1]--|--------+-->
							case 3:
								phase_mod = calculate_1op_fm_0(chip, slot3, 0);
								output2 = calculate_1op_fm_0(chip, slot2, phase_mod);
								output1 = calculate_1op_fm_1(chip, slot1);
								break;

							// ------------[S2]--|
							// <--------|        |
							// +--[S1]--+--[S3]--+-->
							case 4:
								output3 = calculate_2op_fm_0(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								break;

							// ------------[S2]--|
							// <-----------------|
							// +--[S1]--+--[S3]--+-->
							case 5:
								output3 = calculate_2op_fm_1(chip, slot1, slot3);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								break;

							// ---[S2]-----|
							// ---[S3]-----+
							// <--------|  |
							// +--[S1]--+--+-->
							case 6:
								output1 = calculate_1op_fm_1(chip, slot1);
								output3 = calculate_1op_fm_0(chip, slot3, 0);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								break;

							// --------------[S2]--+
							// <--------| +--[S3]--|
							// +--[S1]--+-|--------+-->
							case 7:
								output1 = calculate_1op_fm_1(chip, slot1);
								phase_mod = output1;
								output3 = calculate_1op_fm_0(chip, slot3, phase_mod);
								output2 = calculate_1op_fm_0(chip, slot2, 0);
								break;
						}

						*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch0_level]) +
									(output2 * channel_attenuation[chip->slots[slot2].ch0_level]) +
									(output3 * channel_attenuation[chip->slots[slot3].ch0_level])) >> 16;
						*mixp++ += ((output1 * channel_attenuation[chip->slots[slot1].ch1_level]) +
									(output2 * channel_attenuation[chip->slots[slot2].ch1_level]) +
									(output3 * channel_attenuation[chip->slots[slot3].ch1_level])) >> 16;
					}
				}

				update_pcm(chip, j + (3*12), mixp, length);
				break;
			}

			case 3:		// PCM
			{
				update_pcm(chip, j + (0*12), mixp, length);
				update_pcm(chip, j + (1*12), mixp, length);
				update_pcm(chip, j + (2*12), mixp, length);
				update_pcm(chip, j + (3*12), mixp, length);
				break;
			}

			default: break;
		}
	}

	mixp = &mix[0];
	for (i = 0; i < length; i++)
	{
		outputs[0][i] = (*mixp++)>>2;
		outputs[1][i] = (*mixp++)>>2;
	}
}

static void write_register(YMF271Chip *chip, int slotnum, int reg, int data)
{
	YMF271Slot *slot = &chip->slots[slotnum];

	switch (reg)
	{
		case 0:
		{
			slot->extout = (data>>3)&0xf;

			if (data & 1)
			{
				// key on
				slot->step = 0;
				slot->stepptr = 0;

				slot->active = 1;

				calculate_step(slot);
				init_envelope(slot);
				init_lfo(slot);
				slot->feedback_modulation0 = 0;
				slot->feedback_modulation1 = 0;
			}
			else
			{
				if (slot->active)
				{
					//slot->active = 0;
					slot->env_state = ENV_RELEASE;
				}
			}
			break;
		}

		case 1:
		{
			slot->lfoFreq = data;
			break;
		}

		case 2:
		{
			slot->lfowave = data & 3;
			slot->pms = (data >> 3) & 0x7;
			slot->ams = (data >> 6) & 0x3;
			break;
		}

		case 3:
		{
			slot->multiple = data & 0xf;
			slot->detune = (data >> 4) & 0x7;
			break;
		}

		case 4:
		{
			slot->tl = data & 0x7f;
			break;
		}

		case 5:
		{
			slot->ar = data & 0x1f;
			slot->keyscale = (data>>5)&0x7;
			break;
		}

		case 6:
		{
			slot->decay1rate = data & 0x1f;
			break;
		}

		case 7:
		{
			slot->decay2rate = data & 0x1f;
			break;
		}

		case 8:
		{
			slot->relrate = data & 0xf;
			slot->decay1lvl = (data >> 4) & 0xf;
			break;
		}

		case 9:
		{
			slot->fns &= ~0xff;
			slot->fns |= data;

			calculate_step(slot);
			break;
		}

		case 10:
		{
			slot->fns &= ~0xff00;
			slot->fns |= (data & 0xf)<<8;
			slot->block = (data>>4)&0xf;
			break;
		}

		case 11:
		{
			slot->waveform = data & 0x7;
			slot->feedback = (data >> 4) & 0x7;
			slot->accon = (data & 0x80) ? 1 : 0;
			break;
		}

		case 12:
		{
			slot->algorithm = data & 0xf;
			break;
		}

		case 13:
		{
			slot->ch0_level = data >> 4;
			slot->ch1_level = data & 0xf;
			break;
		}

		case 14:
		{
			slot->ch2_level = data >> 4;
			slot->ch3_level = data & 0xf;
			break;
		}
	}
}

static void ymf271_write_fm(YMF271Chip *chip, int grp, int adr, int data)
{
	int reg;
	int slotnum;
	int slot_group;
	int sync_mode, sync_reg;
	YMF271Slot *slot;

	slotnum = 12*grp;
	slotnum += fm_tab[adr & 0xf];
	slot = &chip->slots[slotnum];
	slot_group = fm_tab[adr & 0xf];

	reg = (adr >> 4) & 0xf;

	// check if the register is a synchronized register
	sync_reg = 0;
	switch (reg)
	{
		case 0:
		case 9:
		case 10:
		case 12:
		case 13:
		case 14:
			sync_reg = 1;
			break;

		default:
			break;
	}

	// check if the slot is key on slot for synchronizing
	sync_mode = 0;
	switch (chip->groups[slot_group].sync)
	{
		case 0:		// 4 slot mode
		{
			if (grp == 0)
				sync_mode = 1;
			break;
		}
		case 1:		// 2x 2 slot mode
		{
			if (grp == 0 || grp == 1)
				sync_mode = 1;
			break;
		}
		case 2:		// 3 slot + 1 slot mode
		{
			if (grp == 0)
				sync_mode = 1;
			break;
		}

		default:
			break;
	}

	if (sync_mode && sync_reg)		// key-on slot & synced register
	{
		switch (chip->groups[slot_group].sync)
		{
			case 0:		// 4 slot mode
			{
				write_register(chip, (12 * 0) + slot_group, reg, data);
				write_register(chip, (12 * 1) + slot_group, reg, data);
				write_register(chip, (12 * 2) + slot_group, reg, data);
				write_register(chip, (12 * 3) + slot_group, reg, data);
				break;
			}
			case 1:		// 2x 2 slot mode
			{
				if (grp == 0)		// Slot 1 - Slot 3
				{
					write_register(chip, (12 * 0) + slot_group, reg, data);
					write_register(chip, (12 * 2) + slot_group, reg, data);
				}
				else				// Slot 2 - Slot 4
				{
					write_register(chip, (12 * 1) + slot_group, reg, data);
					write_register(chip, (12 * 3) + slot_group, reg, data);
				}
				break;
			}
			case 2:		// 3 slot + 1 slot mode
			{
				// 1 slot is handled normally
				write_register(chip, (12 * 0) + slot_group, reg, data);
				write_register(chip, (12 * 1) + slot_group, reg, data);
				write_register(chip, (12 * 2) + slot_group, reg, data);
				break;
			}
			default:
				break;
		}
	}
	else		// write register normally
	{
		write_register(chip, (12 * grp) + slot_group, reg, data);
	}
}

static void ymf271_write_pcm(YMF271Chip *chip, int data)
{
	int slotnum;
	YMF271Slot *slot;

	slotnum = pcm_tab[chip->pcmreg&0xf];
	slot = &chip->slots[slotnum];

	switch ((chip->pcmreg>>4)&0xf)
	{
		case 0:
			slot->startaddr &= ~0xff;
			slot->startaddr |= data;
			break;
		case 1:
			slot->startaddr &= ~0xff00;
			slot->startaddr |= data<<8;
			break;
		case 2:
			slot->startaddr &= ~0xff0000;
			slot->startaddr |= data<<16;
			break;
		case 3:
			slot->endaddr &= ~0xff;
			slot->endaddr |= data;
			break;
		case 4:
			slot->endaddr &= ~0xff00;
			slot->endaddr |= data<<8;
			break;
		case 5:
			slot->endaddr &= ~0xff0000;
			slot->endaddr |= data<<16;
			break;
		case 6:
			slot->loopaddr &= ~0xff;
			slot->loopaddr |= data;
			break;
		case 7:
			slot->loopaddr &= ~0xff00;
			slot->loopaddr |= data<<8;
			break;
		case 8:
			slot->loopaddr &= ~0xff0000;
			slot->loopaddr |= data<<16;
			break;
		case 9:
			slot->fs = data & 0x3;
			slot->bits = (data & 0x4) ? 12 : 8;
			slot->srcnote = (data >> 3) & 0x3;
			slot->srcb = (data >> 5) & 0x7;
			break;
	}
}

static void ymf271_timer_a_tick(void *param)
{
	YMF271Chip *chip = param;

	chip->status |= 1;

	if (chip->enable & 4)
	{
		chip->irqstate |= 1;
		if (chip->irq_callback) chip->irq_callback(1);
	}
}

static void ymf271_timer_b_tick(void *param)
{
	YMF271Chip *chip = param;

	chip->status |= 2;

	if (chip->enable & 8)
	{
		chip->irqstate |= 2;
		if (chip->irq_callback) chip->irq_callback(1);
	}
}

static UINT8 ymf271_read_ext_memory(YMF271Chip *chip, UINT32 address)
{
	if( chip->ext_mem_read ) {
		return chip->ext_mem_read(address);
	} else {
		if( address < 0x800000)
			return chip->rom[address];
	}
	return 0xff;
}

static void ymf271_write_ext_memory(YMF271Chip *chip, UINT32 address, UINT8 data)
{
	if( chip->ext_mem_write ) {
		chip->ext_mem_write(address, data);
	}
}

static void ymf271_write_timer(YMF271Chip *chip, int data)
{
	int slotnum;
	YMF271Group *group;
	mame_time period;

	slotnum = fm_tab[chip->timerreg & 0xf];
	group = &chip->groups[slotnum];

	if ((chip->timerreg & 0xf0) == 0)
	{
		group->sync = data & 0x3;
		group->pfm = data >> 7;
	}
	else
	{
		switch (chip->timerreg)
		{
			case 0x10:
				chip->timerA &= ~0xff;
				chip->timerA |= data;
				break;

			case 0x11:
				if (!(data & 0xfc))
				{
					chip->timerA &= 0x00ff;
					if ((data & 0x3) != 0x3)
					{
						chip->timerA |= (data & 0xff)<<8;
					}
				}
				break;

			case 0x12:
				chip->timerB = data;
				break;

			case 0x13:
				if (data & 1)
				{	// timer A load
					chip->timerAVal = chip->timerA;
				}
				if (data & 2)
				{	// timer B load
					chip->timerBVal = chip->timerB;
				}
				if (data & 4)
				{
					// timer A IRQ enable
					chip->enable |= 4;
				}
				if (data & 8)
				{
					// timer B IRQ enable
					chip->enable |= 8;
				}
				if (data & 0x10)
				{	// timer A reset
					chip->irqstate &= ~1;
					chip->status &= ~1;

					if (chip->irq_callback) chip->irq_callback(0);

					//period = (double)(256.0 - chip->timerAVal ) * ( 384.0 * 4.0 / (double)CLOCK);
					period = scale_up_mame_time(MAME_TIME_IN_HZ(chip->clock), 384 * (1024 - chip->timerAVal));

					mame_timer_adjust_ptr(chip->timA, period, period);
				}
				if (data & 0x20)
				{	// timer B reset
					chip->irqstate &= ~2;
					chip->status &= ~2;

					if (chip->irq_callback) chip->irq_callback(0);

					period = scale_up_mame_time(MAME_TIME_IN_HZ(chip->clock), 384 * 16 * (256 - chip->timerBVal));

					mame_timer_adjust_ptr(chip->timB, period, period);
				}

				break;

			case 0x14:
				chip->ext_address &= ~0xff;
				chip->ext_address |= data;
				break;
			case 0x15:
				chip->ext_address &= ~0xff00;
				chip->ext_address |= data << 8;
				break;
			case 0x16:
				chip->ext_address &= ~0xff0000;
				chip->ext_address |= (data & 0x7f) << 16;
				chip->ext_read = (data & 0x80) ? 1 : 0;
				if( !chip->ext_read )
					chip->ext_address = (chip->ext_address + 1) & 0x7fffff;
				break;
			case 0x17:
				ymf271_write_ext_memory( chip, chip->ext_address, data );
				chip->ext_address = (chip->ext_address + 1) & 0x7fffff;
				break;
		}
	}
}

static void ymf271_w(int chipnum, int offset, int data)
{
	YMF271Chip *chip = sndti_token(SOUND_YMF271, chipnum);

	switch (offset)
	{
		case 0:
			chip->reg0 = data;
			break;
		case 1:
			ymf271_write_fm(chip, 0, chip->reg0, data);
			break;
		case 2:
			chip->reg1 = data;
			break;
		case 3:
			ymf271_write_fm(chip, 1, chip->reg1, data);
			break;
		case 4:
			chip->reg2 = data;
			break;
		case 5:
			ymf271_write_fm(chip, 2, chip->reg2, data);
			break;
		case 6:
			chip->reg3 = data;
			break;
		case 7:
			ymf271_write_fm(chip, 3, chip->reg3, data);
			break;
		case 8:
			chip->pcmreg = data;
			break;
		case 9:
			ymf271_write_pcm(chip, data);
			break;
		case 0xc:
			chip->timerreg = data;
			break;
		case 0xd:
			ymf271_write_timer(chip, data);
			break;
	}
}

static int ymf271_r(int chipnum, int offset)
{
	UINT8 value;
	YMF271Chip *chip = sndti_token(SOUND_YMF271, chipnum);

	switch(offset)
	{
		case 0:
			return chip->status;

		case 2:
			value = ymf271_read_ext_memory( chip, chip->ext_address );
			chip->ext_address = (chip->ext_address + 1) & 0x7fffff;
			return value;
	}

	return 0;
}

static void init_tables(void)
{
	int i,j;

	for (i=0; i < ARRAY_LENGTH(wavetable); i++)
	{
		wavetable[i] = auto_malloc(SIN_LEN * sizeof(INT16));
	}

	for (i=0; i < SIN_LEN; i++)
	{
		double m = sin( ((i*2)+1) * M_PI / SIN_LEN );
		double m2 = sin( ((i*4)+1) * M_PI / SIN_LEN );

		// Waveform 0: sin(wt)    (0 <= wt <= 2PI)
		wavetable[0][i] = (INT16)(m * MAXOUT);

		// Waveform 1: sin?(wt)   (0 <= wt <= PI)     -sin?(wt)  (PI <= wt <= 2PI)
		wavetable[1][i] = (i < (SIN_LEN/2)) ? (INT16)((m * m) * MAXOUT) : (INT16)((m * m) * MINOUT);

		// Waveform 2: sin(wt)    (0 <= wt <= PI)     -sin(wt)   (PI <= wt <= 2PI)
		wavetable[2][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : (INT16)(-m * MAXOUT);

		// Waveform 3: sin(wt)    (0 <= wt <= PI)     0
		wavetable[3][i] = (i < (SIN_LEN/2)) ? (INT16)(m * MAXOUT) : 0;

		// Waveform 4: sin(2wt)   (0 <= wt <= PI)     0
		wavetable[4][i] = (i < (SIN_LEN/2)) ? (INT16)(m2 * MAXOUT) : 0;

		// Waveform 5: |sin(2wt)| (0 <= wt <= PI)     0
		wavetable[5][i] = (i < (SIN_LEN/2)) ? (INT16)(fabs(m2) * MAXOUT) : 0;

		// Waveform 6:     1      (0 <= wt <= 2PI)
		wavetable[6][i] = (INT16)(1 * MAXOUT);

		wavetable[7][i] = 0;
	}

	for (i=0; i < LFO_LENGTH; i++)
	{
		int tri_wave;
		double ftri_wave, fsaw_wave;
		double plfo[4];

		// LFO phase modulation
		plfo[0] = 0;

		fsaw_wave = ((i % (LFO_LENGTH/2)) * PLFO_MAX) / (double)((LFO_LENGTH/2)-1);
		plfo[1] = (i < (LFO_LENGTH/2)) ? fsaw_wave : fsaw_wave - PLFO_MAX;

		plfo[2] = (i < (LFO_LENGTH/2)) ? PLFO_MAX : PLFO_MIN;

		ftri_wave = ((i % (LFO_LENGTH/4)) * PLFO_MAX) / (double)(LFO_LENGTH/4);
		switch (i / (LFO_LENGTH/4))
		{
			case 0: plfo[3] = ftri_wave; break;
			case 1: plfo[3] = PLFO_MAX - ftri_wave; break;
			case 2: plfo[3] = 0 - ftri_wave; break;
			case 3: plfo[3] = 0 - (PLFO_MAX - ftri_wave); break;
		}

		for (j=0; j < 4; j++)
		{
			plfo_table[j][0][i] = pow(2.0, 0.0);
			plfo_table[j][1][i] = pow(2.0, (3.378 * plfo[j]) / 1200.0);
			plfo_table[j][2][i] = pow(2.0, (5.0646 * plfo[j]) / 1200.0);
			plfo_table[j][3][i] = pow(2.0, (6.7495 * plfo[j]) / 1200.0);
			plfo_table[j][4][i] = pow(2.0, (10.1143 * plfo[j]) / 1200.0);
			plfo_table[j][5][i] = pow(2.0, (20.1699 * plfo[j]) / 1200.0);
			plfo_table[j][6][i] = pow(2.0, (40.1076 * plfo[j]) / 1200.0);
			plfo_table[j][7][i] = pow(2.0, (79.307 * plfo[j]) / 1200.0);
		}

		// LFO amplitude modulation
		alfo_table[0][i] = 0;

		alfo_table[1][i] = ALFO_MAX - ((i * ALFO_MAX) / LFO_LENGTH);

		alfo_table[2][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX : ALFO_MIN;

		tri_wave = ((i % (LFO_LENGTH/2)) * ALFO_MAX) / (LFO_LENGTH/2);
		alfo_table[3][i] = (i < (LFO_LENGTH/2)) ? ALFO_MAX-tri_wave : tri_wave;
	}

	mix = auto_malloc(48000*2*sizeof(*mix));
}

static void init_state(YMF271Chip *chip)
{
	int i, instance;

	for (i = 0; i < sizeof(chip->slots) / sizeof(chip->slots[0]); i++)
	{
		instance = chip->index * (sizeof(chip->slots) / sizeof(chip->slots[0])) + i;

		state_save_register_item("ymf271", instance, chip->slots[i].extout);
		state_save_register_item("ymf271", instance, chip->slots[i].lfoFreq);
		state_save_register_item("ymf271", instance, chip->slots[i].pms);
		state_save_register_item("ymf271", instance, chip->slots[i].ams);
		state_save_register_item("ymf271", instance, chip->slots[i].detune);
		state_save_register_item("ymf271", instance, chip->slots[i].multiple);
		state_save_register_item("ymf271", instance, chip->slots[i].tl);
		state_save_register_item("ymf271", instance, chip->slots[i].keyscale);
		state_save_register_item("ymf271", instance, chip->slots[i].ar);
		state_save_register_item("ymf271", instance, chip->slots[i].decay1rate);
		state_save_register_item("ymf271", instance, chip->slots[i].decay2rate);
		state_save_register_item("ymf271", instance, chip->slots[i].decay1lvl);
		state_save_register_item("ymf271", instance, chip->slots[i].relrate);
		state_save_register_item("ymf271", instance, chip->slots[i].fns);
		state_save_register_item("ymf271", instance, chip->slots[i].block);
		state_save_register_item("ymf271", instance, chip->slots[i].feedback);
		state_save_register_item("ymf271", instance, chip->slots[i].waveform);
		state_save_register_item("ymf271", instance, chip->slots[i].accon);
		state_save_register_item("ymf271", instance, chip->slots[i].algorithm);
		state_save_register_item("ymf271", instance, chip->slots[i].ch0_level);
		state_save_register_item("ymf271", instance, chip->slots[i].ch1_level);
		state_save_register_item("ymf271", instance, chip->slots[i].ch2_level);
		state_save_register_item("ymf271", instance, chip->slots[i].ch3_level);
		state_save_register_item("ymf271", instance, chip->slots[i].startaddr);
		state_save_register_item("ymf271", instance, chip->slots[i].loopaddr);
		state_save_register_item("ymf271", instance, chip->slots[i].endaddr);
		state_save_register_item("ymf271", instance, chip->slots[i].fs);
		state_save_register_item("ymf271", instance, chip->slots[i].srcnote);
		state_save_register_item("ymf271", instance, chip->slots[i].srcb);
		state_save_register_item("ymf271", instance, chip->slots[i].step);
		state_save_register_item("ymf271", instance, chip->slots[i].stepptr);
		state_save_register_item("ymf271", instance, chip->slots[i].active);
		state_save_register_item("ymf271", instance, chip->slots[i].bits);
		state_save_register_item("ymf271", instance, chip->slots[i].volume);
		state_save_register_item("ymf271", instance, chip->slots[i].env_state);
		state_save_register_item("ymf271", instance, chip->slots[i].env_attack_step);
		state_save_register_item("ymf271", instance, chip->slots[i].env_decay1_step);
		state_save_register_item("ymf271", instance, chip->slots[i].env_decay2_step);
		state_save_register_item("ymf271", instance, chip->slots[i].env_release_step);
		state_save_register_item("ymf271", instance, chip->slots[i].feedback_modulation0);
		state_save_register_item("ymf271", instance, chip->slots[i].feedback_modulation1);
		state_save_register_item("ymf271", instance, chip->slots[i].lfo_phase);
		state_save_register_item("ymf271", instance, chip->slots[i].lfo_step);
		state_save_register_item("ymf271", instance, chip->slots[i].lfo_amplitude);
	}

	for (i = 0; i < sizeof(chip->groups) / sizeof(chip->groups[0]); i++)
	{
		instance = chip->index * (sizeof(chip->groups) / sizeof(chip->groups[0])) + i;
		state_save_register_item("ymf271", instance, chip->groups[i].sync);
		state_save_register_item("ymf271", instance, chip->groups[i].pfm);
	}

	state_save_register_item("ymf271", chip->index, chip->timerA);
	state_save_register_item("ymf271", chip->index, chip->timerB);
	state_save_register_item("ymf271", chip->index, chip->timerAVal);
	state_save_register_item("ymf271", chip->index, chip->timerBVal);
	state_save_register_item("ymf271", chip->index, chip->irqstate);
	state_save_register_item("ymf271", chip->index, chip->status);
	state_save_register_item("ymf271", chip->index, chip->enable);
	state_save_register_item("ymf271", chip->index, chip->reg0);
	state_save_register_item("ymf271", chip->index, chip->reg1);
	state_save_register_item("ymf271", chip->index, chip->reg2);
	state_save_register_item("ymf271", chip->index, chip->reg3);
	state_save_register_item("ymf271", chip->index, chip->pcmreg);
	state_save_register_item("ymf271", chip->index, chip->timerreg);
	state_save_register_item("ymf271", chip->index, chip->ext_address);
	state_save_register_item("ymf271", chip->index, chip->ext_read);
}

static void ymf271_init(YMF271Chip *chip, UINT8 *rom, void (*cb)(int), read8_handler ext_read, write8_handler ext_write)
{
	chip->timA = mame_timer_alloc_ptr(ymf271_timer_a_tick, chip);
	chip->timB = mame_timer_alloc_ptr(ymf271_timer_b_tick, chip);

	chip->rom = rom;
	chip->irq_callback = cb;

	chip->ext_mem_read = ext_read;
	chip->ext_mem_write = ext_write;

	init_tables();
	init_state(chip);
}

static void *ymf271_start(int sndindex, int clock, const void *config)
{
	const struct YMF271interface *intf;
	int i;
	YMF271Chip *chip;

	chip = auto_malloc(sizeof(*chip));
	memset(chip, 0, sizeof(*chip));
	chip->index = sndindex;
	chip->clock = clock;

	intf = config;

	ymf271_init(chip, memory_region(intf->region), intf->irq_callback, intf->ext_read, intf->ext_write);
	chip->stream = stream_create(0, 2, clock/384, chip, ymf271_update);

	for (i = 0; i < 256; i++)
	{
		env_volume_table[i] = (int)(65536.0 / pow(10.0, ((double)i / (256.0 / 96.0)) / 20.0));
	}

	for (i = 0; i < 16; i++)
	{
		channel_attenuation[i] = (int)(65536.0 / pow(10.0, channel_attenuation_table[i] / 20.0));
	}
	for (i = 0; i < 128; i++)
	{
		double db = 0.75 * (double)i;
		total_level[i] = (int)(65536.0 / pow(10.0, db / 20.0));
	}

	return chip;
}

READ8_HANDLER( YMF271_0_r )
{
	return ymf271_r(0, offset);
}

WRITE8_HANDLER( YMF271_0_w )
{
	ymf271_w(0, offset, data);
}

READ8_HANDLER( YMF271_1_r )
{
	return ymf271_r(1, offset);
}

WRITE8_HANDLER( YMF271_1_w )
{
	ymf271_w(1, offset, data);
}

static void ymf271_reset(void *token)
{
	int i;
	YMF271Chip *chip = (YMF271Chip*)token;

	for (i = 0; i < 48; i++)
	{
		chip->slots[i].active = 0;
		chip->slots[i].volume = 0;
	}
}

/**************************************************************************
 * Generic get_info
 **************************************************************************/

static void ymf271_set_info(void *token, UINT32 state, sndinfo *info)
{
	switch (state)
	{
		/* no parameters to set */
	}
}


void ymf271_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 = ymf271_set_info;		break;
		case SNDINFO_PTR_START:							info->start = ymf271_start;				break;
		case SNDINFO_PTR_STOP:							/* Nothing */							break;
		case SNDINFO_PTR_RESET:							info->reset = ymf271_reset;				break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case SNDINFO_STR_NAME:							info->s = "YMF271";						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;
	}
}
