/*	
 *	clipapu.h
 *
 *	P/ECE APU (RICOH RP2A03) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Sun Feb 06 18:39:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_APU_H__
#define __CLIP_APU_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

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

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

/********** これ以降は書き換えないでください **********/

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

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

#define APUBUFLEN 320 /* = 16000/50 … 1[tick](=1/50[sec])当たりのサンプル数 */

#define APU_CLOCK (NTSC_COLOR_SUBCARRIER / 2) /* APU入力クロック(ファミコン) */

/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */
/* ★アセンブラルーチンから直接参照しているので、フォーマット固定です。★ */
/* ★フォーマットを変更したときは、framapua.sの定義も追従してください。★ */
/* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ */

/* レングスカウンタ (60Hz) */
typedef struct _APULENGTH {
	unsigned char flags;		/* + 0,1: D0: 0 = レングスカウンタ無効 */
					/*            1 = レングスカウンタ有効 */
	unsigned char count;		/* + 1,1: レングスカウンタ (0..127) */
} APULENGTH;				/* = 2 */

/* リニアカウンタ (240Hz) */
typedef struct _APULINEAR {
	unsigned char flags;		/* + 0,1: D0: 0 = リニアカウンタ無効 */
					/*            1 = リニアカウンタ有効 */
	unsigned char count;		/* + 1,1: リニアカウンタ (0..127) */
} APULINEAR;				/* = 2 */

/* エンベロープ (240Hz) */
typedef struct _APUENVELOPE {
	unsigned char flags;		/* + 0,1: D0: 0 = エンベロープ無効 */
					/*            1 = エンベロープ有効 */
					/*        D1: 0 = ループ無し */
					/*            1 = ループ有り */
	unsigned char period;		/* + 1,1: 更新周期 (1..16) */
	unsigned char count;		/* + 2,1: 進行カウンタ (0..16) */
	unsigned char volume;		/* + 3,1: ボリューム (0..15) */
} APUENVELOPE;				/* = 4 */

/* スイープ (120Hz) */
typedef struct _APUSWEEP {
	unsigned char flags;		/* + 0,1: D0: 0 = スイープ無効 */
					/*            1 = スイープ有効 */
					/*        D1: 0 = 周期増加 */
					/*            1 = 周期減少 */
	unsigned char shift;		/* + 1,1: 波長増減量のシフト数 */
	unsigned char period;		/* + 2,1: 更新周期 (1..8) */
	unsigned char count;		/* + 3,1: 進行カウンタ (0..8) */
	short wavelength;		/* + 4,2: 波長 ($000..$800) */
} APUSWEEP;				/* = 6 */

/* 矩形波 (Ch0,Ch1) */
typedef struct _APUSQUARE {
	APULENGTH length;		/* + 0,2: レングスカウンタ */
	APUENVELOPE envelope;		/* + 2,4: エンベロープ */
	APUSWEEP sweep;			/* + 6,6: スイープ */
	unsigned char duty;		/* +12,1: デューティサイクル (2,4,8,12) */
	unsigned char waveindex;	/* +13,1: 波形インデクス (0..15) */
					/* +14,2: (パディング) */
	int wavecount;			/* +16,4: 波形進行カウンタ */
} APUSQUARE;				/* =20 */

/* 三角波 (Ch2) */
typedef struct _APUTRIANGLE {
	APULENGTH length;		/* + 0,2: レングスカウンタ */
	APULINEAR linear;		/* + 2,2: リニアカウンタ */
	short wavelength;		/* + 4,2: 波長 ($000..$7ff) */
	unsigned char waveindex;	/* + 6,1: 波形インデクス (0..31) */
					/* + 7,1: (パディング) */
	int wavecount;			/* + 8,4: 波形進行カウンタ */
} APUTRIANGLE;				/* =12 */

/* ノイズ (Ch3) */
typedef struct _APUNOISE {
	APULENGTH length;		/* + 0,2: レングスカウンタ */
	APUENVELOPE envelope;		/* + 2,4: エンベロープ */
	unsigned char mode;		/* + 6,1: 1 = long mode, 6 = short mode */
					/* + 7,1: (パディング) */
	unsigned short lfsr;		/* + 8,2: LFSR (Linear Feedback Shift Register) */
	short wavelength;		/* +10,2: 波長 ($004..$fe4) */
	int wavecount;			/* +12,4: 波形進行カウンタ */
} APUNOISE;				/* =16 */

/* デルタPCM (Ch4) */
typedef struct _APUDMC {
	unsigned char flags;		/* + 0,1: D0: 0 = ループ無し */
					/*            1 = ループ有り */
					/*        D7: 発声開始時に、apu_write()  がセット(1)します。 */
					/*            発声完了時に、apu_dmc_mix()がクリア(0)します。 */
	char init_output;		/* + 1,1: 出力初期値 (-64..63) */
	unsigned short init_address;	/* + 2,2: サンプルアドレス初期値 ($C000..$FFC0) */
	short init_length;		/* + 4,2: サンプルデータ長初期値 ($001..$FF1) ★出力初期値も1カウント分と見なす★ */
	short wavelength;		/* + 6,2: 波長 ($036..$1ac) */
	//
					/* + 8,1: (パディング) */
	char output;			/* + 9,1: 現在の出力値 (-64..63) */
	unsigned short address;		/* +10,2: 次回サンプルアドレス (init_address->$FFFF->$8000->$FFFF) */
	short length;			/* +12,2: 残りサンプルデータ長 (init_length->$000) */
	unsigned short sample;		/* +14,2: サンプルバッファ */
					/*        サンプル取得時に (0x100|data) をセットします。 */
					/*        最下位ビットを消費するたびに、右シフトします。 */
					/*        (sample<=1) ならば、サンプルバッファが空です。 */
	int wavecount;			/* +16,4: 波形進行カウンタ */
	//
	const unsigned char* memory;	/* +20,4: サンプルデータメモリの先頭アドレス */
} APUDMC;				/* =24 */

typedef struct _APU {
	unsigned char regs[0x20];	/* レジスタ ($4000-$401F) */
	//
	APUSQUARE square[2];		/* Ch1-2: 矩形波 */
	APUTRIANGLE triangle;		/*   Ch3: 三角波 */
	APUNOISE noise;			/*   Ch4: ノイズ */
	APUDMC dmc;			/*   Ch5: デルタPCM */
	//
	unsigned char irq;		/*   D7: デルタPCM IRQ */
					/*   D6: Frame IRQ     */
					/* D5-0: 0             */
	unsigned char fc_250_240;	/* 250Hz->240Hz変換用カウンタ */
	unsigned char framecount;	/* 240Hzカウンタ[4->]3->2->1->0 */
} APU;

extern APU apu;

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

extern const unsigned char apu_square_duty_table[4];
extern const char apu_triangle_waveform_table[32];
extern const short apu_noise_wavelength_table[16];
extern const short apu_dmc_wavelength_table[16];
extern const unsigned char apu_length_count_table[32];

void apu_do_length(APULENGTH* length);
void apu_do_linear(APULINEAR* linear);
void apu_do_envelope(APUENVELOPE* envelope);
void apu_do_sweep(APUSWEEP* sweep);

void apu_square_mix(APUSQUARE* square, short wbuff[/*64*/]);
void apu_triangle_mix(APUTRIANGLE* triangle, short wbuff[/*64*/]);
void apu_noise_mix(APUNOISE* noise, short wbuff[/*64*/]);
void apu_dmc_mix(APUDMC* dmc, short wbuff[/*64*/]);
void apu_volume_shift(short wbuff[/*APUBUFLEN*/]);

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

/* APUコアをリセットします。
 * [in]
 *	memory		サンプルデータメモリの先頭アドレス。
 *			デルタPCMチャネルが、memory[$8000..$FFFF]を参照します。
 *			ファミコンのメインメモリ先頭アドレスを指定してください。
 * [note]
 *	* 入力クロックは、1.79MHzに決め撃ちです。
 *	  アプリケーションから、入力クロックを指定することはできません。
 */
void apu_reset(const void* memory);

/* APUへ1バイト書き込みます。
 * [in]
 *	addr		書き込むアドレス。($4000..$4017)
 *			D4:0以外はマスクするので、0x00..0x17でも構いません。
 *	data		書き込むデータ。
 */
void apu_write(int addr, int data);

/* APUから1バイト読み出します。
 * [in]
 *	addr		読み出すアドレス。($4000..$4017)
 *			D4:0以外はマスクするので、0x00..0x17でも構いません。
 */
int apu_read();

/* 出力サンプリングレート=SPEAKER_OUTPUT[Hz]で、320サンプルのミキシングを行います。
 * [in]
 *	wbuff		出力サンプルバッファ。
 */
void apu_mix(short wbuff[/*APUBUFLEN*/]);

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_APU_H__*/
