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

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

/****************************************************************************
 *	HESファイル仕様
 ****************************************************************************/

/* * HESHEADERのフィールドは適切にアライメントされています。
 *   (ただし、hes_init()呼び出し側がHESHEADER先頭アドレスの4バイトアライメントを確保していると仮定)
 *
 * * HESCHUNKが複数ある場合、先行するHESCHUNKのdata_sizeが4の倍数でなかった場合に、
 *   後続のHESCHUNKヘッダのフィールドは適切にアライメントされていない可能性があります。
 *   適切にアライメントされていないフィールドを直接読もうとすると、アドレス不整例外が発生します。
 *   アドレス不整例外を防ぐために、HESBYTE/HESHALF/HESWORDマクロを使ってフィールドを読んでください。
 *
 * * 前述のように、HESHEADERのフィールドは適切にアライメントされているので、
 *   HESBYTE/HESHALF/HESWORDマクロを使う必要はないのですが、プログラムの統一のために、
 *   HESHEADERのフィールド読み込みにもHESBYTE/HESHALF/HESWORDマクロを使うことにします。
 *
 * * 実際にはHESCHUNKが複数存在することは稀なようで、手元のHESファイルをいくつか調べた限りでは
 *   全て単一のHESCHUNKしか含んでいません。
 *   おそらくほとんど全部のHESファイルが単一のHESCHUNKしか含んでいないのではないかと思います。
 *   実際には上述のようなアライメントの配慮はしなくてもまず問題ないかも知れないのですけれど、
 *   仕様上は一応複数のHESCHUNKが存在し得るので、対策を採っておくことにしました。
 */
#define HESBYTE(v)	(((unsigned char*)&(v))[0])
#define HESHALF(v)	(((unsigned char*)&(v))[0] | ((unsigned char*)&(v))[1] << 8)
#define HESWORD(v)	(((unsigned char*)&(v))[0] | ((unsigned char*)&(v))[1] << 8 | ((unsigned char*)&(v))[2] << 16 | ((unsigned char*)&(v))[3] << 24)

/* HESファイルヘッダ */
typedef struct _HESHEADER {
	         char  magic_id[4];	/* +0x00,4: magic-id 'HESM'                   */
	unsigned char  version;		/* +0x04,1: VERSION (0)                       */
	unsigned char  start_song;	/* +0x05,1: start song                        */
	unsigned short request_address;	/* +0x06,2: request address (logical address) */
	unsigned char  initial_mpr[8];	/* +0x08,1: initial MPR0 ($FF)                */
					/* +0x09,1: initial MPR1 ($F8)                */
					/* +0x0a,1: initial MPR2                      */
					/* +0x0b,1: initial MPR3                      */
					/* +0x0c,1: initial MPR4                      */
					/* +0x0d,1: initial MPR5                      */
					/* +0x0e,1: initial MPR6                      */
					/* +0x0f,1: initial MPR7 ($00)                */
} HESHEADER;				/* =0x10 */
					/* この後ファイル終端まで、0個以上のHESCHUNKが続きます。 */

/* HESチャンクヘッダ */
typedef struct _HESCHUNK {
	char     sub_id[4];		/* +0x00,4: sub-id 'DATA' */
	unsigned data_size;		/* +0x04,4: data size */
	unsigned load_address;		/* +0x08,4: load address (physical address) */
	unsigned reserved;		/* +0x0c,4: reserved (0) */
} HESCHUNK;				/* =0x10 */
					/* この後に、data_sizeバイトのデータ本体が続きます。 */

/****************************************************************************
 *	HESドライバ
 ****************************************************************************/

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

#define HES_CPU_CLOCK		(NTSC_COLOR_SUBCARRIER * 2)	/* =7.16MHz CPU動作クロック (本当はx1/x2可変ですが、x2固定とします) */
#define HES_PSG_CLOCK		(NTSC_COLOR_SUBCARRIER * 1)	/* =3.58MHz PSG入力クロック */

#define HES_VBLANK_PERIOD	(HES_CPU_CLOCK / 60)		/* VBLANK割り込み間隔[CPUサイクル] */

/*
 *	HESドライバ
 */
typedef struct _HESDRIVER {
	/*{{HuC6280コアのREAD外部関数を使うため、レイアウト固定*/
	HUC6280 huc6280;		/* HuC6280-CPU */
	unsigned char mem[0x10000];	/* メインメモリ */
	/*}}HuC6280コアのREAD外部関数を使うため、レイアウト固定*/
	HUC6280PSG psg;			/* HuC6280-PSG */
	//
	const void* data;		/* hes_init()に渡されたHESデータの先頭アドレス */
	int size;			/* hes_init()に渡されたHESデータのバイト数     */
	//
	int vdc_regsel;			/* VDC書き込みレジスタ選択 */
	int vblank_enable;		/* VBLANK割り込み有効 */
	int vblank_progress;		/* VBLANK進行カウンタ */
	//
	int timer_start;		/* タイマスタート */
	int timer_period;		/* タイマリロード値 */
	int timer_progress;		/* タイマ進行カウンタ */
	//
	int timer_limit;		/* タイマ割り込みリミッターが働いたら1になります。   */
					/* hes_huc6280_out()内のコメントを参照してください。 */
} HESDRIVER;

/*
 *	HuC6280エミュレーション用外部関数
 */
unsigned char hes_huc6280_read(HUC6280* huc6280, unsigned short addr); /* テスト用 */
void hes_huc6280_write(HUC6280* huc6280, unsigned short addr, unsigned char data);
void hes_huc6280_out(HUC6280* huc6280, unsigned short addr, unsigned char data);
void hes_huc6280_mprset(HUC6280* huc6280, int addr/*0〜7*/, unsigned char data);
void hes_huc6280_vdcout(HUC6280* huc6280, int addr/*0〜2*/, unsigned char data);

/*
 *	HESドライバ内部関数
 */
int hes_init(HESDRIVER* hes, const void* data, int size, int i_song);
int hes_mix(HESDRIVER* hes, short wbuff[/*HESBUFLEN*/]);
int hes_stream_callback(short* wbuff, int param);

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

/* HESファイルの再生を開始します。
 * [in]
 *	data		HESデータの先頭アドレス。
 *	size		HESデータのバイト数。
 *	i_song		トラック番号。(0〜)
 *			-1を指定すると、HESファイル既定の開始曲番号となります。
 * [out]
 *	戻り値		再生開始に成功したら、 0を返します。
 *			再生開始できなければ、-1を返します。
 * [note]
 *	* この関数は内部でstream_play()を呼び出しています。
 *	* stream_play()を呼ぶと、サウンド割り込みベクタがフックされます。
 *	  プログラム終了前に必ずhes_stop()を呼んで、ベクタを復元してください。
 */
int hes_play(const void* data, int size, int i_song);

/* HESファイルの再生を停止します。
 * [note]
 *	* 再生中でなければ、この関数は何もしません。
 */
void hes_stop();

/* タイマ割り込みリミッターが働いたかどうかを取得します。
 * [out]
 *	戻り値		通常は、0を返します。
 *			タイマ割り込みリミッターが働いたら、0以外の値を返します。
 * [note]
 *	* 演奏停止中は、0を返します。
 *	* 詳しくは、hes_huc6280_out()内のコメントを参照してください。
 */
int hes_timer_limit();

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

#endif /*__CLIP_HES_H__*/
