/*	
 *	clipn2.h
 *
 *	P/ECE YM2612(OPN2) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Dec 05 03:03:00 JST 2003 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_N2_H__
#define __CLIP_N2_H__

/****************************************************************************
 *
 ****************************************************************************/

/* YM2612_ASMシンボル定義を読み込みます。
 * YM2612_ASMシンボルの未定義/定義によるC/アセンブラコードの切り替えは、
 * Cソースファイル(*.c)とアセンブラソースファイル(*.s)から参照するので、
 * 別のインクルードファイルに分けています。
 * (C言語特有の定義を含むインクルードファイルをそのままアセンブラソースに
 *  インクルードすると、アセンブルエラーになってしまうため。)
 */
#include "clipn2a.h"

/* P/ECE以外ではアセンブラコードは使用できません。
 */
#ifndef PIECE
#undef YM2612_ASM
#endif /*PIECE*/

/****************************************************************************
 *
 ****************************************************************************/

typedef struct _YM2612OP YM2612OP;
typedef struct _YM2612CH YM2612CH;
typedef struct _YM2612   YM2612  ;

extern const short ym2612op_pg_table[256]; /* 符号付き9bit */

/* YM2612OP.env */
typedef int YM2612OP_ENV(YM2612OP* op, int eg_cnt);
int ym2612op_env_attack(YM2612OP* op, int eg_cnt);
int ym2612op_env_decay(YM2612OP* op, int eg_cnt);
int ym2612op_env_sustain(YM2612OP* op, int eg_cnt);
int ym2612op_env_release(YM2612OP* op, int eg_cnt);
int ym2612op_env_silent(YM2612OP* op, int eg_cnt);

struct _YM2612OP {
	/* 発声時に使用するフィールド */
	YM2612OP_ENV* eg_env;		/* + 0, 4: 状態に応じたエンベロープ関数 */
	int eg_cnt;			/* + 4, 4: エンベロープカウンタ(整数部0..127、小数部16bit) */
	int eg_ar;			/* + 8, 4: アタックレート	(整数部0..127、小数部16bit) */
	int eg_tl;			/* +12, 4: トータルレベル	(整数部0..127、小数部16bit) */
	int eg_dr;			/* +16, 4: ディケイレート	(整数部0..127、小数部16bit) */
	int eg_sl;			/* +20, 4: サスティンレベル	(整数部0..127、小数部16bit) */
	int eg_sr;			/* +24, 4: サスティンレート	(整数部0..127、小数部16bit) */
	int eg_rr;			/* +28, 4: リリースレート	(整数部0..127、小数部16bit) */
	int pg_cnt;			/* +32, 4: フェーズカウンタ	(整数部0..255、小数部16bit) */
	int pg_spd;			/* +36, 4: フェーズスピード	(整数部0..255、小数部16bit) */
	/* 発声時に使用しないフィールド (レジスタ設定一時保存用) */
	unsigned char mul;		/* +40, 1: MUL */
	unsigned char tl;		/* +41, 1: TL */
	unsigned char sl;		/* +42, 1: SL */
	unsigned char resv[1];		/* +43, 1: (予約) */
};					/* =44 (重要!! 'add %r12,YM2612OP_SIZE'を遅延スロットに入れるため、63以下必須!!) */

/* YM2612CH.mix */
typedef void YM2612CH_MIX(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix0(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix1(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix2(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix3(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix4(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix5(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix6(YM2612CH* ch, int mixbuf[/*count*/], int count);
void ym2612ch_mix7(YM2612CH* ch, int mixbuf[/*count*/], int count);
extern YM2612CH_MIX* const ym2612ch_mix_table[8];

struct _YM2612CH {
	/* 発声時に使用するフィールド */
	YM2612OP op[4];				/* +  0,44*4: オペレータ1..4 */
						/*            重要!! アセンブラの実装が、YM2612CH先頭にYM2612OP配列があることを想定しています。 */
						/*                   YM2612OP配列の位置をYM2612CH先頭から移動してはいけません!!                 */
	YM2612CH_MIX* mix;			/* +176,   4: アルゴリズムに応じたミキシング関数 */
	int fb_in;				/* +180,   4: フィードバック入力値 (2π相当) */
	unsigned char fb;			/* +184,   1: フィードバック (0:Off,1:1/16π,2:1/8π,3:1/4π,4:1/2π,5:π,6:2π,7:4π) */
	unsigned char lr;			/* +185,   1: パン (bit0=R, bit1=L) */
	/* 発声時に使用しないフィールド (レジスタ設定一時保存用) */
	unsigned char f_number_lsb;		/* +186,   1: F-number[ 7..0] */
	unsigned char f_number_msb;		/* +187,   1: F-number[10..8] */
	unsigned char block;			/* +188,   1: Block */
	unsigned char resv2[3];			/* +189,   3: (予約) */
};						/* =192 */

struct _YM2612 {
	int f_sam;				/* サンプリング周波数 = マスタクロック÷(12×12) */
	int dac;				/* DAC (スケーリングしてから格納します) */
	unsigned char dac_en;			/* DAC en (0=disable, 1=enable) */
	unsigned char ch3_mode;			/* Channel 3 has 4 separate frequency (0=Off, 1=On) */
	unsigned char ch3_f_number_lsb[3];	/* Ch3 F-number[ 7..0] ([0]=Op3, [1]=Op1, [2]=Op2) */
	unsigned char ch3_f_number_msb[3];	/* Ch3 F-number[10..8] ([0]=Op3, [1]=Op1, [2]=Op2) */
	unsigned char ch3_block[3];		/* Ch3 Block           ([0]=Op3, [1]=Op1, [2]=Op2) */
	unsigned char i_reg[2];			/* 書き込むレジスタを選択 ([0]=Part I, [1]=Part II) */
	YM2612CH ch[2][3];			/* チャネル1..6 */
};

short* ym2612_copy(short* wbuff, const int* mixbuf, int count);

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

void ym2612_init(YM2612* ym2612, int f_m);
int ym2612_read(YM2612* ym2612, int addr);
void ym2612_write(YM2612* ym2612, int addr, int data);
void ym2612_mix(YM2612* ym2612, short wbuff[/*count*/], int count);

#define ym2612_write_reg(ym2612, i_part, i_reg, data) do {	\
	ym2612_write((ym2612), (i_part) * 2 + 0, (i_reg));	\
	ym2612_write((ym2612), (i_part) * 2 + 1, ( data));	\
} while(0)

#endif /*__CLIP_N2_H__*/
