/*	
 *	clipc62.h
 *
 *	P/ECE HuC6280 Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Mon Feb 23 04:28:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_C62_H__
#define __CLIP_C62_H__

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

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

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

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

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

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

#ifdef HUC6280_TRACE
#define HUC6280TRACE	TRACE
#else /*HUC6280_TRACE*/
#define HUC6280TRACE	1 ? ((void)0) : TRACE
#endif /*HUC6280_TRACE*/

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

typedef struct _HUC6280 HUC6280;

/*
 *	HuC6280コア外部関数の型定義
 */
typedef unsigned char HUC6280READPROC  (HUC6280* huc6280, unsigned short addr);
typedef          void HUC6280WRITEPROC (HUC6280* huc6280, unsigned short addr, unsigned char data);
typedef          void HUC6280MPRSETPROC(HUC6280* huc6280, int addr/*0〜7*/, unsigned char data);
typedef          void HUC6280VDCOUTPROC(HUC6280* huc6280, int addr/*0〜2*/, unsigned char data);

/*
 *	HuC6280コア
 *
 *	* mpr_enについて
 *	- 本エミュレーションプログラム独自仕様で、本物のHuC6280にはありません。
 *	- mpr_enのbit0〜7がMPR0〜7に対応し、0:未設定/1:設定済み を表します。
 *	  リセット直後は 0:未設定 で、MPRiが設定されると 1:設定済み になります。
 *	  1:設定済み => 0:未設定 に変化することはありません。
 *	- このフィールドが必要となった理由は、次の通りです。
 *	  MPRiの値には、無効値がありません。
 *	  (PC-Engineでは絶対に使われない値はありますが、HuC6280の仕様ではありません)
 *	  初期化直後、MPRiはゼロクリアされています。
 *	  MPRiに0を設定しても、0 => 0 で変化が無く、外部関数が呼ばれないことになってしまいます。
 *	  そこで、MPRiの値の変化の有無に関係なく、mpr_enの対応ビットが0ならば、
 *	  リセット後はじめてのMPRi設定と見なして、外部関数を呼ぶことにしました。
 *	  その後、mpr_enの対応ビットを1にします。
 *	  次回からは、MPRiが変化した場合のみ、外部関数が呼ばれます。
 */
/*typedef*/ struct _HUC6280 {
	/*===== レジスタ =====*/
	unsigned char p;		/* + 0, 1 */
	unsigned char a;		/* + 1, 1 */
	unsigned char x;		/* + 2, 1 */
	unsigned char y;		/* + 3, 1 */
	unsigned short pc;		/* + 4, 2 */
	unsigned char s;		/* + 6, 1 */
	unsigned char mpr_en;		/* + 7, 1 */
	unsigned char mpr[8];		/* + 8, 8 */
	/*===== 内部状態 =====*/
	int cycle;			/* +16, 4 */
	unsigned char halt;		/* +20, 1 */
	unsigned char pending;		/* +21, 1 */
	unsigned char resv2[2];		/* +22, 2 */
	/*===== 外部関数 =====*/
	HUC6280READPROC* read;		/* +24, 4 */
	HUC6280WRITEPROC* write;	/* +28, 4 */
	HUC6280MPRSETPROC* mprset;	/* +32, 4 */
	HUC6280VDCOUTPROC* vdcout;	/* +36, 4 */
} /*HUC6280*/;				/* =40    */

/* HUC6280.pending */
#define HUC6280_IRQ1_PENDING	(1 << 0)	/* IRQ1 が保留中 (優先度高) */
#define HUC6280_IRQ2_PENDING	(1 << 1)	/* IRQ2 が保留中 (優先度低) */

/*
 *	割り込みベクタアドレス
 */
#define HUC6280_RESET_VECTOR	0xfffe		/* RESET    P(I)によるマスク不可 */
#define HUC6280_NMI_VECTOR	0xfffc		/* NMI      P(I)によるマスク不可 */
#define HUC6280_TIMER_VECTOR	0xfffa		/* TIMER    P(I)によるマスク不可 <<要注意!!>> */
#define HUC6280_IRQ1_VECTOR	0xfff8		/* IRQ1/VDC P(I)によるマスク可能 */
#define HUC6280_IRQ2_VECTOR	0xfff6		/* IRQ2/BRK P(I)によるマスク可能 */

/*
 *	インストラクション関数宣言
 */
typedef                    void HUC6280OPPROC (HUC6280* huc6280, unsigned char code, int arg1, int arg2, int arg3);
#define HUC6280FN_(OP, FN) void huc6280op_##FN(HUC6280* huc6280, unsigned char code, int arg1, int arg2, int arg3);	/* 宣言用 */
#include "huc6280/list.h" /* [huc6280.xls]List */
HUC6280FN_("ILL_IMP", ILL_IMP)
#undef  HUC6280FN_
#define HUC6280FN_(    FN) void huc6280op_##FN(HUC6280* huc6280, unsigned char code, int arg1, int arg2, int arg3)	/* 定義用 */

/*
 *	オペコードマップ・ニーモニックテーブル
 */
typedef struct _HUC6280OP {
	HUC6280OPPROC* proc;	/* + 0, 4 */
	unsigned char addr;	/* + 4, 1 */
	unsigned char cycle;	/* + 5, 1 */
	unsigned short name;	/* + 6, 2 huc6280_op_name_table[]内の文字列へのオフセットです */
} HUC6280OP;			/* = 8    */
#define HUC6280OP_(CODE, MNEMONIC, ADDRMODE, FUNCTYPE, CYCLE, NAME)	{ huc6280op_##MNEMONIC##_##FUNCTYPE, HUC6280_##ADDRMODE, CYCLE, 0x##NAME },
extern const HUC6280OP huc6280_op_table[256/*opcode*/];
extern const char huc6280_op_name_table[];	/* ニーモニックテーブル */

//***** HUC6280OP.addr  *****	// Name                           | Syntax
//===== operand bytes=0 =====	//                                |
#define HUC6280_ACC	 0	// accumulator                    | a
#define HUC6280_IMP	 1	// implied                        |
//===== operand bytes=1 =====	//                                |
#define HUC6280_IMM	 2	// immediate                      | #$nn
#define HUC6280_ZPG	 3	// zero page                      | <$nn
#define HUC6280_ZPX	 4	// zero page, x-indexed           | <$nn,x
#define HUC6280_ZPY	 5	// zero page, y-indexed           | <$nn,y
#define HUC6280_ZPI	 6	// zero page indirect             | [$nn]
#define HUC6280_IDX	 7	// zero page pre-indexed          | [$nn,x]
#define HUC6280_IDY	 8	// zero page post-indexed         | [$nn],y
#define HUC6280_REL	 9	// relative offset                | *
//===== operand bytes=2 =====	//                                |
#define HUC6280_ABS	10	// absolute                       | $nnnn
#define HUC6280_ABX	11	// absolute x-indexed             | $nnnn,x
#define HUC6280_ABY	12	// absolute y-indexed             | $nnnn,y
#define HUC6280_IND	13	// indirect                       | [$nnnn]
#define HUC6280_IAX	14	// indirect, x-indexed            | [$nnnn,x]
#define HUC6280_IMZ	15	// immediate zero page            | #$nn,<$nn
#define HUC6280_IZX	16	// immediate zero page, x-indexed | #$nn,<$nn,x
#define HUC6280_ZRL	17	// zero page, relative offset     | <$nn,*
//===== operand bytes=3 =====	//                                |
#define HUC6280_IMA	18	// immediate absolute             | #$nn,$nnnn
#define HUC6280_IMX	19	// immediate absolute, x-indexed  | #$nn,$nnnn,x
//===== operand bytes=6 =====	//                                |
#define HUC6280_BLK	20	// block transfer                 | $nnnn,$nnnn,$nnnn

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

/* HuC6280リセット時に設定される、HuC6280コア内蔵の外部関数です。
 * アプリケーションから直接呼び出すことはありません。
 */
unsigned char huc6280_internal_read  (HUC6280* huc6280, unsigned short addr);
         void huc6280_internal_write (HUC6280* huc6280, unsigned short addr, unsigned char data);
         void huc6280_internal_mprset(HUC6280* huc6280, int addr/*0〜7*/, unsigned char data);
         void huc6280_internal_vdcout(HUC6280* huc6280, int addr/*0〜2*/, unsigned char data);

/* MPR[addr]に値を設定し、変化があったらMPR設定外部関数を呼び出します。
 */
void huc6280_mpr_set(HUC6280* huc6280, int addr/*0〜7*/, unsigned char data);

/* 保留されているマスク可能割り込みを検査し、条件が整っていたら割り込みを実行します。
 * CC(I,F)またはPENDINGが変化する可能性のある処理を行った後に、必ず呼び出してください。
 */
void huc6280_check_pending(HUC6280* huc6280);

/* 割り込みを実行します。
 */
void huc6280_int(HUC6280* huc6280, int vector, int brk);

/* 割り込みから復帰します。
 */
void huc6280_rti(HUC6280* huc6280);

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

/* HuC6280をリセットします。
 * [in]
 *	huc6280		HUC6280構造体。
 *	read		メインメモリREAD 外部関数 (NULL:コア内蔵関数を使用)
 *	write		メインメモリWRITE外部関数 (NULL:コア内蔵関数を使用)
 *	mprset		MPR更新外部関数           (NULL:コア内蔵関数を使用)
 *	vdcout		VDC出力外部関数           (NULL:コア内蔵関数を使用)
 * [note]
 *	* read,write,mprset,vdcoutにNULLを指定すると、コア内蔵の外部関数を使用します。
 *	  コア内蔵の外部関数は、次のような動作を行います。
 *	  ・read	HUC6280構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリを読み込みます。
 *	  ・write	HUC6280構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリに書き込みます。
 *	  ・mprset	何もしません。
 *	  ・vdcout	何もしません。
 */
void huc6280_reset(HUC6280* huc6280, HUC6280READPROC* read, HUC6280WRITEPROC* write, HUC6280MPRSETPROC* mprset, HUC6280VDCOUTPROC* vdcout);

/* HuC6280を実行します。
 * [in]
 *	huc6280		HUC6280構造体。
 *	cycle		実行サイクル数。
 * [note]
 *	* 実行サイクル数がcycleに達するか、またはHALT状態になるまで実行します。
 *	  前者の場合、実際の実行サイクル数は、cycleよりも多くなる可能性があります。
 *	  後者の場合、実際の実行サイクル数は、cycleよりも少なくなる可能性があります。
 *	* この関数から戻ったとき、huc6280->cycleに残り実行サイクル数が格納されています。
 *	  例えば cycle=100 で呼び出し、実際には 105 サイクル実行した場合、huc6280->cycle=-5 となります。
 *	* 本物のHuC6280はHALTに相当する命令を持っていないため、HALT状態も存在しません。
 *	  HALT状態は、本エミュレーションプログラムが独自に定義したものです。
 *	* HuC6280はHALT命令を持っていないので、別の方法を使ってHALT状態への移行を指示する必要があります。
 *	  例えば、メインメモリ空間空き領域の特定アドレスへのマジックナンバー出力をHALTと解釈し、
 *	  アプリケーション定義のWRITE外部関数からHUC6280.haltを1にセットする、等の方法を採ってください。
 */
void huc6280_run(HUC6280* huc6280, int cycle);

/* マスク不可割り込みリクエストを発行します。
 * [in]
 *	huc6280		HUC6280構造体。
 * [note]
 *	* 割り込みリクエストは即座に受け付けられます。
 *	  P(I)=1によってマスクされることはありません。
 */
void huc6280_nmi(HUC6280* huc6280);
void huc6280_timer(HUC6280* huc6280);

/* マスク可能割り込みリクエストを発行します。
 * [in]
 *	huc6280		HUC6280構造体。
 * [note]
 *	* P(I)=1の場合、割り込みリクエストは保留されます。
 *	  次にP(I)=0になった時に、割り込みが実行されます。
 */
void huc6280_irq1(HUC6280* huc6280);
void huc6280_irq2(HUC6280* huc6280);

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

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

#endif /*__CLIP_C62_H__*/
