--- nono/debugger/branchhistory.cpp 2026/04/29 17:04:43 1.1 +++ nono/debugger/branchhistory.cpp 2026/04/29 17:05:28 1.1.1.9 @@ -4,17 +4,55 @@ // Licensed under nono-license.txt // +// // ブランチ履歴 +// #include "branchhistory.h" -#include "m68030core.h" +#include "m68030disasm.h" #include "m88100.h" -#include "mystring.h" +#include "mpu64180.h" +#include "vectortable.h" // コンストラクタ -BranchHistory::BranchHistory() +BranchHistory::BranchHistory(uint objid_) + : inherited(objid_) { - monitor_size = nnSize(55, 33); + 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.func = ToMonitorCallback(&BranchHistory::MonitorUpdate); + monitor.SetSize(55, 1 + 32); // ヘッダ1行とエントリ数 + monitor.SetMaxHeight(1 + 256); + monitor.Regist(monitor_id); + + // hd64180 はこれを上書きする + x_count = 45; } // デストラクタ @@ -53,7 +91,7 @@ BranchHistory::GetUsed() const // モニタ更新 void -BranchHistory::MonitorUpdate(TextScreen& monitor) +BranchHistory::MonitorUpdate(Monitor *, TextScreen& screen) { uint8 t; int y; @@ -62,13 +100,10 @@ BranchHistory::MonitorUpdate(TextScreen& // userdata は表示開始位置と各種フラグ。 // 下位 32bit が表示開始位置で、0 なら先頭のエントリから、1 なら先頭の - // 一つ次のエントリから、…を表す (表示行数は monitor の高さ分)。 - // ExHist が %1 なら例外履歴、%0 ならブランチ履歴。これによって - // ヘッダを変えたい。 + // 一つ次のエントリから、…を表す (表示行数は screen の高さ分)。 // BottomToTop が %1 なら並び順を下から上に変える (コンソール用)。 - bool is_exhist = (monitor.userdata & ExHist); - bool bottom_to_top = (monitor.userdata & BottomToTop); - int pos = (int)monitor.userdata; + bool bottom_to_top = (screen.userdata & BottomToTop); + int pos = (int)screen.userdata; // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890123456789 @@ -79,17 +114,12 @@ BranchHistory::MonitorUpdate(TextScreen& // 002 S:01234567< 69> MFP Timer-C // 01234567890123456789012 - monitor.Clear(); - row = monitor.GetRow(); + screen.Clear(); + row = screen.GetRow(); // 最初の1行は常にヘッダ - if (is_exhist) { - monitor.Print(0, 0, "No. FromAddr Vec. Exception"); - monitor.Print(45, 0, "Iteration"); - } else { - monitor.Print(0, 0, "No. FromAddr Instruction"); - monitor.Print(35, 0, "ToAddr Iteration"); - } + std::string header = FormatHeader(); + screen.Puts(0, 0, header.c_str()); // pos は最新を 0 とした通し番号。(スクロールしてたら開始が 0 とは限らない) // t が entry[] 上の現在位置。 @@ -109,40 +139,78 @@ BranchHistory::MonitorUpdate(TextScreen& continue; } std::string str = FormatEntry(e); - monitor.Print(0, y, "%3d %s", pos, str.c_str()); + screen.Print(0, y, "%3u %s", pos, str.c_str()); if (e.count > 1) { - monitor.Print(45, y, "x%9u", e.count); + 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 ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによる -// ジャンプの3つを区別する。 +// m680x0 ブランチ履歴では、通常ブランチ、例外発生 (IOCS コールと +// それ以外)、例外ベクタによるジャンプの4つを区別する。 // -// 通常の分岐: -// from は分岐元 PC、to は分岐先 PC、inst の上位16ビットに命令の1ワード目。 -// inst の $8000 が立っていないことで区別する。 +// 1. 通常の分岐: +// from は上位31ビットが分岐元 PC、最下位ビットが S/U。 +// to は32ビット全体が分岐先 PC。 +// inst は上位2ビットが %00 (Normal)、下位16ビットに分岐元命令の1ワード目。 +// +// 31 30 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 0 | 0 | .. | instruction word | +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// +// 2. 例外発生 (IOCS コール以外の場合): +// from は上位31ビットが例外発生時の PC、最下位ビットが S/U。 +// to は不問だが 0 にすること。 +// inst は上位2ビットが %11 (Exception)、Number はベクタ番号 (0-255)。 +// 例外が TRAP#15 で IOCS コールっぽくない場合もこちら。 +// +// 31 30 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 1 | 1 | .. | 0 | Vector | +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// +// 3. 例外発生 (IOCS コールの場合): +// from, to は同じ。 +// inst は上位2ビットが %10 (IOCSCall)、Number は IOCS コール番号 (0-255)。 +// +// 31 30 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 1 | 0 | .. | 0 | IOCS Call Number | +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// +// 4. 例外ベクタによる分岐 (例外処理の最後に起きる): +// from はベクタアドレス、 +// to は分岐先 (0 も起こりえる)、 +// inst は上位2ビットが %01 (VectorJump)。 +// +// 31 30 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | 0 | 1 | .. | 0 | +// +---+---+-- --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // -// 例外発生: -// 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(uint objid_) + : inherited(objid_) { } @@ -159,26 +227,11 @@ BranchHistory_m680x0::FormatEntry(const std::string desc = string_format("%c:%08x", (e.from & 1) ? 'S' : 'U', (e.from & ~1)); - 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 { - // ブランチ + switch (e.inst >> 30) { + case 0: // 通常分岐 + { const char *mnemonic; - uint32 inst = e.inst >> 16; + uint32 inst = e.inst & 0xffff; if (inst == 0x4e73) { mnemonic = ":rte"; } else if (inst == 0x4e75) { @@ -207,6 +260,36 @@ BranchHistory_m680x0::FormatEntry(const 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 コール) + { + uint num = e.inst & 0xff; + const char *name = m680x0disasm::GetIOCSName(num); + if (name != NULL) { + desc += string_format("<%3u> IOCS %s", M68K::EXCEP_TRAP15, name); + } else { + desc += string_format("<%3u> IOCS $%02x", M68K::EXCEP_TRAP15, num); + } + break; + } + + case 3: // 例外発生 (IOCS コール以外) + { + uint32 vector = e.inst & 0xff; + const char *name = vectortable->GetExceptionName(vector); + desc += string_format("<%3u> %s", vector, name ?: ""); + break; + } + + default: + __unreachable(); } return desc; } @@ -221,14 +304,19 @@ BranchHistory_m680x0::FormatEntry(const // m88k は例外ベクタを直接実行するのでそれは不要。 // // 通常の分岐: -// from は分岐元 XIP、to は分岐先アドレス、inst は命令ワード。 +// from は上位30ビットが分岐元 XIP、最下位ビットが S/U、 +// to は32ビット全体が分岐先アドレス、 +// inst は命令ワード。 // // 例外発生: -// from は例外発生時の XIP、to は 0。inst は $fc000VVV で VVV (9ビット)が -// ベクタ番号。命令ワードとは衝突しない (instruction.txt 参照)。 +// from は上位30ビットが例外発生時の XIP、最下位ビットが S/U、 +// to は 0。 +// inst は $fc000VVV で VVV (9ビット)がベクタ番号。 +// 命令ワードとは衝突しない (instruction.txt 参照)。 // コンストラクタ -BranchHistory_m88xx0::BranchHistory_m88xx0() +BranchHistory_m88xx0::BranchHistory_m88xx0(uint objid_) + : inherited(objid_) { } @@ -249,11 +337,12 @@ BranchHistory_m88xx0::FormatEntry(const // 例外発生記録 // 発生アドレスとベクタを表示 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); + 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.inst >> 12) & 0xfff); } } else if (e.inst >= 0xc0000000) { // ブランチ @@ -266,7 +355,7 @@ BranchHistory_m88xx0::FormatEntry(const case 3: mnemonic = ":bsr.n"; break; case 4: mnemonic = ":bb0"; break; case 5: mnemonic = ":bb0.n"; break; - case 6: mnemonic = ":bb1"; break; + case 6: mnemonic = ":bb1"; break; case 7: mnemonic = ":bb1.n"; break; case 0xa: mnemonic = ":bcnd"; break; @@ -309,3 +398,141 @@ BranchHistory_m88xx0::FormatEntry(const } return desc; } + + +// +// XP ブランチ履歴 +// + +// XP ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによるジャンプの +// 3つを区別する。 +// +// 通常の分岐 +// from は下位 16 ビットが分岐元 PC +// to は下位 16 ビットが分岐先 PC +// inst の最上位ビットが %0 で区別する。 +// inst の下位 16 ビットに命令を上詰めで。例えば +// CALL (1バイト命令 CD nn nn) なら $0000'cd00、 +// RETI (2バイト命令 ED 4D) なら $0000'ed4d。 +// +// 例外発生 +// from は下位 16 ビットが例外発生時の PC +// to は $ffffffff ならこのエントリはベクタ方式の例外発生時側を示す、 +// そうでなければダイレクト方式で下位 16 ビットが分岐先 PC +// inst の最上位ビットが %1 で inst が $ffffffff でなければ +// 下位に優先度(0-15)。 +// +// 例外ベクタジャンプの場合 +// from はベクタアドレス +// to は分岐先 PC、 +// inst が $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.inst == 0xffffffff) { + // 例外ベクタフェッチしてジャンプ + desc += string_format("(vector fetch) -> $%04x", e.to); + } else if ((int32)e.inst < 0) { + // 例外発生 + uint32 vector = e.inst & 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.inst & 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]; +}