--- nono/debugger/debugger.cpp 2026/04/29 17:05:54 1.1.1.21 +++ nono/debugger/debugger.cpp 2026/04/29 17:05:58 1.1.1.22 @@ -37,7 +37,6 @@ #include "debugger_m680x0.h" #include "debugger_m88xx0.h" #include "ascii_ctype.h" -#include "event.h" #include "hostcom.h" #include "mainapp.h" #include "memdump.h" @@ -66,7 +65,7 @@ Debugger::Debugger() // ブレークポイントモニタ bpoint_monitor = gMonitorManager->Regist(ID_MONITOR_BREAKPOINT, this); - bpoint_monitor->func = ToMonitorCallback(&Debugger::MonitorUpdateBpoint); + bpoint_monitor->SetCallback(&Debugger::MonitorScreenBpoint); bpoint_monitor->SetSize(59, 9); // メモリダンプモニタ @@ -217,8 +216,6 @@ Debugger::Init() bp.mask = 0xffff0000; bp.md = curmd; AddBreakpoint(bp); - // 今登録されている命令ブレークの必要命令長を再計算 - RecalcInstMask(); } // 入出力を FILE で扱う @@ -277,7 +274,7 @@ Debugger::ThreadRun() } } - LeavePrompt(false); + QuitPrompt(); Close(); } @@ -466,7 +463,7 @@ Debugger::Input(int c) break; case CmdAct::Quit: // アプリケーション自体を終了 - LeavePrompt(false); + QuitPrompt(); gMainApp.GetUIMessage()->Post(UIMessage::APPEXIT); } } else if (c < ' ' || c == 0x07f) { @@ -502,6 +499,16 @@ Debugger::EnterPrompt() PrintPrompt(); } +// コマンドモード(プロンプト)から出る。終了用。 +void +Debugger::QuitPrompt() +{ + // 終了までの間 VM の実行が進んでしまわないよう停止指示を出しておく。 + scheduler->Terminate(); + + LeavePrompt(false); +} + // コマンドモード(プロンプト)から出る。 // trace はプロンプトから抜ける際のトレース状態の指示。 // スレッド終了時は false を指定すること。 @@ -759,15 +766,11 @@ Debugger::CheckAllBreakpoints(DebuggerMD { bool is_break = false; int vector; - - // 命令ごとにクリアする - bi_inst = 0; - bi_inst_bytes = 0; + uint32 inst = 0; + int instbytes = 0; // 例外はここでローカルにコピーしてから、クリアする。 - // 厳密には atomic exchange すべきのような。 - vector = md->bv_vector; - md->bv_vector = -1; + vector = md->GetAndResetLastVector(); // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが) // 残りの他の条件も成立すればカウントを進める必要があるため、 @@ -804,7 +807,15 @@ Debugger::CheckAllBreakpoints(DebuggerMD continue; case BreakpointType::Instruction: - if (CheckBreakpointInst(bp)) { + if (instbytes < bp.instbytes) { + uint64 data = ReadBreakpointInst(bp); + if ((int64)data < 0) { + continue; + } + inst = data; + instbytes = bp.instbytes; + } + if ((inst & bp.mask) == bp.inst) { break; } continue; @@ -855,8 +866,7 @@ Debugger::CheckAllBreakpoints(DebuggerMD } case BreakpointType::Instruction: desc += string_format("inst=%0*x", - bi_inst_bytes * 2, - bi_inst >> ((4 - bi_inst_bytes) * 8)); + instbytes * 2, inst >> ((4 - instbytes) * 8)); break; default: assert(false); @@ -875,57 +885,21 @@ Debugger::CheckAllBreakpoints(DebuggerMD return is_break; } -// 命令ブレークポイントが成立するか調べる。 -bool -Debugger::CheckBreakpointInst(breakpoint_t& bp) +// 命令ブレークポイントの位置の命令を読み込む。 +uint64 +Debugger::ReadBreakpointInst(breakpoint_t& bp) { - // uint32 bi_inst が現在の PC 位置の命令データ (左詰め) - // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。 - // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。 - busaddr laddr = busaddr(bp.md->GetPC()) | BusAddr::Fetch | busaddr::SU(bp.md->IsSuper()); DebuggerMemoryStream mem(bp.md, laddr); - - // 必要なバイト数に達するまで読み足す - bi_inst = 0; - while (bi_inst_bytes < bi_need_bytes) { - uint64 data = mem.Read(bp.md->inst_bytes); - if ((int64)data < 0) { - return false; - } - - bi_inst <<= bp.md->inst_bytes * 8; - bi_inst |= data; - bi_inst_bytes += bp.md->inst_bytes; + uint64 inst = mem.Read(bp.instbytes); + if ((int64)inst < 0) { + return inst; } - // 左詰めにする - bi_inst <<= (4 - bi_inst_bytes) * 8; - - // 必要なバイト数取得できたので比較 - if ((bi_inst & bp.mask) == bp.inst) { - return true; - } - return false; -} - -// 例外通知 (メイン CPU) -void -Debugger::NotifyExceptionMain(int vector) -{ - auto md = md_mpu.get(); - md->bv_vector = vector; -} - -// 例外通知 (XP)。 -// ベクタとして割り込み優先順位 (Intmap*) を使う。 -void -Debugger::NotifyExceptionXP(int vector) -{ - auto md = md_xp.get(); - md->bv_vector = vector; + inst <<= (4 - bp.instbytes) * 8; + return (uint32)inst; } // コマンド一覧。 @@ -953,6 +927,8 @@ Debugger::NotifyExceptionXP(int vector) { "mpu", &Debugger::cmd_mpu, }, { "n", &Debugger::cmd_n, }, { "nt", &Debugger::cmd_nt, }, + { "pe", &Debugger::cmd_pe, }, + { "pf", &Debugger::cmd_pf, }, { "q", &Debugger::cmd_q, }, { "quit", &Debugger::cmd_q, }, { "reset", &Debugger::cmd_reset, }, @@ -1091,13 +1067,14 @@ Debugger::HelpListMain = { { "b*", "Set/show Breakpoints (Type \"hb\" for details)" }, { "brhist", "Show branch history" }, { "c/ct", "Continue" }, - { "d/dt/D", "Disassemble" }, + { "d/D", "Disassemble" }, { "disp", "Set register group to show" }, { "exhist", "Show exception history" }, { "help", "Show this message" }, { "L", "Set log level" }, - { "m/mt/M", "Memory dump" }, + { "m/M", "Memory dump" }, { "n/nt", "Step until next instruction (Skip subroutines)" }, + { "pe/pf", "Print exception frame / floating-point state frame" }, { "quit", "Quit" }, { "r*", "Show registers (Type \"hr\" for details)" }, { "reset", "Reset the VM" }, @@ -1334,6 +1311,21 @@ Debugger::HelpDetails = { { "nt", "=n" }, //----- + { "pe", R"**( + Command: pe [
] + + Print an exception frame starting with
. If the
is + omitted, %sp is used. + )**" }, + + //----- + { "pf", R"**( + Command: pf
+ + Print a floating-point state frame starting with
. + )**" }, + + //----- { "quit", R"**( Command: quit (or q) @@ -1606,8 +1598,6 @@ Debugger::cmd_b_delete() fprintf(cons, "Breakpoint #%d removed.\n", n); bp.type = BreakpointType::Unused; - // 今登録されている命令ブレークの必要命令長を再計算 - RecalcInstMask(); return CmdAct::Stay; } @@ -1704,6 +1694,7 @@ Debugger::AddBreakInst(const std::string { DebuggerMD *md; breakpoint_t bp; + std::string hexstr; std::string inststr; std::string maskstr; int instlen; @@ -1719,19 +1710,19 @@ Debugger::AddBreakInst(const std::string // 命令部。 // ニーモニックが指定できたら便利。 - inststr = md->ParseInst(argstr); - if (inststr.empty() == false) { + hexstr = md->ParseInst(argstr); + if (hexstr.empty()) { masklen = -1; } else { - auto pos = argstr.find(':'); + auto pos = hexstr.find(':'); if (pos == std::string::npos) { // マスク指定なし。 - inststr = argstr; + inststr = hexstr; masklen = -1; } else { // マスク指定あり。 - inststr = argstr.substr(0, pos); - maskstr = argstr.substr(pos + 1); + inststr = hexstr.substr(0, pos); + maskstr = hexstr.substr(pos + 1); masklen = maskstr.size(); } } @@ -1764,6 +1755,7 @@ Debugger::AddBreakInst(const std::string bp.inst <<= 32 - instlen * 4; bp.mask <<= 32 - instlen * 4; } + bp.instbytes = instlen / 2; // を取り出す。 if (skipstr.empty() == false) { @@ -1778,9 +1770,6 @@ Debugger::AddBreakInst(const std::string return bi; } - // 今登録されている命令ブレークの必要命令長を再計算。 - RecalcInstMask(); - return bi; } @@ -1823,10 +1812,10 @@ Debugger::cmd_bv() fprintf(cons, "Breakpoint #%d added.\n", bi); // この CPU 側に、すでに来ている例外をクリア。 - // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に - // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは + // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側は常に + // 記録している。これをクリアするのは CheckAllBreakpoints() で、これは // 命令間(命令前)に呼ばれるやつ、なのでこうなる。 - // 1. 例外が起きると md->bv_vector がセットされる + // 1. 例外が起きると md->cpu->last_vector がセットされる // 2. 例外ブレークポイントを設定していないとこれがクリアされない // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で // 1.のベクタが反応してしまう。 @@ -1837,7 +1826,7 @@ Debugger::cmd_bv() // bp.md は AddBreakpoint() で bpoint 登録時にセットされるので // インデックスから bp をもう一度読み込む。 auto bp = bpoint[bi]; - bp.md->bv_vector = -1; + bp.md->GetAndResetLastVector(); return CmdAct::Stay; } @@ -2052,7 +2041,7 @@ Debugger::cmd_b_list() // ブレークポイント一覧 (モニタ) void -Debugger::MonitorUpdateBpoint(Monitor *, TextScreen& monitor) +Debugger::MonitorScreenBpoint(Monitor *, TextScreen& monitor) { // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890 @@ -2148,9 +2137,6 @@ Debugger::cmd_bx() } fprintf(cons, "All breakpoints are removed.\n"); - // 今登録されている命令ブレークの必要命令長を再計算 - RecalcInstMask(); - return CmdAct::Stay; } @@ -2210,39 +2196,6 @@ Debugger::AddBreakpoint(const breakpoint return -1; } -// すべての命令ブレークのマスクのうち最長のものを再計算する。 -// 命令ブレークポイントの追加/削除のたびに呼び出すこと。 -void -Debugger::RecalcInstMask() -{ - uint32 mask; - bi_need_bytes = 0; - - // 登録されている命令ブレークの最長マスクを求める - mask = 0; - for (const auto& bp : bpoint) { - if (bp.type == BreakpointType::Instruction) { - mask |= bp.mask; - } - } - - // mask の Number of Trailing Zero を求める。 - // (x & -x) で x の最も下の立ってるビットだけを立てる、 - // (x & -x) -1 でそれより下の全ビットを立てる、 - // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。 - int ntz = __builtin_popcount((mask & -(int32)mask) - 1); - // 上位側から数えたマスクに必要なビット数 - int mlen = 32 - ntz; - // 命令語単位に切り上げる - mlen = roundup(mlen, curmd->inst_bytes * 8); - // バイト数に変換 - mlen /= 8; - - if (mlen > bi_need_bytes) { - bi_need_bytes = mlen; - } -} - // ブランチ履歴、例外履歴表示 // brhist [] // exhist [] @@ -2291,7 +2244,7 @@ Debugger::cmd_hist_common(BranchHistory& // 表示行数 + 1行はヘッダ分 int lines = std::min(used, maxlines) + 1; - // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。 + // MonitorScreen() は TextScreen 高さに合わせて出力してくれる。 auto *histmon = hist.monitor; TextScreen screen; screen.Init(histmon->GetCol(), lines); @@ -2302,7 +2255,7 @@ Debugger::cmd_hist_common(BranchHistory& } // 表示 - histmon->Update(screen); + histmon->UpdateScreen(screen); ShowTextScreen(screen); return CmdAct::Stay; @@ -2518,20 +2471,7 @@ Debugger::CmdAct Debugger::cmd_z() { // 次のアドレスを求める。 - if (curmd->inst_bytes_fixed) { - // 固定長命令 - step_addr = curmd->GetPC() + curmd->inst_bytes_fixed; - } else { - // 可変長なら逆アセンブルしてみるしか。 - - busaddr laddr = busaddr(curmd->GetPC()) - | BusAddr::Fetch - | busaddr::SU(curmd->IsSuper()); - DebuggerMemoryStream mem(curmd, laddr); - // XXX 失敗したらどうすべ - curmd->Disassemble(mem); - step_addr = mem.laddr.Addr(); - } + step_addr = curmd->GetNextPC(); step_type = StepType::Addr; step_md = curmd; @@ -2835,26 +2775,64 @@ Debugger::SetNBreakpoint() // 命令がサブルーチンやトラップなどステップインできる命令か。 if (curmd->IsOpStepIn(mem)) { // この次の命令にブレークをかける - if (curmd->inst_bytes_fixed != 0) { - // 固定長命令 - step_addr = mem.laddr.Addr(); - } else { - // 可変長なら逆アセンブルしてみるしか。 - // 表示用文字列は作る必要ないけど、処理分けるのも手間だし - // ここは人間がコマンド打った時しかこないので気にしない。 - - // IsOpStepIn() が mem を進めるので戻す。 - mem.ResetAddr(curmd->GetPC()); - // XXX 失敗したらどうすべ - curmd->Disassemble(mem); - step_addr = mem.laddr.Addr(); - } + step_addr = curmd->GetNextPC(); } else { // そうでなければ1命令進める。 step_addr = (int64)-1; } } +// 例外フレームを表示する。 +Debugger::CmdAct +Debugger::cmd_pe() +{ + busaddr saddr; + + saddr = busaddr(curmd->GetRegAddr("sp")); + if (cmdargs.size() > 1) { + if (GetAddr(&saddr, true) == false) { + return CmdAct::Stay; + } + } + + // 例外フレームなのでスーパーバイザ、データ空間読み込み固定。 + saddr |= BusAddr::SRead; + // 今のところ論理アドレスのみ(?) + saddr |= BusAddr::Logical; + + curmd->PrintExceptionFrame(cons, saddr); + return CmdAct::Stay; +} + +// FSAVE フレームを表示する。 +Debugger::CmdAct +Debugger::cmd_pf() +{ + busaddr saddr; + + if (cmdargs.size() < 2) { + fprintf(cons, "Usage: pf
\n"); + return CmdAct::Stay; + } + if (GetAddr(&saddr, true) == false) { + return CmdAct::Stay; + } + + // スーパーバイザ、データ空間固定でいいはず。 + saddr |= BusAddr::SRead; + // 今のところ論理アドレスのみ(?) + saddr |= BusAddr::Logical; + + // m68k 専用。 + auto mdm68k = dynamic_cast(curmd); + if (mdm68k) { + mdm68k->PrintFPStateFrame(cons, saddr); + } else { + fprintf(cons, "Not supported.\n"); + } + return CmdAct::Stay; +} + // デバッガ終了コマンド Debugger::CmdAct Debugger::cmd_q() @@ -2949,7 +2927,8 @@ Debugger::ShowMonitor(Monitor *monitor) auto size = monitor->GetSize(); ts.Init(size.width, size.height); - monitor->Update(ts); + // テキストだけで表示 + monitor->UpdateScreen(ts); ShowTextScreen(ts); } @@ -3207,11 +3186,30 @@ DebuggerMD::DebuggerMD(Debugger *parent_ { parent = parent_; arch = arch_; - - bv_vector = -1; } // デストラクタ DebuggerMD::~DebuggerMD() { } + +// 現在の命令の直後の命令アドレスを返す。 +// 命令が可変長のアーキテクチャなら逆アセンブルしてみるしかない。 +// 命令が固定長のアーキテクチャはオーバーライドして解決のこと。 +uint32 +DebuggerMD::GetNextPC() +{ + busaddr laddr = busaddr(GetPC()) + | BusAddr::Fetch + | busaddr::SU(IsSuper()); + DebuggerMemoryStream mem(this, laddr); + // XXX 失敗したらどうすべ + Disassemble(mem); + return mem.laddr.Addr(); +} + +void +DebuggerMD::PrintExceptionFrame(FILE *out, busaddr) +{ + fprintf(out, "Not supported\n"); +}