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

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

//
// ブランチ履歴
//

#include "branchhistory.h"
#include "m680x0disasm.h"
#include "m88100.h"
#include "monitor.h"
#include "mpu64180.h"
#include "vectortable.h"

// コンストラクタ
BranchHistory::BranchHistory(uint objid_)
	: inherited(objid_)
{
	int monitor_id;

	switch (GetId()) {
	 case OBJ_MPU_BRHIST:
		is_exhist = false;
		monitor_id = ID_SUBWIN_BRHIST;
		break;
	 case OBJ_MPU_EXHIST:
		is_exhist = true;
		monitor_id = ID_SUBWIN_EXHIST;
		break;
	 case OBJ_XP_BRHIST:
		is_exhist = false;
		monitor_id = ID_SUBWIN_XPBRHIST;
		break;
	 case OBJ_XP_EXHIST:
		is_exhist = true;
		monitor_id = ID_SUBWIN_XPEXHIST;
		break;
	 default:
		PANIC("unknown id %s", GetIdStr());
	}

	// ログは不要
	ClearAlias();

	vectortable = GetVectorTable();

	monitor = gMonitorManager->Regist(monitor_id, this);
	monitor->SetCallback(&BranchHistory::MonitorScreen);
	monitor->SetSize(55, 1 + 32); // ヘッダ1行とエントリ数
	monitor->SetMaxHeight(1 + 256);

	// hd64180 はこれを上書きする
	x_count = 45;
}

// デストラクタ
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.info  = 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::MonitorScreen(Monitor *, TextScreen& screen)
{
	uint8 t;
	int y;
	int ydelta;
	int row;

	// userdata は表示開始位置と各種フラグ。
	// 下位 32bit が表示開始位置で、0 なら先頭のエントリから、1 なら先頭の
	// 一つ次のエントリから、…を表す (表示行数は screen の高さ分)。
	// BottomToTop が %1 なら並び順を下から上に変える (コンソール用)。
	bool bottom_to_top = (screen.userdata & BottomToTop);
	int pos = (int)screen.userdata;

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

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

	// 最初の1行は常にヘッダ
	std::string header = FormatHeader();
	screen.Puts(0, 0, header.c_str());

	// 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);
		screen.Print(0, y, "%3u %s", pos, str.c_str());
		if (e.count > 1) {
			screen.Print(x_count, y, "x%9u", e.count);
		}
	}
}

// ヘッダ文字列を返す。(m68k, m88k 共通)
std::string
BranchHistory::FormatHeader() const
{
	if (is_exhist) {
		//      012345678901234567890123456789012345678901234567890123456789
		return "No. FromAddr   Vec. Exception                Iteration";
	} else {
		//      012345678901234567890123456789012345678901234567890123456789
		return "No. FromAddr  Instruction          ToAddr    Iteration";
	}
}


//
// m680x0 ブランチ履歴
//

// m680x0 ブランチ履歴では、通常ブランチ、例外発生 (IOCS コールと
// それ以外)、例外ベクタによるジャンプの4つを区別する。
//
// 1. 通常の分岐:
//	from は上位31ビットが分岐元 PC、最下位ビットが S/U。
//	to   は32ビット全体が分岐先 PC。
//	info は上位2ビットが %00 (Normal)、下位16ビットに分岐元命令の1ワード目。
//
//	   3                   2                   1                   0
//	 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//	+-+-+-----------+---------------+---------------+---------------+
//	|0 0|           0               |       Instruction Word        |
//	+-+-+-----------+---------------+---------------+---------------+
//
// 2. 例外発生 (IOCS/DOS コール以外の場合):
//	from は上位31ビットが例外発生時の PC、最下位ビットが S/U。
//	to   は不問だが 0 にすること。
//	info は上位2ビットが %11 (Exception)、最下位バイトがベクタ番号 (0-255)。
//	例外が TRAP#15 だが IOCS コールっぽくない場合と、
//	例外が F ライン例外だが DOS コールっぽくない場合もこちら。
//
//	+-+-+-----------+---------------+---------------+---------------+
//	|1 1|           0               |       0       |   Vector No.  |
//	+-+-+-----------+---------------+---------------+---------------+
//
// 3. 例外発生 (各種サービスコールの場合):
//	from, to は同じ。
//	info は上位2ビットが %10 (ServiceCall)。
//	info の上位ワードが OS 種別。
//	  $0 なら Human68k
//	  $1 なら NetBSD/m68k
//	Human68k なら下位ワードは
//	  IOCS コールなら、b8 が %0 で b7-b0 に IOCS コール番号。
//	  DOS  コールなら、b8 が %1 で b7-b0 に DOS コール番号。
//	NetBSD/m68k なら下位ワードはシステムコール番号。
//
//	+-+-+-----------+-------------+-+---------------+---------------+
//	|1 0|                         $0|      0      |0| IOCS Call No. |
//	+-+-+-----------+-------------+-+---------------+---------------+
//	+-+-+-----------+-------------+-+---------------+---------------+
//	|1 0|                         $0|      -      |1|  DOS Call No. |
//	+-+-+-----------+-------------+-+---------------+---------------+
//	+-+-+-----------+-------------+-+---------------+---------------+
//	|1 0|                         $1|        System Call No.        |
//	+-+-+-----------+-------------+-+---------------+---------------+
//
// 4. 例外ベクタによる分岐 (例外処理の最後に起きる):
//	from はベクタアドレス、
//	to   は分岐先 (0 も起こりえる)、
//	info は上位2ビットが %01 (VectorJump)。
//
//	+-+-+-----------+---------------+---------------+---------------+
//	|0 1|                           0                               |
//	+-+-+-----------+---------------+---------------+---------------+

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

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

// 1エントリ分の表示内容作成
std::string
BranchHistory_m680x0::FormatEntry(const BranchEntry& e)
{
	// m68k では from の最下位1ビットを S/U として使う
	std::string desc = string_format("%c.%08x",
		(e.from & 1) ? 'S' : 'U', (e.from & ~1));

	switch (e.info >> 30) {
	 case 0:	// 通常分岐
	 {
		const char *mnemonic;
		uint32 inst = e.info & 0xffff;
		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);
		break;
	 }

	 case 1:	// 例外ベクタフェッチによるジャンプ
		// 例外ベクタフェッチしてジャンプ
		desc += string_format("(vector fetch)    -> $%08x", e.to);
		break;

	 case 2:	// 例外発生 (IOCS/DOS コール)
	 {
		uint vector;
		if (((e.info >> 16) & 0x01) == 0) {
			// Human68k、IOCS/DOS コール
			const char *call;
			const char *name;
			uint num = e.info & 0xff;
			if ((e.info & 0x0100) == 0) {
				call = "IOCS";
				name = m680x0disasm::GetIOCSName(num);
				vector = M68K::EXCEP_TRAP15;
			} else {
				call = "DOS";
				name = m680x0disasm::GetDOSName(num);
				vector = M68K::EXCEP_FLINE;
			}
			if (name != NULL) {
				desc += string_format("<%3u> %s %s", vector, call, name);
			} else {
				desc += string_format("<%3u> %s $%02x", vector, call, num);
			}
		} else {
			// NetBSD/m68k システムコール
			vector = M68K::EXCEP_TRAP0;
			const char *name = vectortable->GetExceptionName(vector);
			uint num = e.info & 0xffff;
			desc += string_format("<%3u> %s (syscall %u)", vector, name, num);
		}
		break;
	 }

	 case 3:	// 例外発生 (IOCS/DOS コール以外)
	 {
		uint32 vector = e.info & 0xff;
		const char *name = vectortable->GetExceptionName(vector);
		desc += string_format("<%3u> %s", vector, name ?: "");
		break;
	 }

	 default:
		__unreachable();
	}
	return desc;
}


//
// m88xx0 ブランチ履歴
//

// m88xx0 ブランチ履歴では、通常ブランチ、例外発生の2つを区別する。
// m68k は例外ベクタに書いてあるアドレスに飛ぶのでもう1種類分けてあったが、
// m88k は例外ベクタを直接実行するのでそれは不要。
//
// 通常の分岐:
//	from は上位30ビットが分岐元 XIP、最下位ビットが S/U、
//	to   は32ビット全体が分岐先アドレス、
//	info は命令ワード。
//
// 例外発生:
//	from は上位30ビットが例外発生時の XIP、最下位ビットが S/U、
//	to   は不問。
//	info は、9 ビットのベクタ番号と、ベクタが OpenBSD/m88k システムコール
//	の場合はシステムコール番号を含む。
//	   3                   2                   1                   0
//	 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//	+---------------+---------------+-------+-----+-+---------------+
//	|1 1 0 0 0 0 0 0|   System Call No.     |0 0 0|  Vector Number  |
//	+---------------+---------------+-------+-----+-+---------------+
//	命令ワードとは衝突しない (instruction.txt 参照)。

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

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

// 1エントリ分の表示内容作成
std::string
BranchHistory_m88xx0::FormatEntry(const BranchEntry& e)
{
	// m88k では from の最下位1ビットを S/U として使う
	std::string desc = string_format("%c.%08x",
		(e.from & 1) ? 'S' : 'U', (e.from & ~1));

	if (e.info >= 0xfc000000) {
		// 例外発生記録
		// 発生アドレスとベクタを表示
		uint32 vector = e.info & 0x1ff;
		const char *name = vectortable->GetExceptionName(vector);
		desc += string_format("<%3u> %s", vector, name ?: "");
		if (vector == 128 || vector == 450) {
			// 450 なら OpenBSD のシステムコール番号を追加。
			// 非公式 NetBSD/luna88k は(今の所?) 128 を使ってるようだ。
			desc += string_format("(%u)", (e.info >> 12) & 0xfff);
		}
	} else if (e.info >= 0xc0000000) {
		// ブランチ
		const char *mnemonic = "";
		uint32 up4 = (e.info >> 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.info >> 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.info >> 10) & 0x3f;
			if (lo6 == 0x30) {
				if ((e.info & 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.info, mnemonic, e.to);
	}
	return desc;
}


//
// XP ブランチ履歴
//

// XP ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによるジャンプの
// 3つを区別する。
//
// 通常の分岐
//	from は下位 16 ビットが分岐元 PC
//	to   は下位 16 ビットが分岐先 PC
//	info の最上位ビットが %0 で区別する。
//	info の下位 16 ビットに命令を上詰めで。例えば
//	CALL (1バイト命令 CD nn nn) なら $0000'cd00、
//	RETI (2バイト命令 ED 4D) なら $0000'ed4d。
//
// 例外発生
//	from は下位 16 ビットが例外発生時の PC
//	to   は $ffffffff ならこのエントリはベクタ方式の例外発生時側を示す、
//	        そうでなければダイレクト方式で下位 16 ビットが分岐先 PC
//	info の最上位ビットが %1 で info が $ffffffff でなければ
//	下位に優先度(0-15)。
//
// 例外ベクタジャンプの場合
//	from はベクタアドレス
//	to   は分岐先 PC、
//	info が $ffffffff で区別する。

// コンストラクタ
BranchHistory_hd64180::BranchHistory_hd64180(uint objid_)
	: inherited(objid_)
{
	monitor->SetSize(48, 1 + 32); // ヘッダ1行とエントリ数
	x_count = 38;
}

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

// ヘッダ文字列を返す。
std::string
BranchHistory_hd64180::FormatHeader() const
{
	if (is_exhist) {
		//      01234567890123456789012345678901234567890123456789
		return "No. From  Exception                   Iteration";
	} else {
		//      01234567890123456789012345678901234567890123456789
		return "No. From Instruction           ToAddr Iteration";
	}
}

// 1エントリ分の表示内容作成
std::string
BranchHistory_hd64180::FormatEntry(const BranchEntry& e)
{
	// 0         1         2         3         4         5
	// 012345678901234567890123456789012345678901234567890
	// No. From Instruction           ToAddr Iteration
	// 001 0123(0000:CALL NZ)      -> $0234 x123456899
	// No. From Exception
	// 002 0123<00> Timer Overflow
	// 003 0038(vector fetch)      -> $0000

	std::string desc = strhex(e.from, 4);

	if (e.info == 0xffffffff) {
		// 例外ベクタフェッチしてジャンプ
		desc += string_format("(vector fetch)      -> $%04x", e.to);
	} else if ((int32)e.info < 0) {
		// 例外発生
		uint32 vector = e.info & 0xff;
		desc += string_format("<%2u>%-15s",
			vector, MPU64180Device::InterruptName[vector]);
		// 例外履歴には宛先は出さないほうに統一。
		// ブランチ履歴でも、ベクタ方式なら宛先不要。
		if (is_exhist == false && e.to != -1) {
			desc += string_format(" -> $%04x", e.to);
		}
	} else {
		// ブランチ
		std::string mnemonic;
		uint32 inst = e.info & 0xffff;
		if (inst == 0x1000) {
			mnemonic = ":DJNZ";
		} else if (inst == 0x1800) {
			mnemonic = ":JR";
		} else if (inst == 0xc300) {
			mnemonic = ":JP";
		} else if (inst == 0xc900) {
			mnemonic = ":RET";
		} else if (inst == 0xcd00) {
			mnemonic = ":CALL";
		} else if (inst == 0xe900) {
			mnemonic = ":JP (HL)";
		} else if (inst == 0xed45) {
			mnemonic = ":RETN";
		} else if (inst == 0xed4d) {
			mnemonic = ":RETI";
		} else if (inst == 0xdde9) {
			mnemonic = ":JP (IX)";
		} else if (inst == 0xfde9) {
			mnemonic = ":JP (IY)";
		} else if ((inst & 0xe700) == 0x2000) {
			mnemonic = ":JR " + fstr((inst >> 11) & 3);
		} else if ((inst & 0xc700) == 0xc000) {
			mnemonic = ":RET " + fstr((inst >> 11) & 7);
		} else if ((inst & 0xc700) == 0xc200) {
			mnemonic = ":JP " + fstr((inst >> 11) & 7);
		} else if ((inst & 0xc700) == 0xc400) {
			mnemonic = ":CALL " + fstr((inst >> 11) & 7);
		} else if ((inst & 0xc700) == 0xc700) {
			mnemonic = string_format(":RST %02xH", (inst >> 8) & 0x38);
		}
		desc += string_format("(%04x%-8s)      -> $%04x",
			inst, mnemonic.c_str(), e.to);
	}
	return desc;
}

/*static*/ std::string
BranchHistory_hd64180::fstr(int f)
{
	static const char fff[] =
		"NZ\0\0"
		"Z\0\0\0"
		"NC\0\0"
		"C\0\0\0"
		"PO\0\0"
		"PE\0\0"
		"P\0\0\0"
		"M";

	return &fff[f * 4];
}

unix.superglobalmegacorp.com

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