--- nono/debugger/branchhistory.cpp 2026/04/29 17:05:16 1.1.1.7 +++ nono/debugger/branchhistory.cpp 2026/04/29 17:05:41 1.1.1.11 @@ -9,12 +9,14 @@ // #include "branchhistory.h" +#include "m680x0disasm.h" #include "m88100.h" +#include "monitor.h" #include "mpu64180.h" #include "vectortable.h" // コンストラクタ -BranchHistory::BranchHistory(int objid_) +BranchHistory::BranchHistory(uint objid_) : inherited(objid_) { int monitor_id; @@ -45,10 +47,10 @@ BranchHistory::BranchHistory(int objid_) vectortable = GetVectorTable(); - monitor.func = ToMonitorCallback(&BranchHistory::MonitorUpdate); - monitor.SetSize(55, 1 + 32); // ヘッダ1行とエントリ数 - monitor.SetMaxHeight(1 + 256); - monitor.Regist(monitor_id); + monitor = gMonitorManager->Regist(monitor_id, this); + monitor->func = ToMonitorCallback(&BranchHistory::MonitorUpdate); + monitor->SetSize(55, 1 + 32); // ヘッダ1行とエントリ数 + monitor->SetMaxHeight(1 + 256); // hd64180 はこれを上書きする x_count = 45; @@ -69,7 +71,7 @@ BranchHistory::Clear() e.count = 0; e.from = 0xffffffff; e.to = 0xffffffff; - e.inst = 0; + e.info = 0; } } @@ -138,7 +140,7 @@ BranchHistory::MonitorUpdate(Monitor *, continue; } std::string str = FormatEntry(e); - screen.Print(0, y, "%3d %s", pos, str.c_str()); + screen.Print(0, y, "%3u %s", pos, str.c_str()); if (e.count > 1) { screen.Print(x_count, y, "x%9u", e.count); } @@ -160,35 +162,58 @@ BranchHistory::FormatHeader() const // -// m680x0 ブランチ履歴 (どこに置くのがよいか) +// m680x0 ブランチ履歴 // -// m680x0 ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによる -// ジャンプの3つを区別する。 +// m680x0 ブランチ履歴では、通常ブランチ、例外発生 (IOCS コールと +// それ以外)、例外ベクタによるジャンプの4つを区別する。 // -// 通常の分岐: -// from は上位31ビットが分岐元 PC、最下位ビットが S/U、 -// to は32ビット全体が分岐先 PC、 -// inst の上位16ビットに命令の1ワード目。 -// inst の $8000 が立っていないことで区別する。 -// -// 例外発生: -// from は上位31ビットが例外発生時の PC、最下位ビットが S/U、 -// to は不問(通常 0 をセットすること)、 -// inst は $00008LVV で $8000 が例外フラグ、VV がベクタ番号である。 -// ベクタ番号が $00 だと次項(ベクタによる分岐)と区別つかなくなってしまう -// ため、リセット例外 (ベクタ番号0) は V=$01 で記録する。 -// L は割り込みなら割り込みレベル。 -// inst に $8000 が立っていて、かつ $8000 と一致しないことで区別する。 +// 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. 例外発生 (IOCS/DOS コールの場合): +// from, to は同じ。 +// info は上位2ビットが %10 (ServiceCall)。 +// IOCS コールなら、b8 が %0 で b7-b0 に IOCS コール番号。 +// DOS コールなら、b8 が %1 で b7-b0 に DOS コール番号。 +// +// +-+-+-----------+---------------+---------------+---------------+ +// |1 0| 0 | 0 |0| IOCS Call No. | +// +-+-+-----------+---------------+---------------+---------------+ +// +-+-+-----------+---------------+---------------+---------------+ +// |1 0| 0 | - |1| DOS Call No. | +// +-+-+-----------+---------------+---------------+---------------+ // -// 例外ベクタによる分岐 (例外処理の最後に起きる): +// 4. 例外ベクタによる分岐 (例外処理の最後に起きる): // from はベクタアドレス、 // to は分岐先 (0 も起こりえる)、 -// inst は $00008000。 -// inst が $00008000 と一致することで区別する (ビットが立っているではない)。 +// info は上位2ビットが %01 (VectorJump)。 +// +// +-+-+-----------+---------------+---------------+---------------+ +// |0 1| 0 | +// +-+-+-----------+---------------+---------------+---------------+ // コンストラクタ -BranchHistory_m680x0::BranchHistory_m680x0(int objid_) +BranchHistory_m680x0::BranchHistory_m680x0(uint objid_) : inherited(objid_) { } @@ -206,26 +231,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; - } - // とりあえず - const char *name = vectortable->GetExceptionName(vector); - desc += string_format("<%3d> %s", vector, name ?: ""); - // XXX TODO 割り込みレベル - } else { - // 例外ベクタフェッチしてジャンプ - desc += string_format("(vector fetch) -> $%08x", e.to); - } - } else { - // ブランチ + switch (e.info >> 30) { + case 0: // 通常分岐 + { const char *mnemonic; - uint32 inst = e.inst >> 16; + uint32 inst = e.info & 0xffff; if (inst == 0x4e73) { mnemonic = ":rte"; } else if (inst == 0x4e75) { @@ -254,13 +264,54 @@ 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/DOS コール) + { + const char *call; + const char *name; + uint vector; + 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); + } + 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 ブランチ履歴 // // m88xx0 ブランチ履歴では、通常ブランチ、例外発生の2つを区別する。 @@ -270,16 +321,22 @@ BranchHistory_m680x0::FormatEntry(const // 通常の分岐: // from は上位30ビットが分岐元 XIP、最下位ビットが S/U、 // to は32ビット全体が分岐先アドレス、 -// inst は命令ワード。 +// info は命令ワード。 // // 例外発生: // from は上位30ビットが例外発生時の XIP、最下位ビットが S/U、 // to は 0。 -// inst は $fc000VVV で VVV (9ビット)がベクタ番号。 +// 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(int objid_) +BranchHistory_m88xx0::BranchHistory_m88xx0(uint objid_) : inherited(objid_) { } @@ -297,21 +354,21 @@ BranchHistory_m88xx0::FormatEntry(const std::string desc = string_format("%c:%08x", (e.from & 1) ? 'S' : 'U', (e.from & ~1)); - if (e.inst >= 0xfc000000 && e.to == 0) { + if (e.info >= 0xfc000000 && e.to == 0) { // 例外発生記録 // 発生アドレスとベクタを表示 - uint32 vector = e.inst & 0x1ff; + uint32 vector = e.info & 0x1ff; const char *name = vectortable->GetExceptionName(vector); - desc += string_format("<%3d> %s", vector, name ?: ""); + desc += string_format("<%3u> %s", vector, name ?: ""); if (vector == 128 || vector == 450) { // 450 なら OpenBSD のシステムコール番号を追加。 // 非公式 NetBSD/luna88k は(今の所?) 128 を使ってるようだ。 - desc += string_format("(%d)", (e.inst >> 12) & 0xfff); + desc += string_format("(%u)", (e.info >> 12) & 0xfff); } - } else if (e.inst >= 0xc0000000) { + } else if (e.info >= 0xc0000000) { // ブランチ const char *mnemonic = ""; - uint32 up4 = (e.inst >> 26) & 0xf; + uint32 up4 = (e.info >> 26) & 0xf; switch (up4) { case 0: mnemonic = ":br"; break; case 1: mnemonic = ":br.n"; break; @@ -325,7 +382,7 @@ BranchHistory_m88xx0::FormatEntry(const case 0xa: mnemonic = ":bcnd"; break; case 0xb: mnemonic = ":bcnd.n"; break; case 0xc: { - uint32 lo6 = (e.inst >> 10) & 0x3f; + uint32 lo6 = (e.info >> 10) & 0x3f; if (lo6 == 0x34) { mnemonic = ":tb0"; } else if (lo6 == 0x36) { @@ -336,9 +393,9 @@ BranchHistory_m88xx0::FormatEntry(const break; } case 0xd: { - uint32 lo6 = (e.inst >> 10) & 0x3f; + uint32 lo6 = (e.info >> 10) & 0x3f; if (lo6 == 0x30) { - if ((e.inst & 0x1f) == 1) { + if ((e.info & 0x1f) == 1) { mnemonic = ":jmp r1"; } else { mnemonic = ":jmp"; @@ -358,7 +415,7 @@ BranchHistory_m88xx0::FormatEntry(const default: break; } - desc += string_format("(%08x%-7s) -> $%08x", e.inst, mnemonic, e.to); + desc += string_format("(%08x%-7s) -> $%08x", e.info, mnemonic, e.to); } return desc; } @@ -374,8 +431,8 @@ BranchHistory_m88xx0::FormatEntry(const // 通常の分岐 // from は下位 16 ビットが分岐元 PC // to は下位 16 ビットが分岐先 PC -// inst の最上位ビットが %0 で区別する。 -// inst の下位 16 ビットに命令を上詰めで。例えば +// info の最上位ビットが %0 で区別する。 +// info の下位 16 ビットに命令を上詰めで。例えば // CALL (1バイト命令 CD nn nn) なら $0000'cd00、 // RETI (2バイト命令 ED 4D) なら $0000'ed4d。 // @@ -383,19 +440,19 @@ BranchHistory_m88xx0::FormatEntry(const // from は下位 16 ビットが例外発生時の PC // to は $ffffffff ならこのエントリはベクタ方式の例外発生時側を示す、 // そうでなければダイレクト方式で下位 16 ビットが分岐先 PC -// inst の最上位ビットが %1 で inst が $ffffffff でなければ +// info の最上位ビットが %1 で info が $ffffffff でなければ // 下位に優先度(0-15)。 // // 例外ベクタジャンプの場合 // from はベクタアドレス // to は分岐先 PC、 -// inst が $ffffffff で区別する。 +// info が $ffffffff で区別する。 // コンストラクタ -BranchHistory_hd64180::BranchHistory_hd64180(int objid_) +BranchHistory_hd64180::BranchHistory_hd64180(uint objid_) : inherited(objid_) { - monitor.SetSize(48, 1 + 32); // ヘッダ1行とエントリ数 + monitor->SetSize(48, 1 + 32); // ヘッダ1行とエントリ数 x_count = 38; } @@ -431,13 +488,13 @@ BranchHistory_hd64180::FormatEntry(const std::string desc = strhex(e.from, 4); - if (e.inst == 0xffffffff) { + if (e.info == 0xffffffff) { // 例外ベクタフェッチしてジャンプ desc += string_format("(vector fetch) -> $%04x", e.to); - } else if ((int32)e.inst < 0) { + } else if ((int32)e.info < 0) { // 例外発生 - uint32 vector = e.inst & 0xff; - desc += string_format("<%2d>%-15s", + uint32 vector = e.info & 0xff; + desc += string_format("<%2u>%-15s", vector, MPU64180Device::InterruptName[vector]); // 例外履歴には宛先は出さないほうに統一。 // ブランチ履歴でも、ベクタ方式なら宛先不要。 @@ -447,7 +504,7 @@ BranchHistory_hd64180::FormatEntry(const } else { // ブランチ std::string mnemonic; - uint32 inst = e.inst & 0xffff; + uint32 inst = e.info & 0xffff; if (inst == 0x1000) { mnemonic = ":DJNZ"; } else if (inst == 0x1800) {