--- nono/lib/branchhistory.cpp 2026/04/29 17:04:34 1.1 +++ nono/lib/branchhistory.cpp 2026/04/29 17:04:37 1.1.1.2 @@ -7,11 +7,14 @@ // ブランチ履歴 #include "branchhistory.h" +#include "m68030core.h" +#include "m88100.h" +#include "mystring.h" // コンストラクタ BranchHistory::BranchHistory() { - monitor.Init(70, 16); + monitor_size = nnSize(54, 33); } // デストラクタ @@ -19,35 +22,258 @@ BranchHistory::~BranchHistory() { } -bool -BranchHistory::MonitorUpdate() +// 使用中のエントリ数を取得する +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) { - int i; 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 - // 01 $01234567->$01234567 x123456789 16 $01234567->$01234567 x123456789 + // 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"); + } - // top は現在の位置。 - // entry[] は古い順に書き込まれるが、表示は新しい順にしたい。 - // ブランチ履歴は書き込み最適化のため256エントリあるが、そんなに - // 奥のほうが気になることはないだろう。 - t = top; - for (i = 0; i < 32; i++) { + // 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--]; - int x = (i / 16) * 36; - int y = i % 16; if (e.count == 0) { continue; } - monitor.Print(x, y, "%02d:$%08x->$%08x", i, - e.from, e.to); + std::string str = FormatEntry(e); + monitor.Print(0, y, "%3d $%08x%s", pos, e.from, str.c_str()); if (e.count > 1) { - monitor.Print(x + 24, y, "x%9u", e.count); + 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 が例外フラグ、V がベクタ番号、 +// L は割り込みなら割り込みレベルとする。 +// +// 例外ベクタによる分岐 (例外処理の最後に起きる): +// from はベクタアドレス、to は分岐先、inst は $00008000。 +// inst と to によって区別する。 + +// コンストラクタ +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.to == 0) { + // 例外発生記録 + // 発生アドレスとベクタ(と割り込みレベル)を表示 + uint32 vector = e.inst & 0xff; + // とりあえず + 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 ?: ""); + } 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) { + 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 true; + return desc; }