/*	
 *	clippce.h
 *
 *	P/ECE専用ルーチンを集めました。
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Wed Apr 24 09:26:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Sun Jul 28 11:16:00 JST 2002 Naoyuki Sawa
 *	- PAD_AB/TRG_ABの定義を追加。
 *	* Thu May  1 05:20:00 JST 2003 Naoyuki Sawa
 *	- APPSYSVERの再定義を追加。
 *	* Mon Nov 24 06:00:00 JST 2003 Naoyuki Sawa
 *	- SPEAKER_FREQUENCYの定義を追加。
 *	* Thu Nov 27 06:00:00 JST 2003 Naoyuki Sawa
 *	- my_unzip()にufi_out引数を追加。
 *	* Mon Mar  8 12:30:00 JST 2004 Naoyuki Sawa
 *	- stream_play()にfram4引数を追加。
 *	* Mon Mar 15 12:30:00 JST 2004 Naoyuki Sawa
 *	- mmap()関数を追加しました。
 *	* Mon Apr 19 20:17:00 JST 2004 Naoyuki Sawa
 *	- LEBYTE()/LEHALF()/LEWORD()/BEBYTE()/BEHALF()/BEWORD()マクロを追加しました。
 *	  各ファイル毎に定義していた同機能のマクロは、いずれこれに置き換える予定です。
 *	* Mon May 17 19:00:00 JST 2004 Naoyuki Sawa
 *	- /usr/PIECE/sysdev/pcekn/pcekn.hより、ENTER_CS/LEAVE_CS等の宣言をコピー。
 *	* Sun Jun 6 05:37:00 JST 2004 Naoyuki Sawa
 *	- /usr/PIECE/sysdev/pcekn/pcekn.hより、SET_SIL0等の宣言をコピー。
 *	* Thu Jun 17 06:44:00 JST 2004 Naoyuki Sawa
 *	- pceHeapRealloc()の宣言を追加しました。
 *	  PieceSystem 1.20 現在の /usr/PIECE/include/piece.h は、pceHeapRealloc()の宣言が抜けています。
 *	* Thu Jun 17 07:27:00 JST 2004 Naoyuki Sawa
 *	  PieceSystem 1.20 現在のpceHeapRealloc()にはバグがあることがわかったので、やっぱり宣言を取りやめました。
 *	* Sun Oct 31 13:19:00 JST 2004 Naoyuki Sawa
 *	- LEBYTE()/LEHALF()/LEWORD()/BEBYTE()/BEHALF()/BEWORD()マクロをclipmisc.hへ移動しました。
 *	* Sun Dec 12 09:59:00 JST 2004 Naoyuki Sawa
 *	- BLOCKSIZE、SPEAKER_FREQUENCYはP/ECE特有の定数ですが、Win32版テストプログラムからも参照するために、
 *	  clippce.hからclipmisc.hへ移動しました。
 *	* Sat Jan 22 20:57:00 JST 2005 Naoyuki Sawa
 *	- PAD_CD/TRG_CDの定義を追加。
 *	* Mon Mar  7 04:09:00 JST 2005 Naoyuki Sawa
 *	- turbo()関数に、FLASHとSRAMの出力ディセーブル時間変更機能を追加しました。
 *	  P/ECE Kernelは、FLASHとSRAMの出力ディセーブル時間を1.5サイクルに設定しますが、0.5サイクルで大丈夫です。
 *	  FLASHの出力ディセーブル時間は、仕様上20ns必要で、0.5サイクル(21ns)で充分です。
 *	  SRAMの出力ディセーブル時間は、仕様上25ns必要で、0.5サイクル(21ns)ではオーバークロックなのですけれど、
 *	  実際のプログラムで試してみた限りでは、動作するようです。(そもそも48MHzだってオーバークロックですし…)
 *	- なお、今回の変更により、turbo()関数の引数のmodeビット割り当てを変更しています。
 *	  これまでのビット割り当ては、
 *		D0: 24/48MHz
 *		D1: SRAM Wait
 *		D2: FLASH Wait
 *	  でしたが、変更後のビット割り当ては、
 *		D0: SRAM Wait
 *		D1: SRAM OutputDisable
 *		D2: FLASH Wait
 *		D3: FLASH OutputDisable
 *		D4: 24/48MHz
 *	  となりました。注意してください。
 *	* Tue Aug 23 18:31:00 JST 2005 Naoyuki Sawa
 *	- get_psr(),set_psr()を追加しました。
 *	- ENABLED,DISABLEDマクロを追加しました。
 *	* Sat Sep 03 21:40:00 JST 2005 Naoyuki Sawa
 *	- mmap()関数をマクロ化しました。
 *	  これまでは、mmap()関数がfile_defrag()を参照していたため、デフラグ機能が不要なアプリケーションにもclipdfrg.oがリンクされていました。
 *	  mmap()関数のマクロ化により、デフラグ機能が不要なアプリケーションにはclipdfrg.oがリンクされなくなり、2KB弱のメモリ節約となりました。
 *	* Sat Sep 10 05:56:00 JST 2005 Naoyuki Sawa
 *	- timer_start()の戻り値を、0〜31 → 1〜32 に変更しました。
 *	* Sat Sep 17 06:09:00 JST 2005 Naoyuki Sawa
 *	- timer_start()に、コールバック引数の指定を追加しました。
 *	- 拡張タイマの同時使用数を、32個→16個に減らしました。
 *	* Fri Sep 23 19:57:00 JST 2005 Naoyuki Sawa
 *	- ユーティリティ：タイムアウト処理を追加しました。
 *	* Sun Sep 25 13:40:00 JST 2005 Naoyuki Sawa
 *	- ユーティリティ：タイムアウト処理に、CONTINUE_TIMEOUT、RESTART_TIMEOUTを追加しました。
 *	* Thu Jul 13 07:19:22 JST 2006 Naoyuki Sawa
 *	- snprintf(),vsnprintf()の宣言を追加しました。(実装はclipprnt.cにあります)
 *	* Tue Dec 05 14:59:42 JST 2006 Naoyuki Sawa
 *	- オブジェクトを単一色で描くための、DRW_MASKフラグを定義しました。
 *	  キャラクタのドロップシャドウや、ダメージ時のフラッシュ等に利用できます。
 *	- render_object()をDRW_MASKに対応しました。
 *	  sprite_draw()もrender_object()を利用しているので、sprite_draw()にもDRW_MASK指定可能です。
 *	* Tue Dec 05 16:05:22 JST 2006 Naoyuki Sawa
 *	- draw_object()は使用頻度が低いので、メモリ節約のために削除しました。
 *	* Sun Dec 10 21:40:13 JST 2006 Naoyuki Sawa
 *	- resource_address()を追加しました。
 *	* Wed Dec 20 18:08:11 JST 2006 Naoyuki Sawa
 *	- sound_play()のdata引数を、(const unsigned char*)→(void*)に変更しました。
 *	  処理内容は変化ありません。
 *	* Thu Mar 01 01:53:43 JST 2007 Naoyuki Sawa
 *	- sound_stat()を追加しました。
 *	* Mon Dec 10 14:36:46 JST 2007 Naoyuki Sawa
 *	- atexit()に複数の関数を登録できるよう変更しました。
 *	* Mon Dec 31 16:36:08 JST 2007 Naoyuki Sawa
 *	- アプリケーションパッドセンシングを追加しました。
 *	* Thu Mar 12 22:15:08 JST 2009 Naoyuki Sawa
 *	- PAD_LR,PAD_UD,PAD_LRUD,PAD_ABCDの定義を追加しました。
 *	  TRG_LR,TRG_UD,TRG_LRUD,TRG_ABCDの定義を追加しました。
 *	* Mon Mar 30 19:21:12 JST 2009 Naoyuki Sawa
 *	- PieceSystem1.20現在のpceHeapRealloc()バグ修正パッチを適用しました。
 *	  アプリケーションからpceHeapRealloc()が正常に利用できるようになったので、pceHeapRealloc()の宣言コメントアウトを解除しました。
 */
#ifndef __CLIP_PCE_H__
#define __CLIP_PCE_H__

/*****************************************************************************
 *	定数・マクロ
 *****************************************************************************/

/* カーネル1.18→1.20 では、piece.h内のAPPSYSVERは1.18のまま変更されませんでした。
 * API仕様に変更がなかったからです。
 * しかし、いくつかのAPIでは、細かなバグフィクスが行われています。
 * 今回、カーネル側でのAPIバグフィクスに合わせて、CLiPライブラリ側での回避コードを削除しました。
 * 従って、カーネル1.20以上が必須です。
 * P/ECE標準ライブラリ内の__version_check()を使うと、カーネル1.18以上を通してしまいます。
 * そこで、カーネル1.20未満を弾く__version_check()を、自前で用意することにしました。
 * P/ECE標準ライブラリのversion_check.cソースを、CLiPライブラリ内に含めてあります。
 * ここで再定義したAPPSYSVERが使われるので、カーネル1.20未満を弾く__version_check()になります。
 * 今後、piece.hのAPPSYSVERが変更されたり、CLiPライブラリで必要なカーネルバージョンが変化したら、
 * APPSYSVERを適切に再定義してください。
 */
#undef  APPSYSVER				/* まずpiece.hでの定義を解除 */
#define APPSYSVER	VERSION(1, 20)		/* Ver 1.20 以上が必要       */

//#define BLOCKSIZE	4096			/* フラッシュメモリのセクタサイズ */
//→2004/12/12 Naoyuki Sawa clipmisc.hへ移動しました。

#define PAD_MASK	0x00ff			/* PAD_LF|RI|UP|DN|A|B|START|SELECT */
#define TRG_MASK	0xff00			/* TRG_LF|RI|UP|DN|A|B|START|SELECT */
#define PAD_AB		(PAD_A  | PAD_B )
#define TRG_AB		(TRG_A  | TRG_B )
#define PAD_CD		(PAD_C  | PAD_D )
#define TRG_CD		(TRG_C  | TRG_D )
#define PAD_ABCD	(PAD_AB | PAD_CD)
#define TRG_ABCD	(TRG_AB | TRG_CD)
#define PAD_LR		(PAD_LF | PAD_RI)
#define TRG_LR		(TRG_LF | TRG_RI)
#define PAD_UD		(PAD_UP | PAD_DN)
#define TRG_UD		(TRG_UP | TRG_DN)
#define PAD_LRUD	(PAD_LR | PAD_UD)
#define TRG_LRUD	(TRG_LR | TRG_UD)

//#define SPEAKER_FREQUENCY	16000		/* P/ECEスピーカー出力周波数[Hz] */
//→2004/12/12 Naoyuki Sawa clipmisc.hへ移動しました。

/* * pceUSBCOMGetStat()の戻り値には、USBCOMのOpen状態を示すフラグも含まれているのですが、
 *   piece.hではこれらのフラグに対するマスクが定義されていない(BIOS1.18現在)ので、定義しておきます。
 * * \usr\PIECE\sysdev\pcekn\usbcom.cの56行目あたりの「mystat」と「pcstat」の定義、および、
 *   \usr\PIECE\tools\isd\pieceif.hの47行目あたりの「pistat」と「mystat」の定義に相当します。
 *   「mystat」はP/ECE側を指す場合(usbcom.c)とPC側を指す場合(pieceif.h)があってわかりづらいので、
 *   より明示的な「pistat」「pcstat」の名前に合わせて、「UCS_PISTAT」「UCS_PCSTAT」としました。
 * * UCS_RXWAIT/ING/DONE、UCS_TXWAIG/ING/DONEに対するマスクもあった方が便利なので、定義しておきます。
 */
#define UCS_RXSTAT	0x000000ff	/* UCS_RXWAIT/UCS_RXING/UCS_RXDONEに対するマスク    */
#define UCS_TXSTAT	0x0000ff00	/* UCS_TXWAIT/UCS_TXING/UCS_TXDONEに対するマスク    */
#define UCS_PISTAT	0x00ff0000	/* 0以外なら、P/ECE側でUSBCOMがOpenされている       */
#define UCS_PCSTAT	0xff000000	/* 0以外なら、P/ECE側でOpen後にPC側からもOpenされた */

#define DI do { DISABLE; bWD_WP_WRWD = 1; bWD_EN_EWD = 0; } while(0) /* 割り込み禁止(NMIも) */
#define EI do { ENABLE ; bWD_WP_WRWD = 1; bWD_EN_EWD = 1; } while(0) /* 割り込み許可(NMIも) */

int  get_psr();				/* %psrレジスタを取得します */
void set_psr(int psr);			/* %psrレジスタを設定します */
#define ENABLED  (get_psr()&0x10)	/* 割り込み許可状態ならば真 */
#define DISABLED (!(ENABLED))		/* 割り込み禁止状態ならば真 */

extern unsigned char _def_vbuff[DISP_X * DISP_Y]; /* 汎用ワークとして利用(pceapi.lib内にあります) */

/* /usr/PIECE/sysdev/pcekn/pcekn.hより宣言のみコピー。
 * 実体はpceapi.libに含まれているので、ユーザーアプリケーションからも利用できます。
 * [使い方]
 *	ENTER_CS; // “__cs__ = PSR; PSR(IE) = 0;”に相当
 *	// 割り込み禁止が必要な処理を行う
 *	LEAVE_CS; // “PSR = __cs__;”に相当
 */
typedef unsigned long CRITICAL_SECTION;
void pceSyncEnterCriticalSection(CRITICAL_SECTION* pcs); /* 割り込み許可状態を保存→割り込み禁止 */
void pceSyncLeaveCriticalSection(CRITICAL_SECTION* pcs); /* 割り込み許可状態を復元 */
#define ENTER_CS do { CRITICAL_SECTION __cs__; pceSyncEnterCriticalSection(&__cs__)
#define LEAVE_CS pceSyncLeaveCriticalSection(&__cs__); } while(0)

/* /usr/PIECE/sysdev/pcekn/pcekn.hより宣言のみコピー。
 * 実体はpceapi.libに含まれているので、ユーザーアプリケーションからも利用できます。
 */
typedef signed long SYNCVAR;
SYNCVAR pceSyncSafetyAddition(SYNCVAR* pval, SYNCVAR delta); /* 割り込み許可状態保存→割り込み禁止→変数値増減→割り込み許可状態復元 */
void pceSyncSafetySetIL(int level); /* 割り込みプライオリティ設定→割り込み許可 */
#define SET_SIL0 pceSyncSafetySetIL(0)
#define SET_SIL1 pceSyncSafetySetIL(1)
#define SET_SIL2 pceSyncSafetySetIL(2)
#define SET_SIL3 pceSyncSafetySetIL(3)
#define SET_SIL4 pceSyncSafetySetIL(4)
#define SET_SIL5 pceSyncSafetySetIL(5)
#define SET_SIL6 pceSyncSafetySetIL(6)
#define SET_SIL7 pceSyncSafetySetIL(7)

///* PieceSystem 1.20 現在の /usr/PIECE/include/piece.h は、pceHeapRealloc()の宣言が抜けているので、ここで宣言します。 */
//int pceHeapRealloc(void* memp, unsigned long size);
//→* Thu Jun 17 07:27:00 JST 2004 Naoyuki Sawa
//→  PieceSystem 1.20 現在のpceHeapRealloc()にはバグがあるので、うっかり使ってしまわないよう、やっぱり宣言しないことにしました。
//→  バグの内容については、clippce.cのrealloc()内コメントを参照してください。
//→* Mon Mar 30 19:21:12 JST 2009 Naoyuki Sawa
//→- PieceSystem1.20現在のpceHeapRealloc()バグ修正パッチを適用しました。
//→  アプリケーションからpceHeapRealloc()が正常に利用できるようになったので、pceHeapRealloc()の宣言コメントアウトを解除しました。
int pceHeapRealloc(void* memp, unsigned long size);

/****************************************************************************
 *	スタックレイアウト
 ****************************************************************************/

/* 先頭のシグネチャは、メモリダンプからstack_infoの位置を探すためのものです。
 * PCのツールdmalloc.exeに、スタック情報表示機能を持たせました。(2004/12/29)
 */

/*{{アセンブラコードから参照しているので、レイアウト固定です*/
typedef struct _STACKINFO {
	char signature[8];	/* + 0,8: シグネチャ"STACKINF"固定 */
	/* システムスタック */
	int* sysstack_top;	/* + 8,4: 先頭アドレス */
	int* sysstack_end;	/* +12,4: 終端アドレス */
	/* ユーザースタック */
	int* usrstack_top;	/* +16,4: 先頭アドレス */
	int* usrstack_end;	/* +20,4: 終端アドレス */
	/* スタックポインタ */
	int* ssp;		/* +24,4: システムスタックポインタ */
	int* usp;		/* +28,4: ユーザースタックポインタ */
} STACKINFO;			/* =32 */
/*}}アセンブラコードから参照しているので、レイアウト固定です*/

/* アプリケーションからは参照のみ。書き換えないでください。 */
extern STACKINFO stack_info;

/*****************************************************************************
 *	P/ECE専用・擬似mainフレームワーク
 *****************************************************************************/

/* アプリケーション定義のエントリポイントです。
 * [out]
 *	戻り値		終了コード。
 * [note]
 *	* アプリケーションを終了するには、二通りの方法があります。
 *	  1. main()が制御を返す。
 *	  2. pceAppReqExit()を呼んだ後、yield()を呼ぶ。
 */
int main();

/* システムに制御を譲ります。
 * [note]
 *	* アプリケーションは、定期的にyield()を呼ばなければいけません。
 */
void yield();

/* プログラム終了時に実行する関数を指定します。
 * [in]
 *	func		関数へのポインタ。
 * [out]
 *	戻り値		正しく登録できたら0を返します。
 *			失敗したら0以外の値を返します。
 *			→デバッグを容易にするために、エラーメッセージを表示して終了するよう変更。
 * [note]
 *	* 現在の実装では、登録できる関数は一つだけです。
 *	  すなわち、atexit()を複数回呼ぶと、二回目以降は失敗します。
 *	  →2007/12/10:atexit()に複数の関数を登録できるよう変更しました。登録数の上限は「4」です。
 */
int atexit(void (*func)());

/* pceAppNotify()が呼ばれたときに実行する関数を指定します。
 * [in]
 *	func		関数へのポインタ。
 * [out]
 *	戻り値		正しく登録できたら0を返します。
 *			失敗したら0以外の値を返します。
 * [note]
 *	* 現在の実装では、登録できる関数は一つだけです。
 *	  すなわち、atnotify()を複数回呼ぶと、二回目以降は失敗します。
 *	* アプリケーション定義のNotify関数がAPPNR_IGNOREを返した場合、
 *	  ライブラリ内のpceAppNotify()にて規定の処理が行われます。
 *	  アプリケーション定義のNotify関数がAPPNR_IGNORE以外の値を返した場合、
 *	  ライブラリ内のpceAppNotify()は何もせず、その値をシステムへ返します。
 */
int atnotify(int (*func)(int type, int param));

/****************************************************************************
 *	24MHz/48Mhzモード切り替え
 ****************************************************************************/

/* 24MHz/48Mhzモード切り替えを行います。
 * [in]
 *	mode[bit0]	0:SRAMアクセスを2WAITに切り替えます。(初期状態)
 *			1:SRAMアクセスを1WAITに切り替えます。
 *	mode[bit1]	0:SRAMの出力ディセーブル時間を1.5サイクルに切り替えます。(初期状態)
 *			1:SRAMの出力ディセーブル時間を0.5サイクルに切り替えます。
 *	mode[bit2]	0:FLASHアクセスを2WAITに切り替えます。(初期状態)
 *			1:FLASHアクセスを1WAITに切り替えます。
 *	mode[bit3]	0:FLASHの出力ディセーブル時間を1.5サイクルに切り替えます。(初期状態)
 *			1:FLASHの出力ディセーブル時間を0.5サイクルに切り替えます。
 *	mode[bit4]	0:24MHzモードに切り替えます。(初期状態)
 *			1:48MHzモードに切り替えます。
 * [out]
 *	戻り値		切り替える前の状態を返します。
 *			既に要求されたモードだった場合は、現在の状態を返します。
 * [note]
 *	* 48MHz動作中に利用できないAPIは、次のとおりです。
 *		pceCPUSetSpeed()
 *		pcePowerEnterStandby()
 *		pceTimerGetPrecisionCount()
 *		pceTimerAdjustPrecisionCount()
 *		pceFileCreate/Delete/WriteSec() ★重要!!★
 *		pceIR系API全て
 *	  これらのAPIを利用する場合は、いったん24MHzに戻してから利用してください。
 *	* CLiPライブラリのスタートアップルーチンは、48MHzへの切り替えを行いません。
 *	  必要に応じて、アプリケーション側で切り替えを行ってください。
 *	* アプリケーション終了時に48MHzになっていた場合、
 *	  CLiPライブラリのクリーンアップルーチンが24MHzに戻してから終了します。
 *	  アプリケーション側で24MHzに戻す必要はありません。
 */
int turbo(int mode);

/****************************************************************************
 *	アプリケーションパッドセンシング
 ****************************************************************************/

/* ボタン情報を取得します。
 * [out]
 *	戻り値		pcePadGet()に互換です。
 * [note]
 *	- pcePadGetProc()とpcePadGet()の両方を併せたような機能を持ちます。
 *	  すなわち、ボタン情報を更新し、ただちにその情報を返します。
 *	  pcePadGet()のような、更新無しで情報を取得する機能は用意していません。
 *	  もし必要ならば、アプリケーション内で変数に保持してください。
 */
int pad_get();

/* ボタンのオートリピート設定を行います。
 * [in]
 *	pad		設定を反映するボタン。(PAD_*の組み合わせ)
 *	delay		リピートが開始するまでの、pad_get()回数。(0 = リピート無し、1 = 最速 〜 255 = 最遅)
 *	interval	リピート中のトリガ間隔の、pad_get()回数。(0 = リピート無し、1 = 最速 〜 255 = 最遅)
 * [note]
 *	- delayに0を指定すると、リピート無しとなります。
 *	- delayに0以外、intervalに0を指定すると、一回だけリピートする不正な動作となるので、避けてください。
 *	- delay、または、intervalに、0未満や255超過の値を指定した場合の動作は、未定義です。
 */
void pad_set_repeat(int pad, int delay, int interval);

/*****************************************************************************
 *	標準Ｃライブラリの補助
 *****************************************************************************/

/* 以下の関数はANSI規格ではなく、BSDまたはSYSV互換の関数のようです。
 * P/ECEのCライブラリには含まれていませんが、便利なので実装しておきます。
 */
char* strdup(const char* string);
int strcasecmp(const char* s1, const char* s2);
int strncasecmp(const char* s1, const char* s2, size_t n);

/* シフトJIS二バイト文字の、第一バイトまたは第二バイト文字か調べます。
 * ANSI規格ではなく、Visual C++の_ismbblead()/_ismbbtrail()の移植です。
 * P/ECEの標準文字セットも、MS-DOS/Windowsと同じく、シフトJISです。
 */
int ismbblead(int c);
int ismbbtrail(int c);

/* iscsym(c) は、cが英字、アンダースコア、数字のいずれかであると、0以外の値を返します。
 * iscsymf(c)は、cが英字、アンダースコア      のいずれかであると、0以外の値を返します。
 * ANSI規格ではなく、Visual C++の_iscsym()/_iscsymf()の移植です。
 */
int iscsym(int c);
int iscsymf(int c);

/* C99で正式に追加された、安全な書式付き文字列出力です。
 * P/ECEのCライブラリには含まれていないので、追加しました。
 * VC++6.0には_snprintf(),vsnprintf()が含まれていますが、C99とは少し挙動が違っています。
 * 今回の実装は、C99仕様に従いました。(clipprnt.cのコメントを参照してください)
 */
int snprintf(char* out, int cnt, const char* fmt, ...);
int vsnprintf(char* out, int cnt, const char* fmt, va_list ap);

/*****************************************************************************
 *	標準エラー出力 -> clipio.c
 *****************************************************************************/

/* 標準エラー出力を開きます。
 * [in]
 *	timeout		標準エラー出力モニタの接続待ちを行う時間[秒]。
 *			負値を指定すると、接続されるか明示的にキャンセルするまで待ちます。
 * [note]
 *	// * pceAppInit()から一度だけ呼びます。
 *	//   アプリケーションプログラムからは呼ばないでください。
 *	//↓2003/11/22 変更
 *	* 標準エラー出力を使うアプリケーションは、最初にこの関数を呼んでください。
 *	  stderr_open()を呼ばなかった場合、全ての fprintf(stderr, ...) 出力は捨てられます。
 */
void stderr_open(int timeout);

/* 標準エラー出力を閉じます。
 * [note]
 *	* pceAppExit()から一度だけ呼びます。
 *	  アプリケーションプログラムからは呼ばないでください。
 */
void stderr_close();

/* 標準エラー出力にデータを出力します。
 * [in]
 *	ptr		出力するデータの先頭アドレス。
 *	len		出力するデータのバイト数。
 * [note]
 *	* アプリケーションプログラムは直接この関数を使わずに、
 *	  fwrite(..., stderr) や fprintf(stderr, ...) を使ってください。
 */
void stderr_write(const void* ptr, int len);

/*****************************************************************************
 *	ユーティリティ：タイマ
 *****************************************************************************/

/* マイクロ秒単位の時間待ちを行います。
 * [in]
 *	usec		待ち時間[マイクロ秒]
 * [note]
 *	* 超高精度カウント（pceTimerAdjustPrecisionCount()）の値は、
 *	  16bitタイマのカウンタデータとClockTicks値から生成されています。
 *	* どちらの値も割り込み禁止中もカウントアップを継続するので、
 *	  割り込み禁止中でもusleep()を使って時間待ちを行うことができます。
 */
void usleep(unsigned long usec);

/*****************************************************************************
 *	ユーティリティ：描画
 *****************************************************************************/

//{{2003/05/01:BIOS1.20で修正されました。もう不要です。
///* LCDCを再初期化します。
// * [note]
// *	* 現在のP/ECEカーネル(BIOS 1.18)では、アプリケーションを起動するたびに、
// *	  画面の濃度がバラついてしまうことがあります。
// *	* 電源投入後、一度でもリセットボタンを押すと、濃度がバラつきはじめます。
// *	* 濃度のバラつきを防ぐには、RESETB端子を使ってLCDCを再初期化します。
// *	  （P/ECEカーネルは、Resetコマンドを使っていますが、それではダメです）
// *	* この関数を、アプリケーション起動時に呼び出してください。
// *	  RESETB端子を使ってLCDCを再初期化し、確実に濃度のバラつきを修正します。
// *	  →組み込みのpceAppInit()から呼び出すようにしました。
// *	    もう、アプリケーションプログラムから直接呼び出す必要はありません。
// *	* RESETBによるリセットは、画面OFF・VRAMクリアを伴うので、ご注意ください。
// */
//void lcd_init();
//}}2003/05/01:BIOS1.20で修正されました。もう不要です。

//{{2006/12/05:render_object()が定着し、draw_object()は使わなくなったので、削除しました。
///* 現在の仮想画面に、オブジェクトを描きます。
// * [in]
// *	src		転送元ビットマップ。
// *	dx,dy		転送先の左上座標。
// *	sx,sy		転送元の左上座標。
// *	w,h		転送サイズ。
// *	param		パラメータ。
// * [note]
// *	* pceLCDSetObject()/pceLCDDrawObject()のペアを呼び出すだけです。
// *	  引数の意味は、pceLCDSetObject()と同じです。
// */
//void draw_object(PIECE_BMP* src, int dx, int dy, int sx, int sy, int w, int h, int param);
//}}2006/12/05:render_object()が定着し、draw_object()は使わなくなったので、削除しました。

/* レンダラーを指定して、オブジェクトを描きます。
 * [in]
 *	render		レンダラーへのポインタ。
 *	dx,dy		転送先の左上座標。
 *	sx,sy		転送元の左上座標。
 *	w,h		転送サイズ。
 *	param		パラメータ。
 * [note]
 *	* renderは予めrender_init()によって初期化されていなければいけません。
 *	* 描画領域は、renderに設定されたビューポート矩形内にクリッピングされます。
 *	* renderの現在のコンテキストにテクスチャが設定されていなければいけません。
 *	  テクスチャを設定せずにこの関数を呼ぶと、エラーを表示して停止します。
 *	* 通常の横画面ゲームでは、アプリケーション内にサーフェイスは一つしかなく、
 *	  pceLCDSetBuffer()とRENDERは同じサーフェイスを指しているので、この関数を使う必要はありません。
 *	  draw_object()で充分です。
 *	  しかし縦画面ゲームの場合は、メインルーチンが直接縦横変換を意識しながら描画するよりも、
 *	  いったん縦サーフェイスに描画した後、横サーフェイスに変換して転送した方が開発が楽です。
 *	  pceLCDSetBuffer()は128x88の横サーフェイス、RENDERは88x128の縦サーフェイスを指すことになります。
 *	  このようなケースで、pceLCDSetBuffer()に指定したサーフェイス以外にオブジェクトを描画するために、
 *	  render_object()を用意しました。
 *	* この関数はrender_プレフィクスを持っていますが、cliprend.h/cには含めませんでした。
 *	  その理由は、この関数がpceLCDDrawObject()に依存していて、移植性がないからです。
 *	  既にpceLCDDrawObject()の大部分は自前の高速版に置き換えられているのですが、
 *	  まだ1bit描画や特殊なパラメータの処理がオリジナルのpceLCDDrawObject()に依存しています。
 *	  従って、この関数はまだcliprend.h/cには含められないと判断しました。
 */
void render_object(RENDER* render, int dx, int dy, int sx, int sy, int w, int h, int param);

/* * Tue Dec 05 14:59:42 JST 2006 Naoyuki Sawa
 * - オブジェクトを単一色で描くための、DRW_MASKフラグを定義しました。
 *   キャラクタのドロップシャドウや、ダメージ時のフラッシュ等に利用できます。
 *
 *	-----A98 76543210
 *	     ||| ||++++++- DRW_NOMAL等 (1bit用のP/ECE BIOS標準パラメータ)
 *	     ||| |||||+++- DRW_CLR(C1) (2bit用のP/ECE BIOS標準パラメータ)
 *	     ||| ||+++---- DRW_CLR(C2) (2bit用のP/ECE BIOS標準パラメータ)
 *	     ||| |+------- DRW_REVY    (2bit用のP/ECE BIOS標準パラメータ)
 *	     ||| +-------- DRW_REVX    (2bit用のP/ECE BIOS標準パラメータ)
 *	     +++---------- DRW_MASK(C) (1,2,4bit共通のCLiP拡張パラメータ) ← これを定義しました
 *
 * - render_object()をDRW_MASKに対応しました。
 *   sprite_draw()もrender_object()を利用しているので、sprite_draw()にもDRW_MASK指定可能です。
 *
 * - スプライト定義そのものに対して、DRW_MASKを指定することはできません。
 *   SPRITE構造体のparamフィールドは8bitですが、DRW_MASKは9〜11bit目に定義されているからです。
 *   スプライト描画に対するDRW_MASK指定は、sprite_draw()へのパラメータとしてのみ指定可能です。
 *
 * - たとえば、通常、次のように描画を行う2bitマスク付きテクスチャがある場合、
 *
 *	render.context->texture = texture_table[TEX_PAT];
 *	render_object(&render, dx, dy, sx, sy, w, h, DRW_NOMAL);
 *
 *   以下のようにしてドロップシャドウやフラッシュを描くことができます。
 *
 *	render.context->texture = texture_table[TEX_PAT];
 *	render_object(&render, dx, dy, sx, sy, w, h, DRW_MASK(2)); // 暗灰(ドロップシャドウ)
 *
 *	render.context->texture = texture_table[TEX_PAT];
 *	render_object(&render, dx, dy, sx, sy, w, h, DRW_MASK(0)); // 白(フラッシュ)
 *
 *   上下反転、左右反転、上下左右反転と併用することもできます。
 *
 *	render.context->texture = texture_table[TEX_PAT];
 *	render_object(&render, dx, dy, sx, sy, w, h, DRW_REVX | DRW_REVY | DRW_MASK(0));
 *
 *   スプライト描画にも指定できます。
 *
 *	sprite_draw(x, y, SPR_FOO, DRW_MASK(0));
 *
 * - 16階調4bitデータの描画時もDRW_MASKを指定することはできますが、単一色の指定は0〜3に限られます。
 *   4〜15の単一色を指定することはできません。
 *   つまり、この機能を使って、16階調モードで真っ黒のドロップシャドウを描くことはできません。
 *   この制限の理由は、現在の実装では、P/ECE標準の1bit描画機能を利用して単一色描画を行っているためです。
 *   P/ECE標準の1bit描画機能は、0〜3(+透明)の色指定しか認識しないからです。
 */
#define DRW_MASK(c)	(((c)|4)<<8)

/****************************************************************************
 *	ユーティリティ：フォント
 ****************************************************************************/

/* 各フォントタイプごとの幅と高さ[ピクセル]です。
 * clippce.cとclipfram.cから参照するので、グローバルにしました。
 */
extern const unsigned char render_font_size[3][2]/* = { {5,10}, {8,16}, {4,6} }*/;

/* フォントを描きます。
 * [in]
 *	render		レンダラーへのポインタ。
 *	x,y		描画位置の左上座標。
 *	code		文字コード。(シフトJIS)
 *			半角文字の場合は、下位バイトに文字コードを入れて、上位バイトは0としてください。
 *			全角文字の場合は、1バイト目を上位バイト、2バイト目を下位バイトに入れてください。
 *	type		フォントの種類。(bit31〜7は無視します)
 *			0x00: 5x10(半角)または10x10(全角)
 *			0x01: 8x16(半角)
 *			0x02: 4x 6(半角)
 *	color		フォントの色。
 *			0: 白
 *			1: 明灰
 *			2: 暗灰
 *			3: 黒
 * [out]
 *	戻り値		下位8ビットに描画したフォントの幅  [ピクセル]、
 *			上位8ビットに描画したフォントの高さ[ピクセル]を返します。
 * [note]
 *	* 描画領域は、renderに設定されたビューポート矩形内にクリッピングされます。
 *	* render_string(),render_printf()とは戻り値のビット数が異なるので、ご注意ください。
 *	* この関数は内部でpceFontSetType()を呼び出しています。
 *	  この関数を呼び出すと、フォントタイプの設定が変更されるので、ご注意ください。
 *	* この関数はrender_プレフィクスを持っていますが、cliprend.h/cには含めませんでした。
 *	  pceFonSetType()/pceFontGetAdrs()およびP/ECEのフォントデータに依存しているからです。
 */
unsigned short render_font(RENDER* render, int x, int y, unsigned short code, int type, int color);

/* 文字列を描きます。
 *	render		レンダラーへのポインタ。
 *	x,y		描画位置の左上座標。
 *	str		文字列へのポインタ。
 *	type		フォントの種類。
 *			0x00: 5x10(半角)または10x10(全角) 改行あり
 *			0x01: 8x16(半角)                  改行あり
 *			0x02: 4x 6(半角)                  改行あり
 *			0x80: 5x10(半角)または10x10(全角) 改行なし
 *			0x81: 8x16(半角)                  改行なし
 *			0x82: 4x 6(半角)                  改行なし
 *	color		フォントの色。
 *			0: 白
 *			1: 明灰
 *			2: 暗灰
 *			3: 黒
 * [out]
 *	戻り値		下位16ビットに描画したフォントの幅  [ピクセル]、
 *			上位16ビットに描画したフォントの高さ[ピクセル]を返します。
 * [note]
 *	* 描画領域は、renderに設定されたビューポート矩形内にクリッピングされます。
 *	* render_font()とは戻り値のビット数が異なるので、ご注意ください。
 *	* この関数は内部でpceFontSetType()を呼び出しています。(render_font()による)
 *	  この関数を呼び出すと、フォントタイプの設定が変更されるので、ご注意ください。
 *	* この関数はrender_プレフィクスを持っていますが、cliprend.h/cには含めませんでした。
 *	  pceFonSetType()/pceFontGetAdrs()およびP/ECEのフォントデータに依存しているからです。
 */
unsigned int render_string(RENDER* render, int x, int y, const char* str, int type, int color);

/* 書式付き文字列を描きます。
 *	render		レンダラーへのポインタ。
 *	x,y		描画位置の左上座標。
 *	type		フォントの種類。
 *			0x00: 5x10(半角)または10x10(全角) 改行あり
 *			0x01: 8x16(半角)                  改行あり
 *			0x02: 4x 6(半角)                  改行あり
 *			0x80: 5x10(半角)または10x10(全角) 改行なし
 *			0x81: 8x16(半角)                  改行なし
 *			0x82: 4x 6(半角)                  改行なし
 *	color		フォントの色。
 *			0: 白
 *			1: 明灰
 *			2: 暗灰
 *			3: 黒
 *	fmt		書式指定文字列へのポインタ。
 *	...		パラメータ。
 * [out]
 *	戻り値		下位16ビットに描画したフォントの幅  [ピクセル]、
 *			上位16ビットに描画したフォントの高さ[ピクセル]を返します。
 * [note]
 *	* 描画領域は、renderに設定されたビューポート矩形内にクリッピングされます。
 *	* render_font()とは戻り値のビット数が異なるので、ご注意ください。
 *	* この関数は、書式付き文字列を展開するために、_def_vbuff[]を利用しています。
 *	  _def_vbuff[]を仮想画面として設定していた場合、画面左上にゴミが出ます。
 *	* この関数は内部でpceFontSetType()を呼び出しています。(render_font()による)
 *	  この関数を呼び出すと、フォントタイプの設定が変更されるので、ご注意ください。
 *	* この関数はrender_プレフィクスを持っていますが、cliprend.h/cには含めませんでした。
 *	  pceFonSetType()/pceFontGetAdrs()およびP/ECEのフォントデータに依存しているからです。
 */
unsigned int render_printf(RENDER* render, int x, int y, int type, int color, const char* fmt, ...);

/* 縁取りフォント/文字列/書式付き文字列を描きます。
 * [in]
 *	color		0…0ccccbbbbaaaa  ※各4bitです！8bitじゃないよ！
 *			    ||||||||++++- 中抜き
 *			    |||||+++----- 縁(直線部分)
 *			    +++++-------- 縁(角の部分)
 *			白背景に、白抜き・黒縁取りで描くなら、0x130がいいです。
 *			黒背景に、黒抜き・白縁取りで描くなら、0x203がいいです。
 *	color以外の引数については、render_font()/render_string()/render_printf()と同じです。
 *	render_font()/render_string()/render_printf()の説明を参照してください。
 * [out]
 *	戻り値		render_font()/render_string()/render_printf()と同じく、描画サイズを返します。
 *			上記関数の戻り値に較べて、縁取りの分、縦横2ピクセルづつ大きな値を返します。
 *			ただし、描画なしの場合は、上記関数と同じく、0を返します。
 */
unsigned short render_font_framed(RENDER* render, int x, int y, unsigned short code, int type, int color);
unsigned int render_string_framed(RENDER* render, int x, int y, const char* str, int type, int color);
unsigned int render_printf_framed(RENDER* render, int x, int y, int type, int color, const char* fmt, ...);

/*****************************************************************************
 *	ユーティリティ：サウンド
 *****************************************************************************/

/* サウンドを再生します。
 * [in]
 *	ch		チャネル番号(0〜3)
 *	data		サウンドデータの先頭アドレス
 *	{{2006/07/18追加
 *	loop		ループ回数
 *			1を指定すると、一回だけ再生して終了します。(通常の用法)
 *			0を指定すると、再生しません。(「sound_stop(ch)」と等価)
 *			2以上を指定すると、その回数だけループして、終了します。
 *			0未満を指定すると、無限ループします。
 *	}}2006/07/18追加
 * [note]
 *	* pceWaveOut()に渡すPCEWAVEINFOとしては、data内のPCEWAVEINFOは直接使っていません。
 *	  いったんsound_play()内のPCEWAVEINFOにコピーしてからpceWaveOut()に渡しています。
 *	  従って、同じサウンドデータを複数のチャネルで同時に再生しても大丈夫です。
 *	* サウンドデータはpPCMConv.exeの出力形式互換としてください。即ち、
 *	  +0〜:シグネチャ('PPCM')
 *	       データ本体(PCEWAVEINFOとサンプルデータ)のサイズ
 *	  +8〜:PCEWAVEINFO(pData以外のフィールドは初期化済みであること)
 *	       サンプルデータ
 *	  となっている必要があります。僕のdppcmcnv.exeの出力もこの形式です。
 *	* いきなりPCEWAVEINFOから始まるようなRAW形式データを指定すると、
 *	  実行時にsound_play()内のシグネチャチェックでアサートします。
 */
//void sound_play(int ch, const unsigned char* data);
//↓2006/07/18変更
//void sound_play(int ch, const unsigned char* data, int loop);
//↓2006/12/20変更
void sound_play(int ch, const void* data, int loop);

/* サウンドを停止します。
 * [in]
 *	ch		チャネル番号(0〜3)
 */
void sound_stop(int ch);

/* 指定されたサウンドチャネルが、再生中か停止中かを調べます。
 * [in]
 *	ch		チャネル番号(0〜3)
 * [out]
 *	戻り値		再生中ならば0以外の値を返します。
 *			停止中ならば0を返します。
 * [note]
 *	* adpcm_stat()との対称のためだけに追加しました。(形式的な理由です)
 *	  adpcm_stat()は安全ですが、sound_stat()は以下のような危険性があります。
 *	- sound_stat()は内部で、pceWaveCheckBuffs()を呼び出しています。
 *	  もしも、stream_play()などによって環状バッファを再生中であるチャネルに対して
 *	  pceWaveCheckBuffs()を呼び出すと、いつまでも残りバッファ数をカウントし続け、
 *	  ハングアップしてしまいます。
 *	- stream_play()を使う可能性のあるBGMチャネルなどに対しては、sound_stat()を
 *	  呼び出さないよう注意してください。
 *	  環状バッファを構成しない、効果音チャネルに対してだけ、利用してください。
 *	- なお、adpcm_stat()は、sound_stat()のようなハングアップの危険性はありません。
 *	  ループ再生中のチャネルに対しても、利用することができます。
 */
int sound_stat(int ch);

/****************************************************************************
 *	ユーティリティ：ストリーム再生
 ****************************************************************************/

/* ユーザーが用意する、ストリーム再生コールバック関数の型宣言です。
 * [in]
 *	wbuff		ここにサンプルデータを展開してください。
 *			サンプル形式は符号付き16bit(PW_TYPE_16BITPCM)に固定です。
 *			サンプル数はstream_play()のBLKSに指定したサンプル数です。
 *	param		stream_play()に指定したparam値がそのまま渡されます。
 * [out]
 *	戻り値		サンプルデータの展開に成功したら、 0を返してください。
 *			サンプルデータの展開に失敗したら、-1を返してください。
 *			失敗する状況とは、演奏終了やデータが壊れている場合などです。
 * [note]
 *	* コールバック関数が-1を返した場合は、ストリーム再生ルーチン側でバッファをクリアし、
 *	  無音状態にしますので、コールバック関数側でバッファをクリアする必要はありません。
 *	* コールバック関数がいったん1(失敗)を返しても、また次回にコールバックが行われます。
 *	  失敗からの回復が出来ない場合は、毎回-1を返すようにコールバックを作成してください。
 */
typedef int STREAMCALLBACK(short* wbuff, int param);

/* ストリーム再生を開始します。
 * [in]
 *	BLKS		コールバック一回当りの展開サンプル数。（バイト数ではありません！）
 *			pceWave*()の実装に由来する制限により、64の倍数でなければいけません。
 *	param		コールバックに渡される、任意のパラメータを指定することができます。
 *	fram4		0を指定すると、スタックを切り替えずにコールバックを呼び出します。
 *			1を指定すると、スタックをFRAM4領域に切り替えてからコールバックを呼び出します。
 *			詳しくは、clippce.c - STREAMINFO構造体のコメントを参照してください。
 * [note]
 *	* 既にストリーム再生中だったら、自動的にstream_stop()が呼ばれます。
 *	* 現在の実装では、ストリーム再生に利用するチャネルは、チャネル#0に固定です。
 *	* stream_play()の内部から、すぐさま初回のコールバックが行われます。
 *	  この関数を呼ぶ前に、コールバックがサンプルを展開できるよう、準備しておいてください。
 *	* stream_play()を呼ぶと、サウンド割り込みベクタがフックされます。
 *	  プログラム終了前に必ずstream_stop()を呼んで、ベクタを復元してください。
 */
void stream_play(int BLKS, STREAMCALLBACK* callback, int param, int fram4);

/* ストリーム再生を終了します。
 * [note]
 *	* ストリーム再生中でなければ、何もしません。
 *	* コールバック側のリソース開放には関知しませんので、忘れずリソース開放を行ってください。
 *	  全体の流れとしては、次のようになります。
 *		・サンプル展開用のリソース確保・コールバック準備
 *		・stream_play()
 *		・（コールバック×Ｎ）
 *		・stream_stop()
 *		・サンプル展開用のリソース開放
 */
void stream_stop();

/* ストリーム再生中か停止中かを調べます。
 * [out]
 *	戻り値		ストリーム再生中ならば1を返します。
 *			ストリーム停止中ならば0を返します。
 */
int stream_stat();

/****************************************************************************
 *	ユーティリティ：スタック切替コール
 ****************************************************************************/

/* 一時的にスタックを切り替えて、関数を呼び出します。
 * [in]
 *	sp		切替先スタックの終端アドレス。
 *	fn		呼び出す関数のアドレス。
 *	arg1		引数1。必要なければ0を指定してください。
 *	arg2		引数2。必要なければ0を指定してください。
 * [out]
 *	戻り値		呼び出した関数からの戻り値。
 * [note]
 *	* 次のように利用するのが一般的です。
 *
 *		retval = stack_switch(FRAM4_END, fn, arg1, arg2);
 *
 *	  FRAM4領域を高速スタックとして利用することが多いと思うので、
 *	  FRAM4領域のアドレスは下で定義してあります。ご利用ください。
 *
 *	* 呼び出す関数は、次のいずれかの形式でなければいけません。
 *
 *		void fn();
 *		void fn(int arg1);
 *		void fn(int arg1, int arg2);
 *		int  fn();
 *		int  fn(int arg1);
 *		int  fn(int arg1, int arg2);
 *
 *	* 2つ未満の引数しか使わない場合は、不要な引数には0を指定してください。
 *
 *		retval = stack_switch(sp, fn, arg1, 0);
 *		retval = stack_switch(sp, fn,    0, 0);
 *
 *	* 呼び出し先の関数が戻り値を返さない場合は、戻り値を無視してください。
 *
 *		stack_switch(FRAM4_END, fn, arg1, arg2);
 */
int stack_switch(void* sp, void* fn, int arg1, int arg2);

#define FRAM4_START	((void*)0x0e00)		/* FRAM4領域開始アドレス */
#define FRAM4_SIZE		0x0200		/* FRAM4領域サイズ       */
#define FRAM4_END	((void*)0x1000)		/* FRAM4領域終了アドレス */

/* * Fri Jan 7 04:19:00 JST 2005 Naoyuki Sawa
 * - FRAM4領域へのstack_switch()は頻繁に使うので、あらかじめ定義しておくことにしました。
 *   呼び出し前後でスタックが溢れていないことも検査します。
 */
int stack_switch_FRAM4(void* fn, int arg1, int arg2);

/****************************************************************************
 *	ユーティリティ：ファイル
 ****************************************************************************/

//→2002/11/26: OBSOLETEです。fread/fwriteを使ってください。
///* セクタに依存しない、ファイル読み込み関数です。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	ptr		バッファポインタ。
// *	ofs		ファイル先頭からのバイトオフセット。
// *	len		読み込むバイト数。
// * [out]
// *	戻り値		読み込んだバイト数。
// *			読み込みに失敗したら0を返します。
// * [note]
// *	* セクタ境界をまたいで読み込むこともできます。
// */
int file_read(FILEACC* fa, void* ptr, int ofs, int len);
//→GSM再生ルーチンがfile_read()を使っているので、宣言だけ残しておきます。
//  GSM再生ルーチンは、stream_play/stop対応の修正もまだですので、
//  stream_play/stop対応修正時にいっしょに、fread()を使うように修正する予定です。

//→2002/11/26: OBSOLETEです。fread/fwriteを使ってください。
///* セクタに依存しない、ファイル書き込み関数です。
// * [in]
// *	fa		FILEACC構造体。(FOMD_WRで開いておく)
// *	ptr		バッファポインタ。
// *	ofs		ファイル先頭からのバイトオフセット。
// *	len		書き込むバイト数。
// * [out]
// *	戻り値		書き込んだバイト数。
// *			書き込みに失敗したら0を返します。
// * [note]
// *	* セクタ境界をまたいで書き込むこともできます。
// *	* 既存のファイル内容のうち、書き込まなかった部分は変化せず、元の内容を維持します。
// *	  pceFileWriteSct()を使った場合は、同一セクタの書き込まなかった部分は消去されます。
// */
//int file_write(FILEACC* fa, const void* ptr, int ofs, int len);

//→2002/11/26: OBSOLETEです。unzipを使ってください。
///* ファイルパックからファイルを探します。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	fnmame		ファイル名。
// * [out]
// *	戻り値		ファイル情報へのポインタを返します。
// *			見つからなかったらNULLを返します。
// * [note]
// *	* この関数から返されるファイル情報へのポインタは、
// *	  関数内のstatic変数領域を指しています。
// *	  次回、この関数を呼んだときに破壊されますので、
// *	  必要ならば呼び出し元でコピーを取ってください。
// */
//FILE_INFO* fpak_find(FILEACC* fa, const char* fname);

/* フラッシュメモリ上で連続なセクタに配置されたファイルの先頭アドレスを取得します。
 * [in]
 *	fname		ファイル名。
 *	size		ファイルサイズを格納する変数へのポインタ。
 *			ファイルサイズが必要なければ、NULLを指定してください。
 * [out]
 *	戻り値		ファイルが存在し、連続セクタに配置されていたら、先頭アドレスを返します。
 *			ファイルが存在しないか、不連続セクタに配置されていたら、NULLを返します。
 *	*size		ファイルが存在し、連続セクタに配置されていたら、ファイルサイズを格納します。
 *			ただし、sizeにNULLポインタが指定されていた場合は、格納しません。
 */
const void* file_address(const char* fname, int* size);

//{{2003/09/14:あまり使わないので、OBSOLATEにしました。
///* 簡易ファイルセレクタです。
// * [in]
// *	header		ヘッダ文字列。
// *			NULLを指定すると、「ファイルを選んでください」となります。
// *	items		選択候補の文字列リスト。
// *	n_items		選択候補の数。
// *	footer		フッタ文字列。
// *			NULLを指定すると、「[Ａ]：決定／[Ｂ]：取消し」となります。
// * [out]
// *	戻り値		選択確定した場合は、選択された項目のインデックスを返します。
// *			選択取消しした場合は、-1を返します。
// * [note]
// *	* “ファイルセレクタ”と書きましたが、実際は汎用のアイテムセレクタです。
// *	   この関数自身にはファイルをリストアップする機能はありません。
// *	   アプリケーション側でファイルリストを作成する必要があります。
// */
//int file_select(const char* header, char* items[], int n_items, const char* footer);
//}}2003/09/14:あまり使わないので、OBSOLATEにしました。

/* ファイルを整列します。
 * [in]
 *	fname		ファイル名。
 *	size		ファイルサイズを格納する変数へのポインタ。
 *			ファイルサイズが必要なければ、NULLを指定してください。
 * [out]
 *	戻り値		整列したファイルの先頭メモリアドレス。
 *	*size		ファイルが存在し、連続セクタに配置されていたら、ファイルサイズを格納します。
 *			ただし、sizeにNULLポインタが指定されていた場合は、格納しません。
 */
const void* file_defrag(const char* fname, int* size); /* ->clipdfrg.c */

/* ファイルをメモリマッピングします。
 * [in]
 *	fname		ファイル名。
 *	size		ファイルサイズを格納する変数へのポインタ。
 *			ファイルサイズが必要なければ、NULLを指定してください。
 *	defrag		1を指定すると、セクタ不連続時にデフラグ処理を行います。
 *			0を指定すると、セクタ不連続時は失敗します。
 * [out]
 *	戻り値		成功したら、整列したファイルの先頭メモリアドレスを返します。
 *			ファイルが存在しないか、またはセクタ不連続かつdefrag=0の場合は失敗し、NULLを返します。
 *	*size		ファイルが存在し、連続セクタに配置されていたら、ファイルサイズを格納します。
 *			ただし、sizeにNULLポインタが指定されていた場合は、格納しません。
 * [note]
 *	* mmap()関数は、file_address()/file_defrag()の別名です。
 *	- 「mmap(fname, size, 0)」は、「file_address(fname, size)」と等価です。
 *	- 「mmap(fname, size, 1)」は、「file_defrag (fname, size)」と等価です。
 */
//const void* mmap(const char* fname, int* size, int defrag);
//↓2005/09/03:mmap()関数をマクロ化しました。
//↓これまでは、mmap()関数がfile_defrag()を参照していたため、デフラグ機能が不要なアプリケーションにもclipdfrg.oがリンクされていました。
//↓mmap()関数のマクロ化により、デフラグ機能が不要なアプリケーションにはclipdfrg.oがリンクされなくなり、2KB弱のメモリ節約となりました。
#define mmap(fname, size, defrag) (((defrag) ? file_defrag : file_address)((fname), (size)))

/* pexファイルの末尾に付加したリソースデータの、フラッシュメモリ上のアドレスを取得します。
 * [in]
 *	fname		pexファイル名。
 * [out]
 *	戻り値		リソースデータの先頭アドレス。
 * [note]
 *	* 目的のファイルがフラッシュメモリ上で整列していない場合、この関数の内部でデフラグを実行します。
 *	  目的のファイルのデフラグにより、他のファイルの整列が崩れる可能性があることに注意してください。
 *	* プログラム本体(4バイト単位)の直後に、リソースが付加されています。
 *	  付加を行う仕組みについては、test.pce/Makefileを参照してください。
 *	* リソースデータの形式は特に規定していませんが、通常はfpk形式で付加することになると思います。
 *	  (フラッシュメモリ上のデータを直接使うのが目的なので、圧縮されていてはあまり意味が無いから。)
 *	  戻り値のアドレスを、fpk_address()など、リソースデータ形式に応じた関数に引き渡してください。
 */
const void* resource_address(const char* fname); /* →clipdfrg.oの不要なリンクを避けるため、単独モジュールのclipres.cに実装しました */

/****************************************************************************
 *	ユーティリティ：拡張タイマ
 ****************************************************************************/

/* * 拡張タイマ機能を作成した理由は、以下のとおりです。
 *
 *   1. pceTimerSetCallback()は、同時利用可能なチャネルが少ない
 *	pceTimerSetCallback()は、Ch.0-3の4チャネルしか利用できません。
 *	(カーネル1.20においては、Ch.4も利用可能ですが、仕様外です)
 *	拡張タイマは、同時に32の拡張タイマを利用可能です。(2005/09/04現在)
 *	→2005/09/17 16個に減らしました。
 *
 *   2. pceTimerSetCallback()は、チャネル衝突の回避が困難である
 *	pceTimerSetCallback()は、呼び出し側にてチャネル番号を指定するので、
 *	関連の無い複数のルーチンが同時にタイマを利用する場合、チャネル衝突の回避が困難です。
 *	拡張タイマは、timer_start()から呼び出し側へ拡張タイマIDを返すので、
 *	関連の無い複数のルーチンが同時にタイマを利用する場合も、チャネル衝突は有り得ません。
 *
 * * 拡張タイマ機能は、pceTimerSetCallback()のCh.0を利用しています。(2005/09/04現在)
 *   最初にtimer_start()を呼び出したとき、pceTimerSetCallback()のCh.0を設定します。
 *   以後、ライブラリの終了ルーチンにて自動的にタイマを停止するまで、そのままです。
 *   以下の点に注意してください。
 *
 *   1. pceTimerSetCallback()のCh.0を利用するアプリケーションと併用できません
 *	pceTimerSetCallback()のCh.0を利用するアプリケーションは、timer_start()を利用しないでください。
 *	timer_start()を利用するアプリケーションは、pceTimerSetCallback()のCh.0を利用しないでください。
 *	今後のプログラム作成方針として、アプリケーション、および、CLiPライブラリ内の各モジュールは、
 *	pceTimerSetCallback()のいずれのチャネルも利用せず、timer_start()を利用するようにしてください。
 *	(2005/09/04現在、clipcbxモジュールがpceTimerSetCallback()のCh.0を利用しています。いずれ修正する予定です)
 *
 *   2. timer_start()を呼び出すと、アプリケーション終了まで、一定のオーバーヘッドが発生します
 *	拡張タイマ機能は、pceTimerSetCallback()のCh.0を利用し、1ミリ秒コールバックにてドライブしています。
 *	アプリケーション実行後、初回のtimer_start()にて、pceTimerSetCallback()のCh.0を起動しています。
 *	(2005/09/04現在の実装は、簡単のために、timer_start()が呼ばれる度にpceTimerSetCallback()のCh.0を
 *	 再起動していますが、アプリケーションから見た挙動は、初回呼び出し時のみ起動と見なしても同じです)
 *	timer_start()を呼び出さないアプリケーションでは、拡張タイマ機能のオーバーヘッドは発生しません。
 *	一度でもtimer_start()を呼び出すと、アプリケーション終了まで1ミリ秒コールバックが継続するため、
 *	オーバーヘッドが発生します。(実際には、気にするほどの速度低下では無いかも知れませんが…)
 *	処理速度が重要なアプリケーションにおいては、無駄にtimer_start()を利用しないよう注意してください。
 */

/* 拡張タイマを開始します。
 * [in]
 *	interval	コールバック間隔[ms]。
 *			1以上の値を指定してください。1未満の値を指定すると、エラー終了します。
 *	callback	コールバック関数。
 *			コールバック関数の指定は必須です。NULLを指定すると、エラー終了します。
 *	context		コールバック引数。
 *			アプリケーションにて、自由に設定できます。
 * [out]
 *	戻り値		拡張タイマID。
 *			2005/09/10追記: これまで0〜31を返していましたが、1〜32となるよう変更しました。
 *			アプリケーションが保持している拡張タイマIDの、有効or無効を簡単にするためです。
 *			→2005/09/17 1〜16に減らしました。
 * [note]
 *	* 2005/09/04現在、同時に32個までの拡張タイマが利用可能です。
 *	  同時に32個を超える拡張タイマを利用しようとすると、エラー終了します。
 *	  →2005/09/17 16個に減らしました。
 *	* 拡張タイマを停止せずに、アプリケーションを終了しても安全です。
 *	  アプリケーション終了時に、ライブラリが適切に拡張タイマを停止します。
 *	* 2005/09/17追記 コールバック引数の指定を追加しました。
 */
int timer_start(int interval, void (*callback)(void* context), void* context);

/* 拡張タイマを停止します。
 * [in]
 *	id		拡張タイマID。
 *			timer_start()から返された拡張タイマIDを指定してください。
 *			不正な拡張タイマIDや、既に停止した拡張タイマIDを指定すると、エラー終了します。
 */
void timer_stop(int id);

/****************************************************************************
 *	ユーティリティ：タイムアウト処理
 ****************************************************************************/

/* タイムアウト時間が経過するか、明示的に中断されるまで、同じ処理を繰り返します。
 * [in]
 *	timeout		タイムアウト時間[ms]。(0以上)
 *	action		繰り返す処理。(以下、アクションと呼びます)
 * [out]
 *	戻り値		タイムアウトした場合、-1を返します。
 *			アクションの中で、BREAK_TIMEOUTを呼び出した場合、0を返します。
 *			または、BREAK_TIMEOUT_()を呼び出した場合、その引数を返します。
 * [note]
 *	* タイムアウト時間に0を指定した場合も、必ず一度はアクションを実行します。
 *	  その後、ただちに処理を返します。
 *	* REPEAT_TIMEOUT()マクロは、以下のGCC拡張機能を利用しています。
 *	- 式の中の文と宣言
 *	- 局所的に宣言されたラベル
 *	- 定数以外の初期化子
 * [note]
 *	* "if(0) { goto __RESTART__; goto __CONTINUE__; goto __BREAK__; }"は、
 *	  未使用ラベルに対するコンパイラ警告を抑制するための記述です。
 *	  コンパイラの最適化により削除されるので、実行バイナリには残りません。
 */
#define REPEAT_TIMEOUT(timeout, action) ({				\
__label__ __RESTART__, __CONTINUE__, __BREAK__;				\
	int __result__, __timeout__, __start__;				\
	if(0) { goto __RESTART__; goto __CONTINUE__; goto __BREAK__; }	\
	__timeout__ = (timeout);					\
__RESTART__:								\
	__start__ = pceTimerGetCount();					\
	do {								\
		action;							\
__CONTINUE__:								\
	} while(pceTimerGetCount() - __start__ < __timeout__);		\
	__result__ = -1;						\
__BREAK__:								\
	__result__;							\
})

/* 結果コード0で、REPEAT_TIMEOUT()を中断します。 */
#define BREAK_TIMEOUT			BREAK_TIMEOUT_(0)

/* 結果コードを指定して、REPEAT_TIMEOUT()を中断します。 */
#define BREAK_TIMEOUT_(result)		do { __result__ = (result); goto __BREAK__; } while(0)

/* アクションの残りをスキップし、タイムアウト時間を変更せずに、タイムアウト判定へ進みます。 */
#define CONTINUE_TIMEOUT		CONTINUE_TIMEOUT_(__timeout__)

/* アクションの残りをスキップし、タイムアウト時間を再設定して、タイムアウト判定へ進みます。 */
#define CONTINUE_TIMEOUT_(timeout)	do { __timeout__ = (timeout); goto __CONTINUE__; } while(0)

/* アクションの残りをスキップし、タイムアウト時間を変更せずに、タイムアウト処理を再起動します。 */
#define RESTART_TIMEOUT			RESTART_TIMEOUT_(__timeout__)

/* アクションの残りをスキップし、タイムアウト時間を再設定して、タイムアウト処理を再起動します。 */
#define RESTART_TIMEOUT_(timeout)	do { __timeout__ = (timeout); goto __RESTART__; } while(0)

/* REPEAT_TIMEOUT()の使用例:
 *
 *	* 1000ミリ秒タイムアウトするまで、変数aをインクリメントし続けます。
 *	  途中で終了することはありません。
 *
 *		REPEAT_TIMEOUT(1000, a++);
 *
 *	* 一度だけ変数aをインクリメントし、ただちに処理を返します。
 *	  単純に"a++"と書くのと等価であり、あまり意味はありません。
 *
 *		REPEAT_TIMEOUT(0, a++);
 *
 *	* 192.168.1.1の80番ポートに対し、TCPアクティブオープンを起動します。
 *	  3000ミリ秒以内に接続が確立したら、(result= 0)となります。
 *	  3000ミリ秒以内に接続できなければ、(result=-1)となります。
 *	  resultの値によって、接続成功、または、接続失敗時の処理を行います。
 *
 *		socket = tcp_socket_open(0, IP_ADDRESS(192,168,1,1), 80, 1);
 *		result = REPEAT_TIMEOUT(3000,
 *			if(tcp_socket_state(socket) == TCP_ESTABLISHED) {
 *				BREAK_TIMEOUT;
 *			}
 *		);
 *		if(result == 0) {
 *			// 接続成功
 *		} else {
 *			// 接続失敗
 *		}
 *
 *	  ただし実際には、TCP_ESTABLISHEDを経由してTCP_CLOSED等へ進んでしまう場合があり、
 *	  上述のコードではそのようなケースに対応できないことに注意してください。
 *	  tcp_socket_recv()、tcp_socket_send()は、未接続状態にて負数ではなく0を返すので、
 *	  上述のコードのような、明示的な接続待ちを行う必要はありません。
 *	  接続が確立するまでの間、tcp_socket_recv()の戻り値(0)はパケット未到着と同じ、
 *	  tcp_socket_send()の戻り値(0)は送信バッファ空き無しと同じと見なすことができます。
 *	  従って、tcp_socket_open()後、ただちに、送受信処理へ進んでしまって大丈夫です。
 *
 *	* TCPソケットから、データを読み出します。
 *	  3000ミリ秒以内にデータが到着したら、(result=データ長)となります。
 *	  データが到着しないか、または、相手側から切断されたら、(result=負数)となります。
 *	  resultの値によって、受信成功、または、受信失敗時の処理を行います。
 *	  tcp_socket_recv()が、相手から切断されたときに負数の戻り値を返すことを利用して、
 *	  タイムアウト(戻り値=-1)と同じ条件で判断しています。
 *
 *		result = REPEAT_TIMEOUT(3000,
 *			int tmpval = tcp_socket_recv(socket, buf, buflen);
 *			if(tmpval) { // データが到着したら正数、切断されたら負数
 *				BREAK_TIMEOUT_(tmpval);
 *			}
 *		);
 *		if(result > 0) {
 *			// 受信成功 (buf[0..result-1]に、受信データが入っている)
 *		} else {
 *			// 受信失敗 (タイムアウト、または、相手側から切断された)
 *		}
 *
 *	  上のコードでは、読み易さのために、ローカル変数tmpvalを使っていますが、
 *	  次のように、resultを使っても結果は同じです。
 *	  REPEAT_TIMEOUT()の戻り値からresultへの代入が、最後に行われるからです。
 *
 *		result = REPEAT_TIMEOUT(3000,
 *			result = tcp_socket_recv(socket, buf, buflen);
 *			if(result) { // データが到着したら正数、切断されたら負数
 *				BREAK_TIMEOUT_(result);
 *			}
 *		);
 *		if(result > 0) {
 *			// 受信成功 (buf[0..result-1]に、受信データが入っている)
 *		} else {
 *			// 受信失敗 (タイムアウト、または、相手側から切断された)
 *		}
 *
 *	  2005/09/23現在のTCPドライバは、再送間隔2000ミリ秒で実装しているので、
 *	  タイムアウト時間はそれよりも長い時間としてください。(推奨、3000ミリ秒)
 *	  再送間隔よりも短いタイムアウトでは、再送処理が意味を成さなくなります。
 *
 *	* TCPソケットから、1文字づつ受信を試みます。
 *	  TCPソケットが相手から切断されたら、ただちに処理を返します。
 *	  受信データが無いか、または、制御文字を受信した場合は、何もしません。
 *	  それ以外の場合は、関数foo()にて、受信した文字を処理します。
 *	  以上の処理を繰り返し、3000ミリ秒でタイムアウトします。
 *
 *		REPEAT_TIMEOUT(3000,
 *			result = tcp_socket_recv(socket, &ch, 1);
 *			if(result < 0) {
 *				BREAK_TIMEOUT_(result);
 *			}
 *			if(!result) {
 *				CONTINUE_TIMEOUT;
 *			}
 *			if(ch < ' ') {
 *				CONTINUE_TIMEOUT;
 *			}
 *			foo(ch);
 *		);
 *
 *	* UDPソケットに最初のパケットが到着するまで、3000ミリ秒待ちます。
 *	  3000ミリ秒以内に最初のパケットが到着しなければ、タイムアウトします。
 *	  3000ミリ秒以内に最初のパケットが到着した場合は、関数foo()にて処理を行います。
 *	  以降、1000ミリ秒間隔で次のパケットが到着する間、タイムアウトせずに処理を継続します。
 *
 *		REPEAT_TIMEOUT(3000,
 *			result = udp_socket_recv(socket, NULL, NULL, buf, buflen);
 *			if(result >= 0) {
 *				foo(buf, result);
 *				RESTART_TIMEOUT_(1000);
 *			}
 *		);
 */

/****************************************************************************
 *	zlib展開 -> clipzlib.c
 ****************************************************************************/

//→2002/11/26: OBSOLETEです。unzipを使ってください。
///* ファイルに含まれる圧縮データを展開します。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	outbuf		展開バッファ。
// *	outlen		展開バッファサイズ。
// *	inofs		圧縮データの先頭オフセット。
// *	inlen		圧縮データのバイト数。
// *			0を指定すると、inofsからファイル終端までと見なします。
// * [out]
// *	戻り値		展開後のバイト数。
// *			展開に失敗したら0を返します。
// */
//int file_inflate(FILEACC* fa, void* outbuf, int outlen, int inofs, int inlen);

//→2002/11/26: OBSOLETEです。unzipを使ってください。
///* ファイルに含まれる圧縮データの展開サイズを調べます。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	inofs		圧縮データの先頭オフセット。
// *	inlen		圧縮データのバイト数。
// *			0を指定すると、inofsからファイル終端までと見なします。
// * [out]
// *	戻り値		展開後のバイト数。
// *			展開に失敗したら0を返します。
// */
//int file_inflate_size(FILEACC* fa, int inofs, int inlen);

//→2002/11/26: OBSOLETEです。unzipを使ってください。
///* ファイルパックに含まれる圧縮データを展開します。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	outbuf		展開バッファ。
// *	outlen		展開バッファサイズ。
// *	fname		ファイル名。
// * [out]
// *	戻り値		展開後のバイト数。
// *			展開に失敗したら0を返します。
// */
//int fpak_inflate(FILEACC* fa, void* outbuf, int outlen, const char* fname);

//→2002/11/26: OBSOLETEです。unzipを使ってください。
///* ファイルパックに含まれる圧縮データの展開サイズを調べます。
// * [in]
// *	fa		FILEACC構造体。(FOMD_RDで開いておく)
// *	fname		ファイル名。
// * [out]
// *	戻り値		展開後のバイト数。
// *			展開に失敗したら0を返します。
// */
//int fpak_inflate_size(FILEACC* fa, const char* fname);

/* メモリ上のデータを圧縮します。
 * [in]
 *	out		圧縮バッファ。
 *	outlen		圧縮バッファサイズが設定された変数へのポインタ。
 *	in		データバッファ。
 *	inlen		データサイズ。
 * [out]
 *	戻り値		成功ならZ_OK。
 *			失敗ならZ_OK以外の値を返します。
 *	*out		成功なら圧縮データが格納されます。
 *			失敗なら内容は不定となります。
 *	*outlen		成功なら圧縮データサイズが格納されます。
 *			失敗なら内容は不定となります。
 * [note]
 *	* 圧縮処理は展開処理以上に作業用メモリを必要とするので、
 *	  zlib標準のcompress()やcompress2()では、メモリ不足で利用できません。
 *	- また、zlib 1.1.4 には、deflateInit2()のエラー処理に問題があるようです。
 *	  deflateInit2()でエラーが起きると、zlib内部でメモリリークしてしまいます。
 *	  deflateInit2()を使っている、compress()やcompress2()も同様です。
 *	- deflateInit2()が失敗するのはたいてい、作業用メモリが足りない場合です。
 *	  以上の理由により、省メモリ設定の圧縮関数を用意することにしました。
 *	- しかしながら、それでも作業用メモリが不足することはあります。
 *	  圧縮データサイズに比例した作業用メモリが必要になるようで、
 *	  約8KBのデータ圧縮時には、16KB程度の作業用メモリが必要でした。
 *	  大きなデータを圧縮しようとすると、deflateInit2()が失敗し、メモリリークします。
 *	  この関数が失敗した場合は、メッセージ表示後、プログラムを終了した方が安全です。
 *	* my_compress()を呼ぶ前に、*outlen圧縮バッファサイズを設定しておくのを忘れないで！
 *	* 展開には、zlib標準のuncompress()をそのまま使ってください。
 */
int my_compress(void* out, int* outlen, const void* in, int inlen);

/****************************************************************************
 *	zip展開 -> clipunz.c
 ****************************************************************************/

/* 一般的なZIPファイル読み込み手順を行います。
 * [in]
 *	zip_fname	ZIPファイル名。「mydata.zip」等です。
 *	item_name	ZIPファイル内のアイテム名。「music1.mod」等です。
 *			フォルダ付きで指定することもできます。「mus/music1.mod」等です。
 *	buffer		読み込み先バッファの先頭アドレス。
 *			NULLを指定すると、アイテムを読み込まずにサイズだけを取得します。
 *	max_size	バッファサイズ。アイテムサイズがこれより大きければ読み込み失敗します。
 *			bufferにNULLを指定した場合は、max_sizeは無視します。
 *	ufi_out		unz_file_info変数へのポインタを指定すると、読み込んだアイテムの情報が格納されます。
 *			読み込み失敗時の格納値は不定です。(現在は“変化なし”ですが、今後変更の可能性あり。)
 *			アイテムの情報が必要ない場合は、NULLポインタを指定してください。
 * [out]
 *	戻り値		読み込み成功なら、アイテムのバイト数を返します。
 *			読み込み失敗なら、0を返します。
 * [note]
 *	* この関数はunzWindowBitsを操作しません。
 *	  スライドウインドウサイズを減らして作業メモリを節約する場合は、
 *	  この関数を呼び出す側で「unzWindowBits=9」を行ってください。
 *	  圧縮ファイル作成時も「minizip -w9 ...」と指定するのを忘れずに！
 */
int my_unzip(const char* zip_fname, const char* item_name, void* buffer, int max_size, unz_file_info* ufi_out);

/****************************************************************************
 *	GSM再生 -> clipgsm.c
 ****************************************************************************/

/* GSMファイルの再生を開始します。
 * [in]
 *	ch		再生チャネル。(0〜3)
 *	fname		GSMファイル名。
 *	loop		ループ回数。(-1:無限,0:再生しない,1〜:ループ回数)
 * [out]
 *	戻り値		正しく再生を開始できたら0を返します。
 *			失敗したら-1を返します。(ファイルが見つからない、等)
 */
int gsm_play(int ch, const char* fname, int loop);

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

#endif /*__CLIP_PCE_H__*/
