--- nono/debugger/debugger.cpp 2026/04/29 17:04:43 1.1.1.6 +++ nono/debugger/debugger.cpp 2026/04/29 17:04:56 1.1.1.10 @@ -11,26 +11,30 @@ #include "mainapp.h" #include "mystring.h" #include "mythread.h" +#include "scheduler.h" #include #include static Debugger *gDebugger; -BreakpointMonitor *gBreakpointMonitor; CVPrompt *gCVPrompt; static void *debugger_run(void *); -// 初期化。 +// 作成 (MainApp::Init1 から呼ばれる) +void +debugger_create() +{ + gDebugger = new Debugger(); + gCVPrompt = new CVPrompt(); +} + +// 初期化 (MainApp::Init2 から呼ばれる) // この時点で VM が初期化されていること。 void debugger_init() { - gDebugger = new Debugger(); gDebugger->Init(); - gBreakpointMonitor = new BreakpointMonitor(); - gCVPrompt = new CVPrompt(); - // デバッガスレッド起動 pthread_t th; pthread_create(&th, NULL, debugger_run, NULL); @@ -47,8 +51,17 @@ debugger_run(void *dummy) return NULL; } +// メモリダンプモニタを外部から取得 +Monitor& +debugger_memdump_monitor(int n) +{ + assert(n < MAX_MEMDUMP_MONITOR); + return gDebugger->memdump_monitor[n]; +} + // コンストラクタ Debugger::Debugger() + : inherited("Debugger") { if (gMPU680x0) { md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU()); @@ -57,6 +70,23 @@ Debugger::Debugger() } else { throw "unknown mpu"; } + + // ブレークポイントモニター + bpoint_monitor.func = (MonitorCallback_t)&Debugger::MonitorUpdateBpoint; + bpoint_monitor.SetSize(55, 9); + bpoint_monitor.Regist(ID_MONITOR_BREAKPOINT); + + // メモリダンプモニター + for (int i = 0, end = memdump_monitor.size(); i < end; i++) { + auto& mon = memdump_monitor[i]; + mon.obj = this; + mon.func = (MonitorCallback_t)&Debugger::MonitorUpdateMemdump; + mon.SetSize(76, 16); + mon.Regist(ID_MONITOR_MEMDUMP(i)); + } + // m/M コマンド用。こっちは Regist 不要 + m_monitor.func = (MonitorCallback_t)&Debugger::MonitorUpdateMemdump; + m_monitor.SetSize(76, 16); } // コマンドライン引数によって動作を決めるところ。 @@ -151,6 +181,9 @@ Debugger::ThreadRun() break; } + // 実時間を停止 + gRealtime.Stop(); + // d/m をいきなり引数なしで実行した時のため、現在地にしておく。 pc = md->GetPC(); m_last_addr.Set(pc, md->IsSuper(), true); @@ -164,6 +197,9 @@ Debugger::ThreadRun() // 次回との差分のため、今のレジスタセットをバックアップ md->BackupRegs(); + // 実時間を再開 + gRealtime.Start(); + // メインループから戻ったということは Leave か Quit なので // どちらにしても、ここでプロンプトを手放す。 gCVPrompt->NotifyRelease(); @@ -264,6 +300,12 @@ Debugger::CommandAction Debugger::MainLoop() { for (;;) { + // ブレークポイント到達メッセージがあればここで表示 + if (bpointmsg.empty() == false) { + cons->Print("%s", bpointmsg.c_str()); + bpointmsg.clear(); + } + cons->Prompt(); if (cons->Gets(cmdbuf) == false) { // EOF or Error @@ -420,7 +462,8 @@ Debugger::Check() // ブレークポイントがどれかでも成立するかを調べる。 // 1つ以上成立してブレークするなら true を返す。 // 1つも成立しておらずブレークしないなら false を返す。 -// 成立すれば内容をここで表示する。 +// このルーチン内では Console があることを期待してはいけない (プロンプトが +// ない時でも呼ばれるので)。 bool Debugger::CheckAllBreakpoints() { @@ -509,7 +552,13 @@ Debugger::CheckAllBreakpoints() assert(false); break; } - cons->Print("breakpoint #%d (%s) reached\n", i, desc.c_str()); + + // 到達メッセージを作成。 + // この時点ではまだコンソールを取得していない可能性があるので + // (-D なしで起動した場合とか)、表示せず用意するだけ。 + // コンソールが取得できたところで表示する。 + bpointmsg += string_format("breakpoint #%d (%s) reached\n", + i, desc.c_str()); } // 例外通知は通過ごとに常に下ろしておく @@ -842,7 +891,7 @@ Debugger::HelpDetails = { Shows disassemble. The first form ("D") interprets
as a physical address. The second and third form ("d", "dt") interprets
as a logical - address if the address translation is enabled. Normally,
is + address if the address translation is enabled. Normally,
is interpreted in the current privilege and current address space. You can change it by modifier. On m68k, can be specified either by function code number directly @@ -898,7 +947,7 @@ Debugger::HelpDetails = { //----- { "L", R"**( - Command: L + Command: L [=][,=[]]... Set loglevel. XXX To be written... )**" }, @@ -1045,21 +1094,7 @@ Debugger::cmd_b() // 引数取得 if (args[1][0] == '#') { // # 形式なら、指定番号のブレークポイントを削除 - int i = atoi(&args[1][1]); - if (i < 0 || i >= bpoint.size()) { - cons->Print("invaild breakpoint number: #%d\n", i); - return; - } - auto& bp = bpoint[i]; - if (bp.type == BreakpointType::Unused) { - cons->Print("invalid breakpoint number: #%d\n", i); - return; - } - cons->Print("breakpoint #%d (%08x) removed\n", - i, bp.addr); - bp.type = BreakpointType::Unused; - // 今登録されている命令ブレークの必要命令長を再計算 - RecalcInstMask(); + cmd_b_delete(); return; } @@ -1110,6 +1145,30 @@ Debugger::cmd_b_set(BreakpointType type) } } +// ブレークポイント個別削除 +// (引数の先頭が '#' なことを判定したところで呼ばれる) +// b # +void +Debugger::cmd_b_delete() +{ + int n = atoi(&args[1][1]); + if (n < 0 || n >= bpoint.size()) { + cons->Print("invalid breakpoint number: #%d\n", n); + return; + } + + auto& bp = bpoint[n]; + if (bp.type == BreakpointType::Unused) { + cons->Print("invalid breakpoint number: #%d\n", n); + return; + } + + cons->Print("breakpoint #%d removed\n", n); + bp.type = BreakpointType::Unused; + // 今登録されている命令ブレークの必要命令長を再計算 + RecalcInstMask(); +} + // 命令ブレークポイントの設定 // bi [:] [] void @@ -1275,12 +1334,12 @@ Debugger::cmd_bv() void Debugger::cmd_b_list() { - ShowMonitor(*gBreakpointMonitor); + ShowMonitor(bpoint_monitor); } // ブレークポイント一覧 (モニタ) void -Debugger::MonitorBreakpoint(TextScreen& monitor) +Debugger::MonitorUpdateBpoint(Monitor *, TextScreen& monitor) { // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890 @@ -1438,17 +1497,17 @@ Debugger::RecalcInstMask() void Debugger::cmd_brhist() { - cmd_hist_common(md->GetBrHist(), 0); + cmd_hist_common(md->GetBrHist()); } void Debugger::cmd_exhist() { - cmd_hist_common(md->GetExHist(), BranchHistory::ExHist); + cmd_hist_common(md->GetExHist()); } // ブランチ履歴、例外履歴表示の共通部分。 void -Debugger::cmd_hist_common(BranchHistory& hist, uint64 flag) +Debugger::cmd_hist_common(BranchHistory& hist) { // 表示最大行数(と向き) // 向きは bottom_to_top = true が新しいほうを下とする方向。 @@ -1475,19 +1534,19 @@ Debugger::cmd_hist_common(BranchHistory& int lines = std::min(used, maxlines) + 1; // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。 - auto size = hist.GetMonitorSize(); - TextScreen tscr; - tscr.Init(size.width, lines); + auto& histmon = hist.monitor; + auto size = histmon.GetSize(); + TextScreen screen; + screen.Init(size.width, lines); // コンソールでは下が新しいの順のほうがいい if (bottom_to_top) { - flag |= BranchHistory::BottomToTop; + screen.userdata |= BranchHistory::BottomToTop; } - tscr.userdata = flag; // 表示 - hist.MonitorUpdate(tscr); - ShowTextScreen(tscr); + MONITOR_UPDATE(histmon, screen); + ShowTextScreen(screen); } // 実行再開(continue): c [] @@ -1638,8 +1697,8 @@ Debugger::GetAddr(saddr_t *sa, bool defa if (ParseAddr(str.c_str() + addrpos, &addr) == false) { return false; } - // 偶数番地に丸める - sa->SetAddr(addr & 0xfffffffe); + // 命令境界に丸める + sa->SetAddr(addr & ~(md->inst_bytes - 1)); return true; } @@ -1745,42 +1804,45 @@ Debugger::cmd_disp() // XXX チェック } -// ログレベル設定: L +// ログレベル設定: L [=][...] void Debugger::cmd_L() { - const char *name; - int level; - - if (args.size() < 3) { - cons->Print("L \n"); + if (args.size() < 2) { + cons->Print("L [=][,[=]]...\n"); return; } - name = args[1].c_str(); - level = atoi(args[2].c_str()); - - // 短縮形とかエイリアスとか - // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい - if (strcmp(name, "sch") == 0) - name = "scheduler"; - if (strcmp(name, "scc") == 0) - name = "sio"; - - // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。 - // all はどうするか - std::string sname = name; - bool found = false; - for (auto& obj : gObjects) { - if (obj->logname == sname) { - obj->loglevel = level; - cons->Print("%s=%d\n", name, obj->loglevel); - found = true; - } - } - // 見付からない場合はエラー - if (!found) { - cons->Print("%s: invalid logname\n", name); + // 一旦全部つなげる。 + // help が混ざる場合の "L foo=1,help" と "L foo=1 help" を同じ動作に + // するため。 + std::string str; + for (int i = 1, ac = args.size(); i < ac; i++) { + if (str.empty() == false) { + str += ','; + } + str += args[i]; + } + + // で、再分解 + std::vector items = string_split(str, ','); + + // "help" があれば一覧を表示 + for (const auto& item : items) { + if (item == "help") { + std::vector list = MainApp::GetLogNames(); + for (const auto& name : list) { + cons->Print(" %s\n", name.c_str()); + } + return; + } + } + + // ログレベルを設定 + std::string errmsg; + if (MainApp::SetLogopt(items, &errmsg) == false) { + cons->Print("%s\n", errmsg.c_str()); + return; } } @@ -1810,9 +1872,9 @@ void Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode) { saddr_t saddr; - int cnt; + int row; - cnt = 10; + row = 8; // 開始アドレス saddr = m_last_addr; @@ -1821,20 +1883,76 @@ Debugger::cmd_m_common(MemoryMode access } // 2つ目の引数があれば行数 if (args.size() > 2) { - cnt = strtol(args[2].c_str(), NULL, 10); + row = strtol(args[2].c_str(), NULL, 10); + } + + // userdata を作成 + uint64 userdata = saddr.GetAddr(); + if (saddr.IsSuper() == false) { + userdata |= (1ULL << 32); + } + if (saddr.IsData()) { + userdata |= (1ULL << 33); + } + if (access_mode == MemoryMode::Logical) { + userdata |= (1ULL << 34); + } + if (lookup_mode == MMULookupMode::True) { + userdata |= (1ULL << 35); } + // CLI では毎回次を表示していってほしい + userdata |= (1ULL << 36); + TextScreen screen; + screen.Init(m_monitor.GetSize().width, row); + screen.userdata = userdata; + + MONITOR_UPDATE(m_monitor, screen); + ShowTextScreen(screen); + + // アドレスを次回継続用に保存 (Super/Data は維持) + m_last_addr = (uint32)screen.userdata; +} + +// メモリダンプモニタ +// +// userdata の下位32bit はアドレス。上位は各種フラグ。 +// +// ..| b36 | b35 | b34 | b33 | b32 | b31 .. b0 +// userdata | A | T | L | D | U | address +// | | | | +---- 1:User 0:Supervisor space +// | | | +---------- 1:Data, 0:Instruction space +// | | +---------------- 1:Logical, 0:Physical +// | +---------------------- 1:Table Lookup, 0:ATC Only +// +---------------------------- 1:Auto increment mode +void +Debugger::MonitorUpdateMemdump(Monitor *, TextScreen& screen) +{ + uint32 laddr = (uint32)screen.userdata; + bool user = (screen.userdata >> 32) & 1; + bool data = (screen.userdata >> 33) & 1; + MemoryMode access_mode = ((screen.userdata >> 34) & 1) + ? MemoryMode::Logical : MemoryMode::Physical; + MMULookupMode lookup_mode = ((screen.userdata >> 35) & 1) + ? MMULookupMode::True : MMULookupMode::None; + bool autoinc = ((screen.userdata >> 36) & 1); + + saddr_t saddr(laddr, !user, data); DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode); - for (int i = 0; i < cnt; i++) { + screen.Clear(); + + int row = screen.GetRow(); + for (int y = 0; y < row; y++) { std::string addrbuf; std::string dumpbuf; std::string charbuf; // アドレス - if (mem.FormatAddr(addrbuf) == false) { + bool ok = mem.FormatAddr(addrbuf); + screen.Print(0, y, "%s", addrbuf.c_str()); + if (ok == false) { // アドレス変換でバスエラーならここで終了 - cons->Print("%s\n", addrbuf.c_str()); break; } @@ -1844,7 +1962,7 @@ Debugger::cmd_m_common(MemoryMode access if ((int64)b < 0) { dumpbuf += "--"; - charbuf += " "; + charbuf += ' '; } else { uint32 c = (uint32)b; dumpbuf += string_format("%02x", c); @@ -1852,15 +1970,20 @@ Debugger::cmd_m_common(MemoryMode access (0x20 <= c && c <= 0x7e) ? c : '.'); } } - dumpbuf += " "; + dumpbuf += ' '; } - cons->Print("%-18s %-40s %s\n", - addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str()); + screen.Print(19, y, "%s", dumpbuf.c_str()); + screen.Print(60, y, "%s", charbuf.c_str()); } - // アドレスと Super/User 状態を次回継続用に保存 - m_last_addr = mem.laddr; + // 次回継続用に書き戻す + if (autoinc) { + union64 udata; + udata.q = screen.userdata; + udata.l = mem.laddr.GetAddr(); + screen.userdata = udata.q; + } } // プロンプトに来た最初に表示するいつものやつを再表示する @@ -1987,37 +2110,78 @@ void Debugger::cmd_show() { if (args.size() != 2) { - cons->Print("usage: show \n"); - for (auto& d : gObjects) { - if (d->logname != "?") { - cons->Print(" %s\n", d->logname.c_str()); + std::vector list; + + // 登録されているモニターだけを列挙 + // (登録されていてもサブウィンドウの ID を持っているものは除外) + for (const auto mon : gMonitorManager.GetList()) { + int id = mon->GetId(); + if (id <= ID_MONITOR_END) { + list.push_back(id); } } + + // 一覧を表示 + std::string msg = MonitorManager::MakeListString(list); + cons->Print("usage: show \n"); + cons->Print("%s", msg.c_str()); return; } + std::vector candidates; + std::vector cand_names; std::string name = args[1]; - for (auto& d : gObjects) { - if (d->logname == name) { - ShowMonitor(*d); - return; + + for (auto mon : gMonitorManager.GetList()) { + int id = mon->GetId(); + const auto& aliases = gMonitorManager.GetAliases(id); + + bool matched = false; + for (const auto& alias : aliases) { + // 部分一致したら名前はすべて覚えておく + if (starts_with_ignorecase(alias, name)) { + cand_names.push_back(alias); + matched = true; + } + } + + // 同じモニタで別名が複数回部分一致しても、 + // モニタのほうは1回として数える + if (matched) { + candidates.push_back(mon); + } + } + + if (candidates.empty()) { + // 一致しない + cons->Print("unknown monitor name: \"%s\"\n", name.c_str()); + } else if (candidates.size() == 1) { + // 確定した + Monitor& mon = *(candidates[0]); + ShowMonitor(mon); + } else { + // 候補が複数あった + std::string candstr; + for (const auto& cname : cand_names) { + candstr += ' '; + candstr += cname; } + cons->Print("ambiguous monitor name \"%s\": candidates are%s\n", + name.c_str(), candstr.c_str()); } - // 一致しない - cons->Print("show: no such objects\n"); } // モニターを更新して表示。 void -Debugger::ShowMonitor(IMonitor& monitor) +Debugger::ShowMonitor(Monitor& monitor) { // モニタを更新 TextScreen ts; - auto size = monitor.GetMonitorSize(); + auto size = monitor.GetSize(); ts.Init(size.width, size.height); - monitor.MonitorUpdate(ts); + MONITOR_UPDATE(monitor, ts); ShowTextScreen(ts); } @@ -2031,7 +2195,7 @@ Debugger::ShowTextScreen(TextScreen& mon // その際属性もエスケープシーケンスで再現する。 int col = monitor.GetCol(); int row = monitor.GetRow(); - const uint16 *src = monitor.GetBuf(); + const std::vector& src = monitor.GetBuf(); for (int y = 0; y < row; y++) { int sy = y * col; @@ -2234,20 +2398,3 @@ Debugger::ParseAddr(const char *arg, uin DebuggerMD::~DebuggerMD() { } - - -// -// ブレークポイントモニター -// - -// コンストラクタ -BreakpointMonitor::BreakpointMonitor() -{ - monitor_size = nnSize(55, 9); -} - -void -BreakpointMonitor::MonitorUpdate(TextScreen& monitor) -{ - gDebugger->MonitorBreakpoint(monitor); -}