/*	
 *	clipm68.h
 *
 *	P/ECE M6800 Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Feb 01 06:23:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_M68_H__
#define __CLIP_M68_H__

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

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

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

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

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

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

#ifdef M6800_TRACE
#define M6800TRACE	TRACE
#else /*M6800_TRACE*/
#define M6800TRACE	1 ? ((void)0) : TRACE
#endif /*M6800_TRACE*/

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

typedef struct _M6800 M6800;

/*
 *	M6800コア外部関数の型定義
 */
typedef unsigned char M6800READPROC (M6800* m6800, unsigned short addr);
typedef          void M6800WRITEPROC(M6800* m6800, unsigned short addr, unsigned char data);

/*
 *	M6800コア
 */
/*typedef*/ struct _M6800 {
	/*===== レジスタ =====*/
	unsigned char cc;		/* + 0, 1 */
	unsigned char _resv1[1];	/* + 1, 1 */
	unsigned short d;		/* + 2, 2 */
	unsigned short x;		/* + 4, 2 */
	unsigned short s;		/* + 6, 2 */
	unsigned short pc;		/* + 8, 2 */
	/*===== 内部状態 =====*/
	unsigned char pending;		/* +10, 1 */
	unsigned char wait;		/* +11, 1 */
	int cycle;			/* +12, 4 */
	/*===== 外部関数 =====*/
	M6800READPROC* read;		/* +16, 4 */
	M6800WRITEPROC* write;		/* +20, 4 */
} /*M6800*/;				/* =24    */

/* M6800.pending */
//#define M6800_RESET_PENDING	マスク不可
//#define M6800_TRAP_PENDING	マスク不可
//#define M6800_NMI_PENDING	マスク不可
//#define M6800_SWI_PENDING	マスク不可
#define M6800_IRQ1_PENDING	(1 << 0)	/* 高 */
#define M6800_ICI_PENDING	(1 << 1)	/* ↑ */
#define M6800_OCI_PENDING	(1 << 2)	/* 優 */
#define M6800_TOI_PENDING	(1 << 3)	/* 先 */
#define M6800_CMI_PENDING	(1 << 4)	/* 度 */
#define M6800_IRQ2_PENDING	(1 << 5)	/* ↓ */
#define M6800_SIO_PENDING	(1 << 6)	/* 低 */

/*
 *	リセット・割り込みベクタ
 */
#define M6800_RESET_VECTOR	0xfffe		/* #RES                                             */
#define M6800_NMI_VECTOR	0xfffc		/* #NMI                                             */
#define M6800_SWI_VECTOR	0xfffa		/* Software Interrupt                               */
#define M6800_IRQ1_VECTOR	0xfff8		/* #IRQ1                                            */
#define M6800_ICI_VECTOR	0xfff6		/* Timer 1 Input Capture  (M6801/03/HD6301/03 only) */
#define M6800_OCI_VECTOR	0xfff4		/* Timer 1 Output Capture (M6801/03/HD6301/03 only) */
#define M6800_TOI_VECTOR	0xfff2		/* Timer 1 Overflow       (M6801/03/HD6301/03 only) */
#define M6800_SIO_VECTOR	0xfff0		/* RDRF+ORFE+TDRE+PER     (M6801/03/HD6301/03 only) */
#define M6800_TRAP_VECTOR	0xffee		/* TRAP                            (HD6301/03 only) */
#define M6800_CMI_VECTOR	0xffec		/* Timer 2 Counter Match           (HD6301/03 only) */
#define M6800_IRQ2_VECTOR	0xffea		/* #IRQ2                           (HD6301/03 only) */

/*
 *	インストラクション関数宣言
 */
typedef                  void M6800OPPROC (M6800* m6800, int code, int arg);
#define M6800FN_(OP, FN) void m6800op_##FN(M6800* m6800, int code, int arg);	/* 宣言用 */
#include "m6800/list.h" /* [m6800.xls]List */
M6800FN_("ERR_INHERENT", ERR_INHERENT)
#undef  M6800FN_
#define M6800FN_(    FN) void m6800op_##FN(M6800* m6800, int code, int arg)	/* 定義用 */

/*
 *	オペコードマップ・インストラクション名テーブル
 */
typedef struct _M6800OP {
	M6800OPPROC* proc;	/* + 0, 4 */
	unsigned char addr;	/* + 4, 1 */
	unsigned char cycle;	/* + 5, 1 */
	unsigned short name;	/* + 6, 2 m6800_op_name_table[]内の文字列へのオフセットです */
} M6800OP;			/* = 8    */
#define M6800OP_(CODE, MNEMONIC, ADDRMODE, FUNCTYPE, CYCLE, NAME)	{ m6800op_##MNEMONIC##_##FUNCTYPE, M6800_##ADDRMODE, CYCLE, 0x##NAME },
extern const M6800OP m6800_op_table[256/*opcode*/];
extern const char m6800_op_name_table[];	/* インストラクション名テーブル */

/***** M6800OP.addr *****/
/*==== operand bytes=0 ====*/
#define M6800_INHERENT		0
/*==== operand bytes=1 ====*/
#define M6800_IMMEDIATE1	1
#define M6800_DIRECT		2
#define M6800_INDEXED		3
#define M6800_RELATIVE		4
/*==== operand bytes=2 ====*/
#define M6800_IMMEDIATE2	5
#define M6800_EXTENDED		6

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

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

/* 保留されている割り込みをチェックし、実行します。
 *
 * 処理内容：
 * 1. 割り込みが許可(CC(I)=0)されていて
 * 2. マスク可能割り込みのいずれかが保留されていたら、
 * 3. いちばん優先度の高い割り込みを実行し、
 * 4. 実行した割り込みの保留をクリアします。
 *
 * 使い方：
 * 次のいずれかの条件に該当する場合に、必ず呼び出してください。
 * 1. CC(I)が 1:禁止→0:許可 に変換する可能性のある命令を実行した直後。
 *    この条件に該当する命令は、次のとおりです。
 *	・RTI	PULL(CC)を含むから
 *	・CLI	CC(I)に0を設定するから。
 *	・TAP	CCにAの値をコピーするから。
 * 2. 割り込み保留ビットをセットした直後。
 *    割り込みが許可されていたら、ただちに割り込みを実行できるからです。
 *	・m6800_irq1()
 *	・m6800_ici()
 *	・m6800_oci()
 *	・m6800_toi()
 *	・m6800_sio()
 *	・m6800_cmi()
 *	・m6800_irq2()
 */
void m6800_check_pending(M6800* m6800);

/* 割り込みを実行します。
 * m6800_nmi()関数、SWI命令、およびm6800_check_pending()関数から利用します。
 */
void m6800_int(M6800* m6800, int vector);

/* 割り込みから復帰します。
 * RTI命令から利用します。
 */
void m6800_rti(M6800* m6800);

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

/* M6800をリセットします。
 * [in]
 *	m6800		M6800構造体。
 *	read		メインメモリREAD 外部関数 (NULL:コア内蔵関数を使用)
 *	write		メインメモリWRITE外部関数 (NULL:コア内蔵関数を使用)
 * [note]
 *	* read,writeにNULLを指定すると、コア内蔵の外部関数を使用します。
 *	  コア内蔵の外部関数は、次のような動作を行います。
 *	  ・READ	M6800構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリを読み込みます。
 *	  ・WRITE	M6800構造体の直後に64KBのメインメモリが配置されていると仮定し、メインメモリに書き込みます。
 */
void m6800_reset(M6800* m6800, M6800READPROC* read, M6800WRITEPROC* write);

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

/* マスク不可割り込みリクエストを発行します。
 * [in]
 *	m6800		M6800構造体。
 */
void m6800_nmi(M6800* m6800);

/* マスク可能割り込みリクエストを発行します。
 * [in]
 *	m6800		M6800構造体。
 * [note]
 *	* 割り込みが禁止されていたら、割り込みリクエストは保留されます。
 *	  割り込みが許可されたときに、割り込み処理を実行します。
 */
void m6800_irq1(M6800* m6800);
void m6800_ici(M6800* m6800);	/* M6801/03/HD6301/03 only */
void m6800_oci(M6800* m6800);	/* M6801/03/HD6301/03 only */
void m6800_toi(M6800* m6800);	/* M6801/03/HD6301/03 only */
void m6800_sio(M6800* m6800);	/* M6801/03/HD6301/03 only */
void m6800_cmi(M6800* m6800);	/*          HD6301/03 only */
void m6800_irq2(M6800* m6800);	/*          HD6301/03 only */

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

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

#endif /*__CLIP_M68_H__*/
