/*	
 *	main.cpp
 *
 *	dmalloc - malloc dump utility
 *	Copyright (C) 2002-2005 Naoyuki Sawa
 *
 *	* Tue Jul  2 21:24:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Jul  3 21:11:00 JST 2002 Naoyuki Sawa
 *	- ヒープ情報探索領域を、アプリケーションヘッダ〜BSS終了アドレスまでに限定。
 *	  前のアプリケーションのヒープ情報がクリアされずに残っていた場合、
 *	  それを間違って検出してしまっていた問題が、解消されました。
 *	* Sun Jul  7 07:00:00 JST 2002 Naoyuki Sawa
 *	- 統計情報の出力を追加。
 *	- 出力制御オプションの追加。
 *	- デバイス選択の追加。
 *	* Mon Jul 15 21:51:00 JST 2002 Naoyuki Sawa
 *	- SRAM読み込み前のポーズ要求発行後、実際にアプリケーションがポーズ状態になるのを待っていなかった。
 *	  非常に希なタイミングで、メモリ割り当て途中の不安定な状態のSRAM内容を読んでしまう可能性があった。
 *	  アプリケーションがポーズ状態になるのを待つように修正しました。
 *	* Wed Jun 16 06:52:00 JST 2004 Naoyuki Sawa
 *	- P/ECE API pceHeapAlooc()用ヒープ情報表示を追加しました。
 *	  これまでは、EPSONライブラリmalloc()用ヒープ情報だけを表示していました。
 *	* Wed Dec 29 22:57:00 JST 2004 Naoyuki Sawa
 *	- スタック情報表示機能を追加しました。
 *	  2004/12/29以降のCLiPライブラリを利用しているアプリケーションの、スタック情報を表示可能です。
 *	* Wed Jan 05 06:00:00 JST 2005 Naoyuki Sawa
 *	- スタックをFRAM4領域へ切り替えたままyield()する場合があるので、ssp、uspの検査を外しました。
 *	  旧バージョンでは、上述のような状況で、STACKINFOの検出に失敗していました。
 */
#define STRICT
#include <windows.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <pieceif.h>
#pragma comment(lib,"pieceif.lib")

#define VERSION		"20050105"

/****************************************************************************
 *	定数・型
 ****************************************************************************/

#define FRAM_TOP	0x0000	/* 高速RAM先頭アドレス */
#define FRAM_END	0x2000	/* 高速RAM終端アドレス */

typedef struct tagSYSTEMINFO { /* piece.h参照 */
	unsigned short size;
	unsigned short hard_ver;
	unsigned short bios_ver;
	unsigned short bios_date;
	unsigned long sys_clock;
	unsigned short vdde_voltage;
	unsigned short resv1;
	unsigned long sram_top;
	unsigned long sram_end;
	unsigned long pffs_top;
	unsigned long pffs_end;
} SYSTEMINFO;

typedef struct _pceAPPHEAD { /* piece.h参照 */
	unsigned long signature;     /* 'pCeA'固定。ただし実行開始時にAppInit()内で0クリアされます */
	unsigned short sysver;       /* システムバージョン（非ゼロ、詳細な値は仮定できない）       */
	unsigned short resv1;        /* 現在のスタートアップライブラリでは0固定（仮定できない）    */
	unsigned long initialize;    /* 初期化関数アドレス（非ゼロ必須、ハーフワードアライメント） */
	unsigned long periodic_proc; /* 処理関数アドレス（非ゼロ必須、ハーフワードアライメント） */
	unsigned long pre_terminate; /* 終了関数アドレス（非ゼロ必須、ハーフワードアライメント）   */
	unsigned long notify_proc;   /* 通知関数アドレス（非ゼロ必須、ハーフワードアライメント）   */
	unsigned long stack_size;    /* 現在のスタートアップライブラリでは0固定（仮定できない）    */
	unsigned long bss_end;       /* BSSの終了アドレス＝pceHeapAlloc()用ヒープ開始アドレス      */
	                             /*（非ゼロ。BIOS1.18はワードアライメントを想定しているが、    */
	                             /* リンカの-alignオプションで変更可能。仮定しない方が安全）   */
} pceAPPHEAD;
#define APPSTARTPOS1	0x100000 /* 通常のアプリケーションのヘッダ位置   */
#define APPSTARTPOS2	0x138000 /* 昔のスタートアップシェルのヘッダ位置 */

typedef struct tagHEAPMEM { /* piece.h参照 */
	unsigned short mark;		/* シグネチャ (HEAPMEMMK固定) */
	unsigned short owner;		/* 種別 (0:空き / HMO_DEFAULT_USER:通常割り当て / HMO_SYSTEM:システム予約)          */
					/* PieceSystem1.20現在、HMO_SYSTEMは使われていません。0またはHMO_DEFAULT_USERです。 */
	//struct tagHEAPMEM* chain;
	unsigned long chain;		/* 次のHEAPMEMのアドレス (NULL:終端) */
} HEAPMEM;
/* HEAPMEM.mark */
#define HEAPMEMMK		0xa431
/* HEAPMEM.owner */
#define HMO_DEFAULT_USER	1
#define HMO_SYSTEM		0xffff

typedef struct tagHEAPINFO { /* /use/piece/include/stdlib.h参照 */
	unsigned long start_alloc; /* ansi_InitMallocの第一引数保持  */
	unsigned long end_alloc;   /* ansi_InitMallocの第二引数保持  */
	unsigned long nxt_alc_p;   /* 空き領域の先頭アドレス         */
	unsigned long tbl_ptr;     /* 割り当てテーブルの先頭アドレス */
	unsigned long row;         /* 割り当てテーブルの要素数       */
} HEAPINFO;

typedef struct tagBLKTBL { /* 推測 */
	unsigned long ptr;	/* このブロックの先頭アドレス   */
	unsigned long len:31;	/* このブロックのサイズ[バイト] */
	unsigned long use: 1;	/* 0:空き/1:使用中              */
} BLKTBL;

/* * なぜかブロックテーブルは一要素余計で、
 *	tbl_ptr = (BLKTBL*)end_alloc - (row + 1)
 *   になっています。
 *   一番若いアドレスのテーブル要素にはゴミが入ってます。
 * * 隣り合うテーブル要素の指すブロックアドレスは連続しています。
 *	blktbl[n].ptr + blktbl[n].len = blktbl[n - 1].ptr
 *   が常に成立します。
 * * 上位アドレス(13FFFF側)のテーブル要素の方が、
 *   下位アドレス(100000側)のテーブル要素よりも先に割り当てられるので、
 *   ブロックアドレスの増加方向とテーブル要素アドレスの増加方向が
 *   逆であることに注意してください。
 */

typedef struct _STACKINFO { /* clip/clippce.h(2004/12/29時点)参照 */
	char signature[8];			/* + 0,8: シグネチャ"STACKINF"固定 */
	/* システムスタック */
	/* int* */ unsigned long sysstack_top;	/* + 8,4: 先頭アドレス */
	/* int* */ unsigned long sysstack_end;	/* +12,4: 終端アドレス */
	/* ユーザースタック */
	/* int* */ unsigned long usrstack_top;	/* +16,4: 先頭アドレス */
	/* int* */ unsigned long usrstack_end;	/* +20,4: 終端アドレス */
	/* スタックポインタ */
	/* int* */ unsigned long ssp;		/* +24,4: システムスタックポインタ */
	/* int* */ unsigned long usp;		/* +28,4: ユーザースタックポインタ */
} STACKINFO;					/* =32 */

/****************************************************************************
 *	変数
 ****************************************************************************/

char opt_heap;			/* ヒープ情報を出力します   */
char opt_block;			/* ブロック情報を出力します */
char opt_total;			/* 統計情報を出力します     */
char opt_stack;			/* スタック情報を出力します */

char devno;			/* P/ECEデバイス番号        */

SYSTEMINFO sysinfo;		/* システム情報                       */
char* mem;			/* 高速RAM+SRAMイメージ               */
int mem_len;			/* 高速RAM+SRAMサイズ                 */
int heaptop_addr;		/* pceHeapAlloc()用ヒープ先頭アドレス */
unsigned long apphead_addr;	/* アプリケーションヘッダアドレス     */

/****************************************************************************
 *	関数宣言
 ****************************************************************************/

void usage();
int mem_read();
unsigned long apphead_find();
unsigned long epson_heapinfo_find(unsigned long heapinfo_addr, unsigned long end_addr);
unsigned long clip_stackinfo_find(unsigned long stackinfo_addr, unsigned long end_addr);
void piece_heap_dump();
void epson_heap_dump(unsigned long heapinfo_addr);
void clip_stack_dump(unsigned long heapinfo_addr);

/****************************************************************************
 *	usage()
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "dmalloc - malloc dump utility (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2002-2005 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE: dmalloc [options] [devno]\n");
	fprintf(stderr, "    devno   P/ECEデバイス番号\n");
	fprintf(stderr, "            省略時は、#0（一台目のP/ECE）に接続します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "    -h      ヒープ情報を出力します。\n");
	fprintf(stderr, "    -b      ブロック情報を出力します。\n");
	fprintf(stderr, "    -t      統計情報を出力します。\n");
	fprintf(stderr, "    -s      スタック情報を出力します。\n");
	fprintf(stderr, "    -h、-b、-t、-sいずれも指定しなければ、全情報を出力します。\n");
	abort();
}

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
#define N_MAX	1024

	int retval, i, n;
	char* p;
	unsigned long addr, addrs[N_MAX];
	pceAPPHEAD* apphead;

	/* コマンドライン解析。 */
	for(i = 1; i < argc; i++) {
		p = argv[i];
		if(p[0] == '-') {
			/* オプション指定 */
			switch(p[1]) {
			case 'h':
				if(opt_heap) usage(); /* 二重指定 */
				opt_heap = 1;
				break;
			case 'b':
				if(opt_block) usage(); /* 二重指定 */
				opt_block = 1;
				break;
			case 't':
				if(opt_total) usage(); /* 二重指定 */
				opt_total = 1;
				break;
			case 's':
				if(opt_stack) usage(); /* 二重指定 */
				opt_stack = 1;
				break;
			default:
				usage();
			}
		} else {
			/* デバイス番号指定 */
			devno = (char)strtol(p, &p, 10);
			if(*p != '\0') usage(); /* 数字以外の文字で終端した */
		}
	}
	/* 出力選択されていなければ、すべて出力。 */
	if(!opt_heap && !opt_block && !opt_total) {
		opt_heap = opt_block = opt_total = opt_stack = 1;
	}

	/* 高速RAM+SRAMイメージを読み込みます。 */
	retval = mem_read();
	if(retval != 0) abort();

	/* アプリケーションヘッダを探します。 */
	apphead_addr = apphead_find();
	if(apphead_addr == 0) {
		fprintf(stderr, "アプリケションヘッダが見つかりません。\n");
		abort();
	}
	apphead = (pceAPPHEAD*)&mem[apphead_addr];

	/***** P/ECE APIのpceHeapAlloc()用ヒープ情報 *****/
	printf("\n"); /* 一行空ける */
	printf("#### P/ECE API pceHeapAlloc() Heap ###\n");

	/* ヒープ情報を表示します。 */
	piece_heap_dump();

	/***** EPSONライブラリのmalloc()用ヒープ情報 *****/
	/* ヒープ情報を探します。 */
	n = 0;
	addr = apphead_addr + sizeof(pceAPPHEAD); /* アプリケーションヘッダの直後から... */
	while(n < N_MAX) {
		addr = epson_heapinfo_find(addr, apphead->bss_end); /* ...BSS終了アドレスまで */
		if(addr == 0) break;
		addrs[n++] = addr;
		addr += 4;
	}
	if(n) {
		printf("\n"); /* 一行空ける */
		printf("##### EPSON Library malloc() Heap ####\n");

		if(n != 1) {
			fprintf(stderr, "### 警告！複数のヒープ情報が見つかりました。\n");
			/* 継続 */
		}

		/* ヒープ情報を表示します。 */
		for(i = 0; i < n; i++) {
			if(n != 1) printf("\n### 候補%d\n\n", i + 1);
			epson_heap_dump(addrs[i]);
		}
	} else {
		/* malloc()用ヒープがない(ansi_InitMalloc()が呼ばれてない)場合は、何も表示しません。 */
	}

	/***** CLiPライブラリのスタック情報 *****/
	/* スタック情報を探します。 */
	if(opt_stack) {
		n = 0;
		addr = apphead_addr + sizeof(pceAPPHEAD); /* アプリケーションヘッダの直後から... */
		while(n < N_MAX) {
			addr = clip_stackinfo_find(addr, apphead->bss_end); /* ...BSS終了アドレスまで */
			if(addr == 0) break;
			addrs[n++] = addr;
			addr += 4;
		}
		if(n) {
			printf("\n"); /* 一行空ける */
			printf("####### CLiP Library Stack Info ######\n");

			if(n != 1) {
				fprintf(stderr, "### 警告！複数のスタック情報が見つかりました。\n");
				/* 継続 */
			}

			/* スタック情報を表示します。 */
			for(i = 0; i < n; i++) {
				if(n != 1) printf("\n### 候補%d\n\n", i + 1);
				clip_stack_dump(addrs[i]);
			}
		} else {
			/* スタック情報が無い場合は、何も表示しません。
			 * スタック情報は、CLiP Library 2004/12/29 以降のアプリケーションに有ります。
			 */
		}
	}

	return 0;

#undef N_MAX
}

/****************************************************************************
 *	mem_read
 ****************************************************************************/

static int
ismAppPause(int pausef)
{
	unsigned char tmp[2];
	tmp[0] = 16;
	tmp[1] = (unsigned char)pausef;
	return ismCmdW(tmp, 2, 0, 0);
}

static int
ismLCDGetInfo(int* pstat, unsigned long* pbuff)
{
	int retval;
	unsigned char tmp[12];
	tmp[0] = 17;
	retval = ismCmdR(tmp, 1, tmp, 12);
	if(retval == 0) {
		if(pstat != NULL) *pstat = tmp[0];
		if(pbuff != NULL) *pbuff = *(unsigned long*)(tmp + 8);
	}
	return retval;
}

int
mem_read()
{
	int result, retval, ofs, len, stat, T0, T, dT;

	result = -1;

	/* P/ECEに接続します。 */
	retval = ismInitEx(devno, 0);
	if(retval != 0) {
		fprintf(stderr, "P/ECEに接続できません。\n");
		goto L10;
	}

	/* システム情報を取得します。 */
	retval = ismGetVersion(&sysinfo, 1);
	if(retval == PIECE_INVALID_VERSION) {
		fprintf(stderr, "システム情報が取得できません。\n");
		goto L20;
	}

	/* 高速RAM+SRAM全体を読み込むためのメモリを確保します。 */
	mem_len = sysinfo.sram_end > FRAM_END ?
	          sysinfo.sram_end : FRAM_END;
	mem = (char*)malloc(mem_len);
	if(mem == NULL) {
		fprintf(stderr, "メモリ不足です。\n");
		goto L20;
	}

	/* メモリ読み込み中に内容が変わらないよう、ポーズをかけます。 */
	retval = ismAppPause(1);
	if(retval != 0) {
		fprintf(stderr, "アプリケーションが応答しません。\n");
		goto L20;
	}

	/* アプリケーションが実際にポーズ状態になるまで待ちます。 */
	T0 = GetTickCount();
	for(;;) {
		retval = ismLCDGetInfo(&stat, NULL);
		if(retval != 0) goto L30;
		if(stat) break;
		T = GetTickCount();
		dT = T - T0;
		if(dT >= 1000) goto L30; /* 1秒でタイムアウト */
	}

	/* 高速RAMを読み込みます。
	 * ※一度の受信サイズが64KB超えたあたりから動作不安定になる傾向があり、
	 *   P/ECEがハングアップすることがあるので、分割受信することにしました。
	 *   バルク転送の安全サイズは4KBと聞いたことがあるので、4KBにしました。
	 */
	ofs = FRAM_TOP;
	while(ofs < FRAM_END) {
		len = FRAM_END - ofs;
		if(len > 0x1000) len = 0x1000; /* 4KB制限 */
		retval = ismReadMem(mem + ofs, ofs, len);
		if(retval != 0) {
			fprintf(stderr, "高速RAMの読み込みに失敗しました。\n");
			goto L30;
		}
		ofs += len;
	}

	/* SRAMを読み込みます。
	 * ※一度の受信サイズが64KB超えたあたりから動作不安定になる傾向があり、
	 *   P/ECEがハングアップすることがあるので、分割受信することにしました。
	 *   バルク転送の安全サイズは4KBと聞いたことがあるので、4KBにしました。
	 */
	ofs = sysinfo.sram_top;
	while(ofs < (int)sysinfo.sram_end) {
		len = sysinfo.sram_end - ofs;
		if(len > 0x1000) len = 0x1000; /* 4KB制限 */
		retval = ismReadMem(mem + ofs, ofs, len);
		if(retval != 0) {
			fprintf(stderr, "SRAMの読み込みに失敗しました。\n");
			goto L30;
		}
		ofs += len;
	}

	/* pceHeapAlloc()用ヒープ先頭アドレスを取得します。 */
	retval = ismHeapGetAdrs(&heaptop_addr);
	if(retval != 0) {
		fprintf(stderr, "pceHeapAlloc()用ヒープ先頭アドレスが取得できません。\n");
		goto L30;
	}

	result = 0; /* 読み込み成功 */

L30:
	/* ポーズを解除します。 */
	ismAppPause(0);
L20:
	/* P/ECEから切断します。 */
	ismExit();
L10:
	return result;
}

/****************************************************************************
 *	apphead_find
 ****************************************************************************/

unsigned long
apphead_find()
{
	unsigned long addrs[] = { APPSTARTPOS1, APPSTARTPOS2 };
	unsigned long addr;
	int i;
	pceAPPHEAD* apphead;

	for(i = 0; i < sizeof addrs / sizeof *addrs; i++) {
		addr = addrs[i];
		if(addr < sysinfo.sram_top || sysinfo.sram_end < addr + sizeof(pceAPPHEAD)) continue; /* ヘッダがSRAMをはみ出してる */
		apphead = (pceAPPHEAD*)&mem[addr];

		if(apphead->signature != 0) continue; /* シグネチャがクリアされていない */
		if(apphead->sysver == 0) continue; /* システムバージョンが設定されていない */
		if(apphead->bss_end < sysinfo.sram_top || sysinfo.sram_end < apphead->bss_end) continue; /* BSS終了アドレスがSRAM外 */

		/* 各エントリポイントは、命令の１バイト目を指しているので、ハーフワードアライメントされているはず。
		 * また、srfファイルのセクション配置は、{ pceAPPHEAD〜codeセクション〜dataセクション〜bssセクション〜bss_end } なので、
		 * 各エントリポイントは、少なくとも、pceAPPHEADの直後からbss_endの間にあるはずです。
		 * 以上、二つの条件を、四つのエントリポイントに対して検査します。
		 */
		if(apphead->initialize    & 1 || apphead->initialize    < addr + sizeof(pceAPPHEAD) || apphead->bss_end <= apphead->initialize   ) continue;
		if(apphead->periodic_proc & 1 || apphead->periodic_proc < addr + sizeof(pceAPPHEAD) || apphead->bss_end <= apphead->periodic_proc) continue;
		if(apphead->pre_terminate & 1 || apphead->pre_terminate < addr + sizeof(pceAPPHEAD) || apphead->bss_end <= apphead->pre_terminate) continue;
		if(apphead->notify_proc   & 1 || apphead->notify_proc   < addr + sizeof(pceAPPHEAD) || apphead->bss_end <= apphead->notify_proc  ) continue;

		/* APPSTARTPOS1が有効なら、APPSTARTPOS2は調べません。通常のアプリケーションは、APPSTARTPOS1でヒットするはずです。
		 * APPSTARTPOS2を使っているのはごく初期のスタートアップシェルだけで、しかもそれはmalloc()を使っていません。
		 * 通常のアプリケーションでAPPSTARTPOS2を調べても、間違ってゴミにヒットするだけだと思います。
		 */
		return addr;
	}

	return 0;
}

/****************************************************************************
 *	epson_heapinfo_find
 ****************************************************************************/

unsigned long
epson_heapinfo_find(unsigned long heapinfo_addr, unsigned long end_addr)
{
	int i;
	unsigned long blktbl_addr;
	unsigned long ptr;
	HEAPINFO hi;
	BLKTBL bt;

	for( ; heapinfo_addr <= end_addr - sizeof(HEAPINFO); heapinfo_addr += 4) {
		memcpy(&hi, &mem[heapinfo_addr], sizeof(HEAPINFO));

		/***** HEAPINFO単独の検査 *****/

		if(hi.start_alloc & 3) continue; /* ヒープ開始アドレスのアライメントエラー */
		if(hi.nxt_alc_p & 3) continue;   /* 空き領域先頭アドレスのアライメントエラー */
		if(hi.tbl_ptr & 3) continue;     /* 割り当てテーブル先頭アドレスのアライメントエラー */
		if(hi.end_alloc & 3) continue;   /* ヒープ終了アドレスのアライメントエラー */

		if(hi.start_alloc < sysinfo.sram_top) continue; /* SRAM先頭〜ヒープ先頭   */
		if(hi.start_alloc > hi.nxt_alc_p) continue;     /*         〜空き領域先頭 */
		if(hi.nxt_alc_p > hi.tbl_ptr) continue;         /*         〜テーブル先頭 */
		if(hi.tbl_ptr > hi.end_alloc) continue;         /*         〜終了アドレス */
		if(hi.end_alloc > sysinfo.sram_end) continue;   /*         〜SRAM終端 の順であること */

		if(hi.tbl_ptr != hi.end_alloc - sizeof(BLKTBL) * (hi.row + 1)) continue; /* 「定数・型」セクションのコメント参照 */

		/* * HEAPINFO単独の検査だけで、たいてい、正解は１つに絞られます。
		 *   多くのメモリブロックが割り当てられているほど、正解の確率は高くなりますが、
		 *   実際には、ansi_InitMalloc()で初期化しただけでも、ただ１つの正解が得られました。
		 * * 念のため、引き続いて、ブロックテーブルの検査も行うことにします。
		 */

		/***** BLKTBLの検査 *****/

		ptr = hi.start_alloc; /* 最初のブロックはヒープ先頭にあるはず */
		for(i = 0; i < (int)hi.row; i++) {
			blktbl_addr = hi.end_alloc - sizeof(BLKTBL) * (i + 1);
			memcpy(&bt, &mem[blktbl_addr], sizeof(BLKTBL));
			if(bt.ptr != ptr) break; /* 前のブロックと連続していない     */
			if(bt.len & 3) break;    /* 割り当て単位のアライメントエラー */
			ptr += bt.len;           /* 次のブロックアドレスを求めておく */
			if(ptr < hi.start_alloc || hi.end_alloc < ptr) break; /* ブロックサイズエラー */
			/* ptr.useは検査してはいけません。未使用ブロックが複数連続することはありえます。
			 * 連続した未使用ブロックをくっつけるのは、毎回ではなく、4連続ぐらいの時に行われるようです。
			 */
		}
		if(i != (int)hi.row) continue; /* ブロックの途中で検査失敗した */

		return heapinfo_addr; /* このヒープ情報は正しい */
	}

	return 0; /* もう見つからなかった */
}

/****************************************************************************
 *	clip_stackinfo_find
 ****************************************************************************/

unsigned long
clip_stackinfo_find(unsigned long stackinfo_addr, unsigned long end_addr)
{
	STACKINFO si;

	for( ; stackinfo_addr <= end_addr - sizeof(STACKINFO); stackinfo_addr += 4) {
		memcpy(&si, &mem[stackinfo_addr], sizeof(STACKINFO));

		/***** STACKINFO単独の検査 *****/

		if(memcmp(si.signature, "STACKINF", 8) != 0) continue;	/* シグネチャエラー */
		if(si.sysstack_top & 3) continue;			/* システムスタック先頭アドレスのアライメントエラー */
		if(si.sysstack_end & 3) continue;			/* システムスタック終端アドレスのアライメントエラー */
		if(si.usrstack_top & 3) continue;			/* ユーザースタック先頭アドレスのアライメントエラー */
		if(si.usrstack_end & 3) continue;			/* ユーザースタック終端アドレスのアライメントエラー */
		if(si.ssp & 3) continue;				/* ユーザースタックポインタのアライメントエラー */
		if(si.usp & 3) continue;				/* ユーザースタックポインタのアライメントエラー */

#if 0
//		if((FRAM_TOP <= si.sysstack_top &&		/* 高速RAM先頭〜システムスタック先頭     */
//		    si.sysstack_top <= si.ssp &&		/*            〜システムスタックポインタ */
//		    si.ssp <= si.sysstack_end &&		/*            〜システムスタック終端     */
//		    si.sysstack_end <= FRAM_END)		/*            〜高速RAM終端       または */
//		|| (sysinfo.sram_top <= si.sysstack_top &&	/* SRAM先頭   〜システムスタック先頭     */
//		    si.sysstack_top <= si.ssp &&		/*            〜システムスタックポインタ */
//		    si.ssp <= si.sysstack_end &&		/*            〜システムスタック終端     */
//		    si.sysstack_end <= sysinfo.sram_end)) {	/*            〜SRAM終端          ならば */
//			/** no job **/				/* 継続                                  */
//		} else {					/*                            さもなくば */
//			continue;				/* エラー                                */
//		}
//
//		if((FRAM_TOP <= si.usrstack_top &&		/* 高速RAM先頭〜ユーザースタック先頭     */
//		    si.usrstack_top <= si.usp &&		/*            〜ユーザースタックポインタ */
//		    si.usp <= si.usrstack_end &&		/*            〜ユーザースタック終端     */
//		    si.usrstack_end <= FRAM_END)		/*            〜高速RAM終端       または */
//		|| (sysinfo.sram_top <= si.usrstack_top &&	/* SRAM先頭   〜ユーザースタック先頭     */
//		    si.usrstack_top <= si.usp &&		/*            〜ユーザースタックポインタ */
//		    si.usp <= si.usrstack_end &&		/*            〜ユーザースタック終端     */
//		    si.usrstack_end <= sysinfo.sram_end)) {	/*            〜SRAM終端          ならば */
//			/** no job **/				/* 継続                                  */
//		} else {					/*                            さもなくば */
//			continue;				/* エラー                                */
//		}
#else //↓2005/01/05 スタックをFRAM4領域へ切替たままyield()する場合があるので、ssp、uspの検査を外しました。
		if((FRAM_TOP <= si.sysstack_top &&		/* 高速RAM先頭〜システムスタック先頭     */
		    si.sysstack_top  <= si.sysstack_end &&	/*            〜システムスタック終端     */
		    si.sysstack_end  <= FRAM_END)		/*            〜高速RAM終端       または */
		|| (sysinfo.sram_top <= si.sysstack_top &&	/* SRAM先頭   〜システムスタック先頭     */
		    si.sysstack_top  <= si.sysstack_end &&	/*            〜システムスタック終端     */
		    si.sysstack_end  <= sysinfo.sram_end)) {	/*            〜SRAM終端          ならば */
			/** no job **/				/* 継続                                  */
		} else {					/*                            さもなくば */
			continue;				/* エラー                                */
		}

		if((FRAM_TOP <= si.usrstack_top &&		/* 高速RAM先頭〜ユーザースタック先頭     */
		    si.usrstack_top  <= si.usrstack_end &&	/*            〜ユーザースタック終端     */
		    si.usrstack_end  <= FRAM_END)		/*            〜高速RAM終端       または */
		|| (sysinfo.sram_top <= si.usrstack_top &&	/* SRAM先頭   〜ユーザースタック先頭     */
		    si.usrstack_top  <= si.usrstack_end &&	/*            〜ユーザースタック終端     */
		    si.usrstack_end  <= sysinfo.sram_end)) {	/*            〜SRAM終端          ならば */
			/** no job **/				/* 継続                                  */
		} else {					/*                            さもなくば */
			continue;				/* エラー                                */
		}
#endif

		return stackinfo_addr; /* このスタック情報は正しい */
	}

	return 0; /* もう見つからなかった */
}

/****************************************************************************
 *	piece_heap_dump
 ****************************************************************************/

void
piece_heap_dump()
{
	int i;
	unsigned long phm;
	unsigned long len;
	unsigned long free_cnt = 0;
	unsigned long free_sum = 0;
	unsigned long free_max = 0;
	unsigned long free_min = 0;
	unsigned long user_cnt = 0;
	unsigned long user_sum = 0;
	unsigned long user_max = 0;
	unsigned long user_min = 0;
	unsigned long system_cnt = 0;
	unsigned long system_sum = 0;
	unsigned long system_max = 0;
	unsigned long system_min = 0;
	unsigned long unknown_cnt = 0;
	unsigned long unknown_sum = 0;
	unsigned long unknown_max = 0;
	unsigned long unknown_min = 0;
	HEAPMEM hm;

	/***** ヒープ情報の表示 *****/

	if(opt_heap) {
		printf("============= ヒープ情報 =============\n");
		printf("--------: heaptop           = %08x\n", heaptop_addr);
	}

	/***** ブロック情報の表示 *****/

	if(opt_block) {
		printf("============ ブロック情報 ============\n");
		printf("Index     Address   Size      Status  \n");
	}
	phm = heaptop_addr;
	i = 0;
	for(;;) {
		/* メモリブロック情報を取得します。 */
		if(phm < sysinfo.sram_top || phm + sizeof(HEAPMEM) > sysinfo.sram_end) { /* メモリ外 */
			fprintf(stderr, "### Heap broken !!\n");
			break;
		}
		memcpy(&hm, &mem[phm], sizeof(HEAPMEM));
		if(hm.mark != HEAPMEMMK) { /* シグネチャ不正 */
			fprintf(stderr, "### Heap broken !!\n");
			break;
		}
		if(!hm.chain) break;
		len = hm.chain - (phm + sizeof(HEAPMEM));
		if((long)len < 0) { /* サイズ不正 */
			fprintf(stderr, "### Heap broken !!\n");
			break;
		}

		/* メモリブロック情報を表示し、統計情報を収集します。 */
		switch(hm.owner) {
		case 0:
			if(opt_block) printf("%08x  %08x  %08x  %-8s\n", i, phm + sizeof(HEAPMEM), len, "FREE");
			free_cnt++;
			free_sum += len;
			if(free_cnt == 1 || len > free_max) free_max = len;
			if(free_cnt == 1 || len < free_min) free_min = len;
			break;
		case HMO_DEFAULT_USER:
			if(opt_block) printf("%08x  %08x  %08x  %-8s\n", i, phm + sizeof(HEAPMEM), len, "USER");
			user_cnt++;
			user_sum += len;
			if(user_cnt == 1 || len > user_max) user_max = len;
			if(user_cnt == 1 || len < user_min) user_min = len;
			break;
		case HMO_SYSTEM:
			if(opt_block) printf("%08x  %08x  %08x  %-8s\n", i, phm + sizeof(HEAPMEM), len, "SYSTEM");
			system_cnt++;
			system_sum += len;
			if(system_cnt == 1 || len > system_max) system_max = len;
			if(system_cnt == 1 || len < system_min) system_min = len;
			break;
		default:
			if(opt_block) printf("%08x  %08x  %08x  (%04x)\n", i, phm + sizeof(HEAPMEM), len, hm.owner);
			unknown_cnt++;
			unknown_sum += len;
			if(unknown_cnt == 1 || len > unknown_max) unknown_max = len;
			if(unknown_cnt == 1 || len < unknown_min) unknown_min = len;
			break;
		}

		/* 次のメモリブロックへ。 */
		phm = hm.chain;
		i++;
	}

	/***** 統計情報の表示 *****/

	if(opt_total) {
		printf("============== 統計情報 ==============\n");
		printf("             個数  最小   最大   合計 \n");
		printf("USERブロック %4d %6d %6d %6d\n", user_cnt,    user_min,    user_max,    user_sum   );
		printf("FREEブロック %4d %6d %6d %6d\n", free_cnt,    free_min,    free_max,    free_sum   );
		if(system_cnt) {
			printf("SYSTEMブロック%3d %6d %6d %6d\n", system_cnt,  system_min,  system_max,  system_sum );
		}
		if(unknown_cnt) {
			printf("不明ブロック %4d %6d %6d %6d\n", unknown_cnt, unknown_min, unknown_max, unknown_sum);
		}
	}
}

/****************************************************************************
 *	epson_heap_dump
 ****************************************************************************/

void
epson_heap_dump(unsigned long heapinfo_addr)
{
	int i;
	unsigned long blktbl_addr;
	unsigned long heap_mem = 0;
	unsigned long extd_mem = 0;
	unsigned long used_cnt = 0;
	unsigned long used_sum = 0;
	unsigned long used_max = 0;
	unsigned long used_min = 0;
	unsigned long free_cnt = 0;
	unsigned long free_sum = 0;
	unsigned long free_max = 0;
	unsigned long free_min = 0;
	HEAPINFO hi;
	BLKTBL bt;

	/***** ヒープ情報の表示 *****/

	memcpy(&hi, &mem[heapinfo_addr], sizeof(HEAPINFO));
	if(opt_heap) {
		printf("============= ヒープ情報 =============\n");
		printf("%08x: ansi_ucStartAlloc = %08x\n", heapinfo_addr +  0, hi.start_alloc);
		printf("%08x: ansi_ucEndAlloc   = %08x\n", heapinfo_addr +  4, hi.end_alloc  );
		printf("%08x: ansi_ucNxtAlcP    = %08x\n", heapinfo_addr +  8, hi.nxt_alc_p  );
		printf("%08x: ansi_ucTblPtr     = %08x\n", heapinfo_addr + 12, hi.tbl_ptr    );
		printf("%08x: ansi_ulRow        = %08x\n", heapinfo_addr + 16, hi.row        );
	}

	/***** ブロック情報の表示 *****/

	if(opt_block) {
		printf("============ ブロック情報 ============\n");
		printf("Index     Address   Size      Status  \n");
	}
	for(i = 0; i < (int)hi.row; i++) {
		blktbl_addr = hi.end_alloc - sizeof(BLKTBL) * (i + 1);
		memcpy(&bt, &mem[blktbl_addr], sizeof(BLKTBL));
		if(opt_block) {
			printf("%08x  %08x  %08x  %-8s\n", i, bt.ptr, bt.len, bt.use ? "USE" : "FREE");
		}

		/* 統計情報の収集。 */
		if(bt.use) {
			used_cnt++;
			used_sum += bt.len;
			if(used_cnt == 1 || bt.len > used_max) used_max = bt.len;
			if(used_cnt == 1 || bt.len < used_min) used_min = bt.len;
		} else {
			free_cnt++;
			free_sum += bt.len;
			if(free_cnt == 1 || bt.len > free_max) free_max = bt.len;
			if(free_cnt == 1 || bt.len < free_min) free_min = bt.len;
		}
	}

	/***** 統計情報の表示 *****/

	/* ヒープ領域サイズを求めます。 */
	heap_mem = hi.end_alloc - hi.start_alloc;

	/* 未使用領域サイズを求めます。
	 * * 未使用領域から割当可能な最大ブロックサイズは、未使用領域サイズ-8バイトまでです。
	 *   新しいブロックを割り当てるために、テーブル拡張用に8バイト余計に必要だからです。
	 * * このプログラムは、割当可能サイズを表示するのが目的ではなく、
	 *   単にブロックサイズを表示するのが目的なので、8バイト分は考慮しないことにしました。
	 */
	extd_mem = hi.tbl_ptr - hi.nxt_alc_p/* - 8*/;

	if(opt_total) {
		printf("============== 統計情報 ==============\n");
		printf("             個数  最小   最大   合計 \n");
		printf(" ヒープ領域   --    --     --   %6d\n", heap_mem);
		printf(" 未使用領域   --    --     --   %6d\n", extd_mem);
		printf("使用ブロック %4d %6d %6d %6d\n", used_cnt, used_min, used_max, used_sum);
		printf("空きブロック %4d %6d %6d %6d\n", free_cnt, free_min, free_max, free_sum);
	}
}

/****************************************************************************
 *	clip_stack_dump
 ****************************************************************************/

void
clip_stack_dump(unsigned long heapinfo_addr)
{
	STACKINFO si;
	int top;
	int end;
	int pos;
	int len;
	int use;
	int per;

	memcpy(&si, &mem[heapinfo_addr], sizeof(STACKINFO));

	printf("========= スタックレイアウト =========\n");
	printf("%08x: sysstack_top      = %08x\n", heapinfo_addr +  8, si.sysstack_top);
	printf("%08x: sysstack_end      = %08x\n", heapinfo_addr + 12, si.sysstack_end);
	printf("%08x: usrstack_top      = %08x\n", heapinfo_addr + 16, si.usrstack_top);
	printf("%08x: usrstack_end      = %08x\n", heapinfo_addr + 20, si.usrstack_end);
	printf("%08x: ssp               = %08x\n", heapinfo_addr + 24, si.ssp);
	printf("%08x: usp               = %08x\n", heapinfo_addr + 28, si.usp);

	printf("========== 推定使用バイト数 ==========\n");
	printf("                 使用量   総容量   ％ \n");
	/* システムスタック */
	top = si.sysstack_top;
	end = si.sysstack_end;
	len = end - top;
	for(pos = top; pos < end; pos += sizeof(int)) {
		if(*(int*)&mem[pos] != pos) break;
	}
	use = end - pos;
	per = len ? use * 100 / len : 0;
	printf("システムスタック %6d / %6d (%3d)\n", use, len, per);
	/* ユーザースタック */
	top = si.usrstack_top;
	end = si.usrstack_end;
	len = end - top;
	for(pos = top; pos < end; pos += sizeof(int)) {
		if(*(int*)&mem[pos] != pos) break;
	}
	use = end - pos;
	per = len ? use * 100 / len : 0;
	printf("ユーザースタック %6d / %6d (%3d)\n", use, len, per);
}

