/*	
 *	clipsrf.h
 *
 *	ld33互換ローダ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Mon Jul 13 18:26:06 JST 2009 Naoyuki Sawa
 *	- 1st リリース。
 *	  ld33の仕様は、EPSON開発ツール(ccEVv40.exe)のドキュメント(/cc33/UTILITY/ld33)を参照してください。
 *	  同じものを、keep/ld33.7zにも入れてあります。
 *	* Tue Jul 14 23:40:10 JST 2009 Naoyuki Sawa
 *	- ソースコード例を追記しました。
 *	  プログラムは変更ありません。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_SRF_H__
#define __CLIP_SRF_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/*****************************************************************************
 *	グローバル関数
 *****************************************************************************/

/* ローダ用srf33ファイルイメージを、リロケーションします。
 * [in]
 *	srf		RAM上の、ローダ用srf33ファイルイメージ。
 *	symbol		symbolサーチ関数。
 *			ローダ用srf33ファイルがエクスターン情報を含んでいない場合は、NULLを指定できます。
 *	user_data	symbolサーチ関数に渡される、任意の引数。
 * [note]
 *	- ローダ用srf33ファイルイメージを直接書き換えて、リロケーション処理を行ないます。
 *	  書き換えを行なうために、ローダ用srf33ファイルイメージはRAM上である必要があります。
 *	  フラッシュメモリ上に置いた、ローダ用srf33ファイルイメージを指定することはできません。
 *	- ローダ用srf33ファイルイメージは、4バイト境界にアライメントしたRAM上にロードしてください。
 *	  4バイト境界にアライメントされていない場合、エラー停止します。
 *	- ローダ用srf33ファイルがエクスターン情報を含んでいない場合は、symbolにNULLを指定できます。
 *	  ローダ用srf33ファイルがエクスターン情報を含んでいる場合は、symbolにNULLを指定すると、エラー停止します。
 *	- srf_init()は、bssセクションのメモリを確保します。
 *	  使い終わったら、srf_free()を呼んで、bssセクションのメモリを開放してください。
 * <使用例>
 *	void sample() {
 *		FILE* fp;
 *		int len;
 *		void* srf;
 *		// ローダ用srf33ファイルイメージを、メモリを確保して読み込む
 *		fp = fopen("module.srf", "rb");
 *		fseek(fp, 0, SEEK_END);
 *		len = ftell(fp);
 *		rewind(fp);
 *		srf = malloc(len);
 *		fread(srf, 1, len, fp);
 *		fclose(fp);
 *		// リロケーションする。この中で、bssセクションのメモリが確保される
 *		srf_init(srf, symbol, 0);
 *		...
 *		// (モジュールを使用)
 *		...
 *		// bssセクションのメモリを開放する。これ以降、srfを再利用してはいけない
 *		srf_free(srf);
 *		// ローダ用srf33ファイルイメージを読み込んだメモリを開放する
 *		free(srf);
 *	}
 *	// モジュールが、struprとstrlwrを外部参照している場合の、symbolサーチ関数例。
 *	static void* symbol(const char* name, int user_data) {
 *		if(!strcmp(name, "strupr")) return strupr;
 *		if(!strcmp(name, "strlwr")) return strlwr;
 *		DIE(); // 予期しないsymbolサーチが要求されたら、エラー停止させる
 *	}
 */
void srf_init(void* srf, void* (*symbol)(const char* name, int user_data), int user_data);

/* リロケーション時に確保した、bssセクションを開放します。
 * [in]
 *	srf		RAM上の、ローダ用srf33ファイルイメージ。
 * [note]
 *	- srf_free()が開放するのは、srf_init()が確保したbssセクションのメモリだけです。
 *	  ローダ用srf33ファイルイメージのメモリは、開放しません。
 *	  ローダ用srf33ファイルイメージのメモリは、必要に応じて、アプリケーションが開放してください。
 *	- 一つのローダ用srf33ファイルイメージに対して、srf_init()とsrf_free()は一回づつしか呼べません。
 *	  srf_free()を呼んだsrf33ファイルイメージに対して、srf_init()を呼んで再利用することはできません。
 *	  codeセクションやbssセクションの実データが、書き換えられている可能性があるからです。
 *	- srf_init()を呼ぶ前にsrf_free()を呼んだり、複数回srf_free()を呼んではいけません。
 *	  不正なsrf_init()やsrf_free()の呼び出しは、エラー停止でなく、ハングアップする可能性があります。
 */
void srf_free(void* srf);

/* codeセクションの実行時アドレスを取得します。
 * [out]
 *	戻り値		codeセクションの実行時アドレス。
 *			codeセクションが無い場合は、NULLを返します。
 * [note]
 *	- ローダ用srf33ファイルには、エクスポートを行なう仕組みが有りません。
 *	  codeセクションの先頭に、アプリケーション定義のエクスポートテーブルを配置するなどの方法が必要です。
 *	  そうした場合、本関数を使用して、エクスポートテーブルの先頭アドレスを取得することができます。
 */
void* srf_code(void* srf);

/* dataセクションの実行時アドレスを取得します。
 * [out]
 *	戻り値		dataセクションの実行時アドレス。
 *			dataセクションが無い場合は、NULLを返します。
 */
void* srf_data(void* srf);

/* bssセクションの実行時アドレスを取得します。
 * [out]
 *	戻り値		bssセクションの実行時アドレス。
 *			bssセクションが無い場合は、NULLを返します。
 */
void* srf_bss(void* srf);

/*****************************************************************************
 *	ソースコード例
 *****************************************************************************/

#if 0 //======================================================================

//----------------------------------------------------------------------------
ローダ用srf33ファイルのソースコード例を示します。
//----------------------------------------------------------------------------
// module.c

#include <stdio.h>

int get_num();
void print_str(int i);

//エクスポートテーブル
// codeセクションの先頭に配置するために、const指定が必須
// const指定を忘れると、dataセクションに配置されてしまう
const struct {
	int (*get_num)();
	void (*print_str)(int i);
} exp_tbl = {
	get_num,   // 関数アドレスに、リロケーションタイプ10を使用
	print_str, // 関数アドレスに、リロケーションタイプ10を使用
};

// テスト関数1: 1ずつ増える値を返す
int get_num() {
	static int n;
	return n++;
}

// テスト関数2: str_tbl[i]の文字列を表示する
void print_str(int i) {
	extern const char* const str_tbl[];
	// str_tblとputsは、エクスターン参照
	// str_tblをレジスタへロードする部分に、リロケーションタイプ7を使用
	// putsを呼び出す部分に、リロケーションタイプ2を使用
	puts(str_tbl[i]);
}
//----------------------------------------------------------------------------

ローダ用srf33ファイルのソースコードは、以下のようにコンパイルしてください。

	pcc33 -Wall -O2 -lld -nodefsup -nodeflib -b module.c

各オプションの意味は、以下のとおりです。

	-Wall		警告レベルの設定。通常のコンパイルと同じ。
	-O2		最適化レベルの設定。通常のコンパイルと同じ。
	-lld		通常のsrf33ファイルでなく、ローダ用srf33ファイルを生成する。
	-nodefsup	スタートアップオブジェクトをリンクしない。
	-nodeflib	ライブラリをリンクしない。
	-b		警告が発生しても、生成したファイルを削除しないために指定。
			ローダ用srf33ファイルのリンクにおいては、「Warning: Unresolved external symbol '〜'」が発生しても正常です。
			未解決のエクスターンシンボルは、リロケーション処理を行なうときに解決するからです。
			しかしpcc33は、警告が発生すると生成したファイルを削除するため、正常であるsrfファイルも削除されてしまいます。
			上記の警告は抑制できないので、警告が発生してもファイルを削除しないよう、-bオプションを指定することにします。

以下のオプションを指定*しない*よう、注意してください。

	-gp=0x0		グローバルポインタによる最適化が行なわれ、@ah,@alが生成されます。
			ローダ用srf33ファイルの仕様では、@ah,@alのリロケーションは行なえません。
			このタイプのリロケーションが存在すると、リンカは「Warning: Invalid loader information.」の警告を表示します。
	-near		2命令での拡張分岐(ext命令1個+分岐命令)が生成されます。
			ローダ用srf33ファイルの仕様では、3命令での拡張分岐(ext命令2個+分岐命令)のリロケーションのみ許されており、
			2命令での拡張分岐(ext命令1個+分岐命令)のリロケーションは行なえません。
			2命令での拡張分岐(ext命令1個+分岐命令)が存在しても、リンカは警告も表示せず、リロケーション情報が欠落します。
			見つけづらいバグの原因となりますので、このオプションを指定しないよう、注意してください。

//----------------------------------------------------------------------------
ローダ用srf33ファイルを利用するプログラム例を示します。
//----------------------------------------------------------------------------
// main.c (ローダ用srf33ファイルを利用するプログラムの部分のみ)

// module.srfから、エクスターン参照される
// symbolサーチ関数がアドレスを返すので、globalである必要は無い
static const char* const str_tbl[] = { "Zero", "One", "Two" };

// symbolサーチ関数
static void* symbol(const char* name, int user_data) {
	if(!strcmp(name, "str_tbl")) {
		return (void*)str_tbl;
	}
	if(!strcmp(name, "puts")) {
		return puts;
	}
	DIE();
}

void sample() {
	FILE* fp;
	int len;
	void* srf;
	const struct {
		int (*get_num)();
		void (*print_str)(int i);
	} *exp_tbl;
	int n;

	// ローダ用srf33ファイルを、メモリを確保してロードする
	fp = fopen("module.srf", "rb");
	fseek(fp, 0, SEEK_END);
	len = ftell(fp);
	rewind(fp);
	srf = malloc(len);
	fread(srf, 1, len, fp);
	fclose(fp);
	// リロケーション処理を行い、bssセクションを確保する
	srf_init(srf, symbol, 0);
	// エクスポートテーブルを取得する
	exp_tbl = srf_code(srf);

	n = exp_tbl->get_num(); // n = 0
	exp_tbl->print_str(n);  // "Zero"をstderrに出力
	n = exp_tbl->get_num(); // n = 1
	exp_tbl->print_str(n);  // "One"をstderrに出力
	n = exp_tbl->get_num(); // n = 2
	exp_tbl->print_str(n);  // "Two"をstderrに出力

	// bssセクションを開放する
	srf_free(srf);
	// ローダ用srf33ファイルのメモリを開放する
	free(srf);
}
//----------------------------------------------------------------------------

ローダ用srf33ファイルを利用するプログラムは、通常のアプリケーションの一部としてコンパイルしてください。

#endif //=====================================================================

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_SRF_H__*/

