/*	
 *	clipdmg.h
 *
 *	P/ECE DMG-CPU Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Thu Jan 15 20:09:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Fri Feb 18 03:32:00 JST 2005 Naoyuki Sawa
 *	- 割り込みの保留に対応しました。
 *	* Fri Feb 18 18:28:00 JST 2005 Naoyuki Sawa
 *	- dmg_reset()にGameBoy擬似IPL機能を追加しました。
 *	* Tue Feb 22 19:00:00 JST 2005 Naoyuki Sawa
 *	- DMGOP_マクロにて、実行サイクル数の定義を間違えていたのを修正しました。
 *	  dmg/xx.h、dmg/cb.hの中では10進数で記述してあるのに、間違えて16進数と解釈してしまっていました。
 *	  たとえば、本当は12サイクル消費すべき命令が、間違って0x12=18サイクル消費してしまっていました。
 *	  この誤りの原因は、DMGOP_マクロ定義の原型を、clipz80.hのZ80OP_マクロからコピーしてきたためです。
 *	  Z80エミュレーションモジュールの実行サイクル数定義は、実際に16進数表記だったので、"0x##CYCLE"で正しいのですが、
 *	  DMG-CPUエミュレーションモジュールの実行サイクル数定義は10進数に変更していたために、前述の不一致が生じたのです。
 *	  これまでに、本DMG-CPUエミュレーションモジュールを使ったアプリケーションは、「GBSプレイヤー」のみです。
 *	  GBSプレイヤーは、100%のサイクル実行を行う前にHALTで抜けていたので、実行サイクル数の不一致が露見しませんでした。
 */
#ifndef __CLIP_DMG_H__
#define __CLIP_DMG_H__

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

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

/* このシンボルを定義すると、全ての命令実行をトレース出力します。
 * このシンボルを定義しなければ、各命令実行はトレース出力しません。
 * 全ての命令実行をトレースするととても遅くなるので、通常は定義しないでください。
 * なお、このシンボルを定義しても、全体のトレースON指示“TRACE_ON=1”がなければ、
 * トレース出力は行われません。
 */
//#define DMG_TRACE

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

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

/* アセンブラコードは全命令トレースに対応していないので、
 * アセンブラコード使用時は確実に全命令トレースをOFFにします。
 */
#ifdef DMG_ASM
#undef DMG_TRACE
#endif /*DMG_ASM*/ 

#ifdef DMG_TRACE
#define DMGTRACE	TRACE
#else /*DMG_TRACE*/
#define DMGTRACE	1 ? ((void)0) : TRACE
#endif /*DMG_TRACE*/

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

typedef struct _DMG DMG;

/*
 *	DMGコア外部関数の型定義
 */
typedef unsigned char DMGREADPROC (DMG* dmg, unsigned short addr);
typedef          void DMGWRITEPROC(DMG* dmg, unsigned short addr, unsigned char data);

/*
 *	DMGコア
 */
/*typedef*/ struct _DMG {
	/*===== レジスタ =====*/
	unsigned short af;		/* + 0, 2 */
	unsigned short bc;		/* + 2, 2 */
	unsigned short de;		/* + 4, 2 */
	unsigned short hl;		/* + 6, 2 */
	unsigned short sp;		/* + 8, 2 */
	unsigned short pc;		/* +10, 2 */
	/*===== 内部状態 =====*/
	unsigned char ime;		/* +12, 1 */ /* Interrupt Master Enable */
	unsigned char halt;		/* +13, 1 */
	unsigned char _resv1[6];	/* +14, 6 */
	int cycle;			/* +20, 4 */
	/*===== 外部関数 =====*/
	DMGREADPROC* read;		/* +24, 4 */
	DMGWRITEPROC* write;		/* +28, 4 */
} /*DMG*/;				/* =32    */

/*
 *	インストラクション関数宣言
 */
typedef                void DMGOPPROC (DMG* dmg, unsigned char code, int arg);
#define DMGFN_(OP, FN) void dmgop_##FN(DMG* dmg, unsigned char code, int arg);	/* 宣言用 */
#include "dmg/list.h" /* [dmg.xls]List!C5 */
DMGFN_("CB"   , CB   )
DMGFN_("ERROR", ERROR)
#undef  DMGFN_
#define DMGFN_(FN) void dmgop_##FN(DMG* dmg, unsigned char code, int arg)	/* 定義用 */

/*
 *	オペコードマップ・ニーモニックテーブル
 */
typedef struct _DMGOP {
	DMGOPPROC* proc;	/* + 0, 4 */
	unsigned char argc;	/* + 4, 1 */
	unsigned char cycle;	/* + 5, 1 */
	unsigned short name;	/* + 6, 2 dmg_op_name_table[]内の文字列へのオフセットです */
} DMGOP;			/* = 8    */
//#define DMGOP_(CODE, PROC, ARGC, CYCLE, NAME)	{ dmgop_##PROC, 0x##ARGC, 0x##CYCLE, 0x##NAME },
//↓* Tue Feb 22 19:00:00 JST 2005 Naoyuki Sawa
//↓- DMGOP_マクロにて、実行サイクル数の定義を間違えていたのを修正しました。
//↓  dmg/xx.h、dmg/cb.hの中では10進数で記述してあるのに、間違えて16進数と解釈してしまっていました。
//↓  たとえば、本当は12サイクル消費すべき命令が、間違って0x12=18サイクル消費してしまっていました。
//↓  この誤りの原因は、DMGOP_マクロ定義の原型を、clipz80.hのZ80OP_マクロからコピーしてきたためです。
//↓  Z80エミュレーションモジュールの実行サイクル数定義は、実際に16進数表記だったので、"0x##CYCLE"で正しいのですが、
//↓  DMG-CPUエミュレーションモジュールの実行サイクル数定義は10進数に変更していたために、前述の不一致が生じたのです。
//↓  これまでに、本DMG-CPUエミュレーションモジュールを使ったアプリケーションは、「GBSプレイヤー」のみです。
//↓  GBSプレイヤーは、100%のサイクル実行を行う前にHALTで抜けていたので、実行サイクル数の不一致が露見しませんでした。
#define DMGOP_(CODE, PROC, ARGC, CYCLE, NAME)	{ dmgop_##PROC, 0x##ARGC, CYCLE, 0x##NAME },
extern const DMGOP dmg_op_table[256/*XX*/];		/* XX */
extern const DMGOP dmg_op_table_CB[256/*XX*/];		/* CB XX */
extern const char dmg_op_name_table[];			/* ニーモニックテーブル */

/****************************************************************************
 *	内部関数
 ****************************************************************************/

/* DMGリセット時に設定される、DMGコア内蔵のメインメモリREAD/WRITEの外部関数です。
 * アプリケーションから直接呼び出すことはありません。
 */
unsigned char dmg_internal_read (DMG* dmg, unsigned short addr);
         void dmg_internal_write(DMG* dmg, unsigned short addr, unsigned char data);

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

/* DMGをリセットします。
 * [in]
 *	dmg		DMG構造体。
 *	read		メインメモリREAD 外部関数 (NULL:コア内蔵関数を使用)
 *	write		メインメモリWRITE外部関数 (NULL:コア内蔵関数を使用)
 *	gb_ipl		0以外の値を指定すると、GameBoy擬似IPLを実行します。
 * [note]
 *	* read,writeにNULLを指定すると、コア内蔵の外部関数を使用します。
 *	  コア内蔵の外部関数は、次のような動作を行います。
 *	  ・READ	DMG構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリを読み込みます。
 *	  ・WRITE	DMG構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリに書き込みます。
 */
void dmg_reset(DMG* dmg, DMGREADPROC* read, DMGWRITEPROC* write, int gb_ipl);

/* DMGを実行します。
 * [in]
 *	dmg		DMG構造体。
 *	cycle		実行サイクル数。
 * [note]
 *	* 実行サイクル数がcycleに達するか、またはHALTされるまで実行します。
 *	  前者の場合、実際の実行サイクル数は、cycleよりも多くなる可能性があります。
 *	  後者の場合、実際の実行サイクル数は、cycleよりも少なくなる可能性があります。
 *	* この関数から戻ったとき、dmg->cycleに残り実行サイクル数が格納されています。
 *	  例えば cycle=100 で呼び出し、実際には 105 サイクル実行した場合、  dmg->cycle=-5 となります。
 *	  また、 cycle=100 で呼び出し、10 サイクル実行後に HALT した場合は、dmg->cycle=90 となります。
 */
void dmg_run(DMG* dmg, int cycle);

/* * 明示的な割り込み要求を行う関数はありません。
 *   $FFFF(IE)または$FF0F(IF)の変更後に、dmg_check_pending()を呼び出してください。
 *
 *   <例>
 *	// メインメモリWrite関数のサンプル
 *	void dmg_write(DMG* dmg, unsigned short addr, unsigned char data) {
 *		switch(addr) {
 *		case 0xff0f: // $FF0F (IF)
 *		case 0xffff: // $FFFF (IE)
 *			mem[addr] = data;
 *			dmg_check_pending(dmg);
 *			return;
 *		}
 *		dmg_internal_write(addr, data);
 *	}
 *	// V-Blank割り込み要求のサンプル
 *	void dmg_vblank(DMG* dmg) {
 *		mem[0xff0f] |= DMG_V_BLANK;
 *		dmg_check_pending(dmg);
 *	}
 */
void dmg_check_pending(DMG* dmg);

/* $FF0F (IF)、$FFFF (IE) */
#define DMG_V_BLANK			(1<<0)	/* RST $40 */
#define DMG_LCDC_STATUS			(1<<1)	/* RST $48 */
#define DMG_TIMER_OVERFLOW		(1<<2)	/* RST $50 */
#define DMG_SERIAL_TRANSFER_COMPLETION	(1<<3)	/* RST $58 */
#define DMG_HIGH_TO_LOW_OF_P10_P13	(1<<4)	/* RST $60 */

/* 標準エラー出力に、レジスタダンプを行います。
 * [in]
 *	dmg		DMG構造体。
 */
void dmg_dump(DMG* dmg);

#ifdef PIECE
/* DMGを実行します。(スタック切替・高速版)
 * [in]
 *	dmg		DMG構造体。
 *	cycle		実行サイクル数。
 * [note]
 *	* dmg_run()との違いは、この関数の実行中のみ、スタックを0x0e00〜0x1000の高速RAMに切り換えることです。
 *	- 0x0e00〜0x1000は、P/ECEカーネルが同期的に高速RAMルーチンを転送して使用するエリアです。
 *	  例えば、pceLCDTrans()やpceFileWriteSct()等が、このエリアに高速RAMルーチンを転送します。
 *	  同期的に使用されるため、これらのAPIによって0x0e00〜0x1000が破壊されることを留意しておけば、
 *	  アプリケーションプログラムがこの領域を使うことも可能です。
 *	- dmg_run()の実装は、call使用回数が非常に多いため、スタックを高速RAMに移すことには意味があります。
 *	  dmg_run2()はスタックを0x1000に移した後、dmg_run()を呼び出し、スタックを元に戻して処理を返します。
 *	- スタックエリアは0x0e00〜0x1000の512バイトしかないので、dmg_run2()の使用には条件があります。
 *	  ・dmg_run()とそこから呼ばれる命令処理関数は、大きなスタックフレームを確保してはいけません。
 *	    各命令処理の実装を変更する場合は、注意してください。
 *	  ・dmg_run()から呼ばれる外部関数(READ/WRITE)では、大きなスタックフレームを確保してはいけません。
 *	  ・スタックをたくさん消費するような割り込みルーチンとの併用は不可です。
 *	    スタックが0x0e00〜0x1000に切り替わっているときに割り込みが発生すると、スタックがあふれる恐れがあるからです。
 *	  ・dmg_run2()を割り込みルーチン内で呼び出してはいけません。
 *	    前面で、pceLCDTrans()が0x0e00〜0x1000エリアを使用中である可能性があるからです。
 *	- 以上のような制約があるので、dmg_run2()の使用には充分な注意が必要です。
 *	  dmg_run()を使った場合に較べて、dmg_run2()を使った場合の効果は、(※未測定)の高速化となります。
 */
void dmg_run2(DMG* dmg, int cycle);
#endif /*PIECE*/

#endif /*__CLIP_DMG_H__*/
