File:  [Isaki's NoNo m68k/m88k emulator] / nono / lib / branchhistory.cpp
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:04:40 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v006, HEAD
nono 0.1.2

//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//

// ブランチ履歴

#include "branchhistory.h"
#include "m68030core.h"
#include "m88100.h"
#include "mystring.h"

// コンストラクタ
BranchHistory::BranchHistory()
{
	monitor_size = nnSize(54, 33);
}

// デストラクタ
BranchHistory::~BranchHistory()
{
}

// 初期化
void
BranchHistory::Clear()
{
	top = 0;
	for (int i = 0; i < countof(entry); i++) {
		BranchEntry& e = entry[i];
		e.count = 0;
		e.from  = 0xffffffff;
		e.to    = 0xffffffff;
		e.inst  = 0;
	}
}

// 使用中のエントリ数を取得する
int
BranchHistory::GetUsed() const
{
	int i = 0;

	// top の位置から一巡するまで。有効エントリは count > 0。
	for (; i < 256; i++) {
		if (entry[(256 + top - i) % 256].count == 0) {
			break;
		}
	}
	return i;
}

// モニタ更新
void
BranchHistory::MonitorUpdate(TextScreen& monitor)
{
	uint8 t;
	int y;
	int ydelta;
	int row;

	// userdata は表示開始位置と各種フラグ。
	// 下位 32bit が表示開始位置で、0 なら先頭のエントリから、1 なら先頭の
	// 一つ次のエントリから、…を表す (表示行数は monitor の高さ分)。
	// ExHist が %1 なら例外履歴、%0 ならブランチ履歴。これによって
	// ヘッダを変えたい。
	// BottomToTop が %1 なら並び順を下から上に変える (コンソール用)。
	bool is_exhist = (monitor.userdata & ExHist);
	bool bottom_to_top = (monitor.userdata & BottomToTop);
	int pos = (int)monitor.userdata;

	// 0         1         2         3         4         5         6
	// 0123456789012345678901234567890123456789012345678901234567890123456789
	// No. FromAddr Instruction          ToAddr    Iteration
	// 001 $01234567(01234567:bcnd.n) -> $01234567 x123456789
	// 001 $01234567(0123:trap )      -> $01234567 x123456789
	// 002 $01234567<  2> Bus Error
	// 002 $01234567< 69> MFP Timer-C
	//                    01234567890123456789012

	monitor.Clear();
	row = monitor.GetRow();

	// 最初の1行は常にヘッダ
	if (is_exhist) {
		monitor.Print(0, 0, "No. FromAddr  Vec. Exception");
		monitor.Print(44, 0, "Iteration");
	} else {
		monitor.Print(0, 0, "No. FromAddr Instruction");
		monitor.Print(34, 0, "ToAddr    Iteration");
	}

	// pos は最新を 0 とした通し番号。(スクロールしてたら開始が 0 とは限らない)
	// t が entry[] 上の現在位置。
	// y は表示座標。(上から表示か下から表示かが変わる)
	t = top - pos;
	int pos_end = pos + row - 1;
	if (bottom_to_top == false) {
		y = 1;
		ydelta = 1;
	} else {
		y = row - 1;
		ydelta = -1;
	}
	for (; pos < pos_end; pos++, y += ydelta) {
		BranchEntry& e = entry[t--];
		if (e.count == 0) {
			continue;
		}
		std::string str = FormatEntry(e);
		monitor.Print(0, y, "%3d $%08x%s", pos, e.from, str.c_str());
		if (e.count > 1) {
			monitor.Print(44, y, "x%9u", e.count);
		}
	}
}



//
// m680x0 ブランチ履歴 (どこに置くのがよいか)
//

// m680x0 ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによる
// ジャンプの3つを区別する。
//
// 通常の分岐:
//	from は分岐元 PC、to は分岐先 PC、inst の上位16ビットに命令の1ワード目。
//	inst の $8000 が立っていないことで区別する。
//
// 例外発生:
//	from は例外発生時の PC、to は不問(通常 0 をセットすること)、
//	inst は $00008LVV で $8000 が例外フラグ、VV がベクタ番号である。
//	ベクタ番号が $00 だと次項(ベクタによる分岐)と区別つかなくなってしまう
//	ため、リセット例外 (ベクタ番号0) は V=$01 で記録する。
//	L は割り込みなら割り込みレベル。
//	inst に $8000 が立っていて、かつ $8000 と一致しないことで区別する。
//
// 例外ベクタによる分岐 (例外処理の最後に起きる):
//	from はベクタアドレス、to は分岐先 (0 も起こりえる)、inst は $00008000。
//	inst が $00008000 と一致することで区別する (ビットが立っているではない)。

// コンストラクタ
BranchHistory_m680x0::BranchHistory_m680x0()
{
}

// デストラクタ
BranchHistory_m680x0::~BranchHistory_m680x0()
{
}

// 1エントリ分の表示内容作成
std::string
BranchHistory_m680x0::FormatEntry(const BranchEntry& e)
{
	std::string desc;

	if ((e.inst & 0x8000)) {
		if ((e.inst & 0x7fff) != 0) {
			// 例外発生記録
			// 発生アドレスとベクタ(と割り込みレベル)を表示
			uint32 vector = e.inst & 0xff;
			if (vector == 1) {
				vector = 0;
			}
			// とりあえず
			desc = string_format("<%3d> %s", vector,
				m68030_get_exception_name(vector));
			// XXX TODO 割り込みレベル
		} else {
			// 例外ベクタフェッチしてジャンプ
			desc = string_format("(vector fetch)    -> $%08x", e.to);
		}
	} else {
		// ブランチ
		const char *mnemonic;
		uint32 inst = e.inst >> 16;
		if (inst == 0x4e73) {
			mnemonic = ":rte";
		} else if (inst == 0x4e75) {
			mnemonic = ":rts";
		} else if (inst == 0x4e77) {
			mnemonic = ":rtr";
		} else if ((inst & 0xffc0) == 0x4e80) {
			mnemonic = ":jsr";
		} else if ((inst & 0xffc0) == 0x4ec0) {
			mnemonic = ":jmp";
		} else if ((inst & 0xfff8) == 0x51c8) {
			mnemonic = ":dbra";
		} else if ((inst & 0xf0f8) == 0x50c8) {
			mnemonic = ":dbcc";
		} else if ((inst & 0xff00) == 0x6000) {
			mnemonic = ":bra";
		} else if ((inst & 0xff00) == 0x6100) {
			mnemonic = ":bsr";
		} else if ((inst & 0xf000) == 0x6000) {
			mnemonic = ":bcc";
		} else if ((inst & 0xfff8) == 0xf248) {
			mnemonic = ":fdbcc";
		} else if ((inst & 0xff80) == 0xf280) {
			mnemonic = ":fbcc";
		} else {
			mnemonic = "";
		}
		desc = string_format("(%04x%-6s)      -> $%08x", inst, mnemonic, e.to);
	}
	return desc;
}


//
// m88xx0 ブランチ履歴 (どこに置くのがよいか)
//

// m88xx0 ブランチ履歴では、通常ブランチ、例外発生の2つを区別する。
// m68k は例外ベクタに書いてあるアドレスに飛ぶのでもう1種類分けてあったが、
// m88k は例外ベクタを直接実行するのでそれは不要。
//
// 通常の分岐:
//	from は分岐元 XIP、to は分岐先アドレス、inst は命令ワード。
//
// 例外発生:
//	from は例外発生時の XIP、to は 0。inst は $fc000VVV で VVV (9ビット)が
//	ベクタ番号。命令ワードとは衝突しない (instruction.txt 参照)。

// コンストラクタ
BranchHistory_m88xx0::BranchHistory_m88xx0()
{
}

// デストラクタ
BranchHistory_m88xx0::~BranchHistory_m88xx0()
{
}

// 1エントリ分の表示内容作成
std::string
BranchHistory_m88xx0::FormatEntry(const BranchEntry& e)
{
	std::string desc;

	if (e.inst >= 0xfc000000 && e.to == 0) {
		// 例外発生記録
		// 発生アドレスとベクタを表示
		uint32 vector = e.inst & 0x1ff;
		const char *name = m88kcpu::GetExceptionName(vector);
		desc = string_format("<%3d> %s", vector, name ?: "");
		if (vector == 450) {
			// OpenBSD のシステムコール番号
			desc += string_format("(%d)", (e.inst >> 12) & 0xfff);
		}
	} else if (e.inst >= 0xc0000000) {
		// ブランチ
		const char *mnemonic = "";
		uint32 up4 = (e.inst >> 26) & 0xf;
		switch (up4) {
		 case 0:	mnemonic = ":br";		break;
		 case 1:	mnemonic = ":br.n";		break;
		 case 2:	mnemonic = ":bsr";		break;
		 case 3:	mnemonic = ":bsr.n";	break;
		 case 4:	mnemonic = ":bb0";		break;
		 case 5:	mnemonic = ":bb0.n";	break;
	 	 case 6:	mnemonic = ":bb1";		break;
		 case 7:	mnemonic = ":bb1.n";	break;

		 case 0xa:	mnemonic = ":bcnd";		break;
		 case 0xb:	mnemonic = ":bcnd.n";	break;
		 case 0xc: {
			uint32 lo6 = (e.inst >> 10) & 0x3f;
			if (lo6 == 0x34) {
				mnemonic = ":tb0";
			} else if (lo6 == 0x36) {
				mnemonic = ":tb1";
			} else if (lo6 == 0x3a) {
				mnemonic = ":tcnd";
			}
			break;
		 }
		 case 0xd: {
			uint32 lo6 = (e.inst >> 10) & 0x3f;
			if (lo6 == 0x30) {
				if ((e.inst & 0x1f) == 1) {
					mnemonic = ":jmp r1";
				} else {
					mnemonic = ":jmp";
				}
			} else if (lo6 == 0x31) {
				mnemonic = ":jmp.n";
			} else if (lo6 == 0x32) {
				mnemonic = ":jsr";
			} else if (lo6 == 0x33) {
				mnemonic = ":jsr.n";
			} else if (lo6 == 0x3f) {
				mnemonic = ":rte";
			}
			break;
		 }
		 case 0xe:	mnemonic = ":tbnd";	break;
		 default:
			break;
		}
		desc = string_format("(%08x%-7s) -> $%08x", e.inst, mnemonic, e.to);
	}
	return desc;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.