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

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

typedef struct _PSIDDRIVER PSIDDRIVER;

/****************************************************************************
 *	PSIDファイル形式
 ****************************************************************************/

/* PSIDファイル内のフィールドは、ビッグエンディアンでアライメントされていません。
 * 任意の非アライメント位置からBYTE/HALF/WORDを取得するマクロです。
 * ※注意!!マクロ引数は★ポインタ★です。
 *         うっかり変数を渡すとポインタにキャストされて警告も出ないので、ご注意ください。
 */
#define PSID_BYTE(p)	(((unsigned char*)(p))[0])
#define PSID_HALF(p)	(((unsigned char*)(p))[0] <<  8 |	\
			 ((unsigned char*)(p))[1])
#define PSID_WORD(p)	(((unsigned char*)(p))[0] << 24 |	\
			 ((unsigned char*)(p))[1] << 16 |	\
			 ((unsigned char*)(p))[2] <<  8 |	\
			 ((unsigned char*)(p))[3])

/*
 *	PSIDファイルヘッダ ("SIDPLAY.htm"参照)
 */
typedef struct _PSIDHEADER {
	unsigned char signature[4];	/* +00, 4 */
	unsigned char version[2];	/* +04, 2 */
	unsigned char data_offset[2];	/* +06, 2 */
	unsigned char load_addr[2];	/* +08, 2 */
	unsigned char init_addr[2];	/* +0a, 2 */
	unsigned char play_addr[2];	/* +0c, 2 */
	unsigned char songs[2];		/* +0e, 1 */
	unsigned char start_song[2];	/* +10, 1 */
	unsigned char speed[4];		/* +12, 4 */
	unsigned char name[32];		/* +16,32 */
	unsigned char author[32];	/* +36,32 */
	unsigned char copyright[32];	/* +56,32 */
	/* ↑version=1:ここまで↑ */
	unsigned char flags[2];		/* +76, 2 */
	unsigned char reserved[4];	/* +78, 4 */
	/* ↑version=2:ここまで↑ */	/* =7c    */
} PSIDHEADER;

/****************************************************************************
 *	拡張SID
 ****************************************************************************/

/* Galway-Noise */
typedef struct _XSIDGN {
	int tones;		/* Number of tones */
	int addr;		/* Byte address (0x0000〜0xffff) */
	int len;		/* Tonelength */
	int vol;		/* Volume of Noise */
	int period1;		/* Period for each value of ToneData */
	int period0;		/* Period for value 0 of ToneData */
	//
	int progress;		/* クロック進行カウンタ */
	int period;		/* 現在のトーンの波形サイズ[Clock] */
	int count;		/* 現在のトーンの残り波形数 */
} XSIDGN;

/* Sample */
typedef struct _XSIDSM {
	int vol;		/* Volume */
	int addr;		/* Nibble address (0x00000〜0x1fffe) */
	int addr_end;		/* Nibble address (0x00002〜0x20000) */
	int addr_rep;		/* Nibble address (0x00000〜0x1fffe) */
	int rep_cnt;		/* 0x00〜0xfe:count/0xff:∞ */
	int period;		/* freq = clock / period */
	int stride;		/* Nibble address stride */
	int order;		/* Nibble order 0:Lo,Hi/1:Hi,Lo */
	//
	int dda_d;		/* サンプルデータアドレスのDDA */
} XSIDSM;

typedef struct _XSID {
	int state;		/* 0(OFF)/XSID_GALWAY_NOISE/XSID_SAMPLE */
	union {
		XSIDGN gn;	/* state=XSID_GALWAY_NOISE */
		XSIDSM sm;	/* state=XSID_SAMPLE       */
	} context;
	//
	unsigned char reg[0x80];/* 拡張SID#1(0xd4xx)のみ対応。#2〜(0xd5xx〜)は未対応です。 */
} XSID;

/* XSID.state */
#define XSID_GALWAY_NOISE	1
#define XSID_SAMPLE		2

/*
 *	拡張SID内部関数
 */
void psid_xsid_write(PSIDDRIVER* psid, unsigned short addr, unsigned char data);
void psid_xsid_galway_noise_start(PSIDDRIVER* psid, unsigned char data);
void psid_xsid_sample_start(PSIDDRIVER* psid, unsigned char data);
int psid_xsid_process(PSIDDRIVER* psid, int step);		/* 出力サンプル=符号付き17.5bit */
int psid_xsid_galway_noise_process(PSIDDRIVER* psid, int step);	/* 出力サンプル=符号付き17.5bit */
int psid_xsid_sample_process(PSIDDRIVER* psid, int step);	/* 出力サンプル=符号付き17.5bit */

/****************************************************************************
 *	PSIDドライバ
 ****************************************************************************/

#define PSIDBUFLEN	SIDBUFLEN	/* 1[tick](=1/50[sec])当たりのサンプル数 */

#define PSID_C64_CLOCK	985248		/* CPU＆SIDクロック[Hz] (共通です) */

/*
 *	PSIDドライバ
 */
/*typedef*/ struct _PSIDDRIVER {
	/*==== Commodore64 ハードウェアに関するもの ====**/
	/*{{M6502コアのREAD外部関数を使うため、レイアウト固定*/
	M6502 m6502;			/* 6502 Core */
	unsigned char mem[0x10000];	/* Main Memory */
	/*}}M6502コアのREAD外部関数を使うため、レイアウト固定*/
	//
	SID sid;			/* SID Chip */
	XSID xsid;			/* PlaySID Extended SID */
	//
	int speed;			/* 0:PAL(1/50[秒])固定 / 1:CIA(1/60[秒])可変 */
	int cia_timer;			/* CIA#1 Timer A start value */
	int cia_count;			/* CIA#1 TImer A count down  */

	/*==== PSIDファイル ソフトウェアに関するもの ====*/
	unsigned short play_addr;	/* Interrupt Handler */
	int progress;			/* SID→CPUクロック変換DDA */
} /*PSIDDRIVER*/;

/*
 *	M6502エミュレーション用外部関数
 */
//unsigned char psid_m6502_read(M6502* m6502, unsigned short addr); /* ※I/O READ 省略 */
void psid_m6502_write(M6502* m6502, unsigned short addr, unsigned char data);
void psid_m6502_out(M6502* m6502, unsigned short addr, unsigned char data);

/*
 *	PSIDドライバ内部関数
 */
void psid_m6502_setup(PSIDDRIVER* psid, int a, int pc);
int psid_init(PSIDDRIVER* psid, const void* data, int len, int i_song);
int psid_mix(PSIDDRIVER* psid, short* wbuff);
int psid_stream_callback(short* wbuff, int param);

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

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

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

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

#endif /*__CLIP_PSID_H__*/
