/*	
 *	clipssm.h
 *
 *	P/ECE S-SMP (SPC700) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Jan 04 21:33:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#ifndef __CLIP_SSM_H__
#define __CLIP_SSM_H__

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

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

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

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

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

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

#ifdef SSMP_TRACE
#define SSMPTRACE	TRACE
#else /*SSMP_TRACE*/
#define SSMPTRACE	1 ? ((void)0) : TRACE
#endif /*SSMP_TRACE*/

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

typedef struct _SSMP SSMP;

/*
 *	SSMPコア外部関数の型定義
 */
typedef unsigned char SSMPREADPROC (SSMP* ssmp, unsigned short addr);
typedef          void SSMPWRITEPROC(SSMP* ssmp, unsigned short addr, unsigned char data);

/*
 *	SSMPコア
 */
/*typedef*/ struct _SSMP {
	/*===== レジスタ =====*/
	unsigned char  psw;		/* + 0, 1 */
	unsigned char  sp;		/* + 1, 1 */
	unsigned short pc;		/* + 2, 2 */
	unsigned short ya;		/* + 4, 2 */
	unsigned char  x;		/* + 6, 1 */
	unsigned char  resv1[1];	/* + 7, 1 */
	/*===== 内部状態 =====*/
	int cycle;			/* + 8, 4 */
	unsigned char  sleep;		/* +12, 1 */
	unsigned char  resv2[3];	/* +13, 3 */
	/*===== 外部関数 =====*/
	SSMPREADPROC*  read;		/* +16, 4 */
	SSMPWRITEPROC* write;		/* +20, 4 */
} /*SSMP*/;				/* =24    */

/*
 *	インストラクション関数宣言
 */
typedef                 void SSMPOPPROC (SSMP* ssmp, unsigned char code, int arg1, int arg2);
#define SSMPFN_(OP, FN) void ssmpop_##FN(SSMP* ssmp, unsigned char code, int arg1, int arg2);	/* 宣言用 */
#include "ssmp/list.h" /* [ssmp.xls]List!C6 */
SSMPFN_("ERROR", ERROR)
#undef  SSMPFN_
#define SSMPFN_(    FN) void ssmpop_##FN(SSMP* ssmp, unsigned char code, int arg1, int arg2)	/* 定義用 */

/*
 *	オペコードマップ・ニーモニックテーブル
 */
typedef struct _SSMPOP {
	SSMPOPPROC* proc;	/* + 0, 4 */
	unsigned char addr;	/* + 4, 1 */
	unsigned char cycle;	/* + 5, 1 */
	unsigned short name;	/* + 6, 2 ssmp_op_name_table[]内の文字列へのオフセットです */
} SSMPOP;			/* = 8    */
#define SSMPOP_(CODE, PROC, ADDR, CYCLE, NAME)	{ ssmpop_##PROC, SSMP_##ADDR, CYCLE, 0x##NAME },
extern const SSMPOP ssmp_op_table[256/*opcode*/];
extern const char ssmp_op_name_table[];	/* ニーモニックテーブル */

/***** SSMPOP.addr *****/
/*==== operand bytes=0 ====*/
#define SSMP_IMPLIED				 0	/* A,X,Y,etc */
#define SSMP_INDIRECT				 1	/* (X) */
#define SSMP_INDIRECT_AUTO_INCREMENT		 2	/* (X)+ */
#define SSMP_INDIRECT_PAGE_TO_IP		 3	/* (X),(Y) */
/*==== operand bytes=1 ====*/
#define SSMP_INMEDIATE_DATA			 4	/* #inm */
#define SSMP_DIRECT_PAGE			 5	/* dp */
#define SSMP_X_INDEXED_DIRECT_PAGE		 6	/* dp+X */
#define SSMP_Y_INDEXED_DIRECT_PAGE		 7	/* dp+Y */
#define SSMP_X_INDEXED_INDIRECT			 8	/* (dp+X) */
#define SSMP_INDIRECT_Y_INDEXED_INDIRECT	 9	/* (dp)+Y */
#define SSMP_RELATIVE				10	/* rel */
/*==== operand bytes=2 ====*/
#define SSMP_DIRECT_PAGE_TO_DP			11	/* dp(d),dp(s) */
#define SSMP_INMEDIATE_DATA_TO_DP		12	/* dp,#inm */
#define SSMP_DIRECT_PAGE_RELATIVE		13	/* dp,rel */
#define SSMP_ABSOLUTE_BOOLEAN_BIT		14	/* mem.bit */
#define SSMP_ABSOLUTE				15	/* labs */
#define SSMP_X_INDEXED_ABSOLUTE			16	/* labs+X */
#define SSMP_Y_INDEXED_ABSOLUTE			17	/* labs+Y */
#define SSMP_X_INDEXED_ABSOLUTE_INDIRECT	18	/* (labs+X) */
#define SSMP_X_INDEXED_DIRECT_PAGE_RELATIVE	19	/* dp+X,rel */

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

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

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

/* 割り込み関連のアプリケーション用関数は用意していません。
 * 本物のSPC700は割り込み機能を持っていますが、S-SMPは割り込み機能を使っていないからです。
 */

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

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

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

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

#endif /*__CLIP_SSM_H__*/
