--- nono/debugger/debugger.cpp 2026/04/29 17:04:40 1.1.1.5 +++ 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); } // コマンドライン引数によって動作を決めるところ。 @@ -141,9 +171,6 @@ Debugger::ThreadRun() cons->Print("This is debugger console\n"); // 接続ごとに初期化する値 - // XXX もうちょっときれいにしたい - d_last_addr = 0xffffffff; - m_last_addr = 0xffffffff; n_enable = false; s_enable = false; t_enable = true; @@ -154,9 +181,14 @@ Debugger::ThreadRun() break; } - // プロンプトに来た時に表示するいつものやつ + // 実時間を停止 + gRealtime.Stop(); + + // d/m をいきなり引数なしで実行した時のため、現在地にしておく。 pc = md->GetPC(); - d_last_addr = pc; + m_last_addr.Set(pc, md->IsSuper(), true); + d_last_addr.Set(pc, md->IsSuper(), false); + // プロンプトに来た時に表示するいつものやつ cmd_minus(); // コマンド処理のメインループ @@ -165,6 +197,9 @@ Debugger::ThreadRun() // 次回との差分のため、今のレジスタセットをバックアップ md->BackupRegs(); + // 実時間を再開 + gRealtime.Start(); + // メインループから戻ったということは Leave か Quit なので // どちらにしても、ここでプロンプトを手放す。 gCVPrompt->NotifyRelease(); @@ -265,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 @@ -421,7 +462,8 @@ Debugger::Check() // ブレークポイントがどれかでも成立するかを調べる。 // 1つ以上成立してブレークするなら true を返す。 // 1つも成立しておらずブレークしないなら false を返す。 -// 成立すれば内容をここで表示する。 +// このルーチン内では Console があることを期待してはいけない (プロンプトが +// ない時でも呼ばれるので)。 bool Debugger::CheckAllBreakpoints() { @@ -510,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()); } // 例外通知は通過ごとに常に下ろしておく @@ -524,20 +572,17 @@ Debugger::CheckAllBreakpoints() bool Debugger::CheckBreakpointInst(breakpoint_t& bp) { - saddr_t laddr; - // uint32 bi_inst が現在の PC 位置の命令データ (左詰め) // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。 // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。 - laddr.addr = md->GetPC(); - laddr.super = md->IsSuper(); - laddr.logical = true; + saddr_t laddr(md->GetPC(), md->IsSuper()); + DebuggerMemoryStream mem(md, laddr); // 必要なバイト数に達するまで読み足す bi_inst = 0; while (bi_inst_bytes < bi_need_bytes) { - uint64 data = md->PeekFetch(laddr); + uint64 data = mem.FetchInst(); if ((int64)data < 0) { return false; } @@ -545,7 +590,6 @@ Debugger::CheckBreakpointInst(breakpoint bi_inst <<= md->inst_bytes * 8; bi_inst |= data; bi_inst_bytes += md->inst_bytes; - laddr.addr += md->inst_bytes; } // 左詰めにする @@ -617,7 +661,7 @@ Debugger::cmd_h() { if (args.size() < 2) { // 引数なしなら一覧表示。 - Help(HelpMsgMain); + ShowHelpList(HelpListMain); return; } @@ -625,8 +669,14 @@ Debugger::cmd_h() bool try_again = false; std::string cmd = args[1]; do { + // レジスタ系のヘルプを MD から取得して、ローカル変数で足す + auto details = HelpDetails; + for (const auto& dict : md->GetHelpReg()) { + details.push_back(dict); + } - for (const auto& dict : HelpDetails) { + // そこから検索 + for (const auto& dict : details) { if (cmd == dict.first) { const auto& desc = dict.second; if (desc[0] == '=') { @@ -651,7 +701,7 @@ Debugger::cmd_h() void Debugger::cmd_hb() { - Help(HelpMsgBreakpoints); + ShowHelpList(HelpListBreakpoints); } // hr : レジスタ表示系コマンドの一覧を表示 @@ -659,12 +709,12 @@ Debugger::cmd_hb() void Debugger::cmd_hr() { - Help(md->GetRegisterHelp()); + ShowHelpList(md->GetHelpListReg()); } -// ヘルプ表示の下請け。 +// ヘルプ一覧を表示。 void -Debugger::Help(const HelpMessages& msgs) +Debugger::ShowHelpList(const HelpMessages& msgs) { cons->Print("Type \"help \" for indivisual details.\n"); for (const auto& pair : msgs) { @@ -707,7 +757,7 @@ Debugger::HelpConvert(const std::string& } /*static*/ const HelpMessages -Debugger::HelpMsgMain = { +Debugger::HelpListMain = { { "b*", "Set/show Breakpoints (Type \"hb\" for details)" }, { "brhist", "Show branch history" }, { "c", "Continue" }, @@ -727,7 +777,7 @@ Debugger::HelpMsgMain = { }; /*static*/ const HelpMessages -Debugger::HelpMsgBreakpoints = { +Debugger::HelpListBreakpoints = { { "b", "Show all breakpoints" }, { "b arg..","Set/Delete breakpoint" }, { "bm", "Set memory breakpoint" }, @@ -835,15 +885,24 @@ Debugger::HelpDetails = { //----- { "d", R"**( Command: D
] [] - Command: d [[:]
] [] - Command: dt [[:]
] [] + Command: d [[:]
] [] + Command: dt [[:]
] [] 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 - interpreted in the current privilege. You can force this by - specifier. "s" means the supervisor and "u" means the user privilege. + 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 + ('1', '2', '5', and '6) or by one or more following modifiers: + 's' .. Supervisor mode 'u' .. User mode + 'd' .. Data space 'p' or 'i' .. Program space + On m88k, can be specified by using combination of privilege + modifier ('s' or 'u', same as above) and following CMMU identifiers: + 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU + Or CMMU can also be identified by CMMU number (like '6' or '7'). + "d" only looks up in ATC (on m68k) or in BATC/PATC (on m88k). "dt" will simulate to search the page table in addition to that. )**" }, @@ -888,7 +947,7 @@ Debugger::HelpDetails = { //----- { "L", R"**( - Command: L + Command: L [=][,=[]]... Set loglevel. XXX To be written... )**" }, @@ -896,15 +955,24 @@ Debugger::HelpDetails = { //----- { "m", R"**( Command: M
] [] - Command: m [[:]
] [] - Command: mt [[:]
] [] + Command: m [[:]
] [] + Command: mt [[:]
] [] Shows memory dump. The first form ("M") interprets
as a physical address. The second and third form ("m", "mt") interprets
as a logical address if the address translation is enabled. Normally,
is - interpreted in the current privilege. You can force this by - specifier. "s" means the supervisor and "u" means the user privilege. + 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 + ('1', '2', '5', and '6) or by one or more following modifiers: + 's' .. Supervisor mode 'u' .. User mode + 'd' .. Data space 'p' or 'i' .. Program space + On m88k, can be specified by using combination of privilege + modifier ('s' or 'u', same as above) and following CMMU identifiers: + 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU + Or CMMU can also be identified by CMMU number (like '6' or '7'). + "m" only looks up in ATC (on m68k) or in BATC/PATC (on m88k). "mt" will simulate to search the page table in addition to that. )**" }, @@ -1026,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; } @@ -1053,7 +1107,7 @@ void Debugger::cmd_bm() { // XXX m68k では未サポート - if (dynamic_cast(md)) { + if (md->arch == Arch::M680x0) { cons->Print("bm not supported yet on m68k\n"); return; } @@ -1091,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 @@ -1256,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 @@ -1419,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 が新しいほうを下とする方向。 @@ -1456,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 [] @@ -1490,50 +1568,137 @@ Debugger::cmd_c() // 引数などからアドレスを取得するごった煮共通ルーチン。 // +// 引数がなければ sa->addr には書き込まずに true で帰る (ので、呼び出し側が +// 事前に適切なデフォルト値をセットしておくと、それがそのまま使われる)。 // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。 -// 引数がなければ sa->addr には書き込まない、ただし sa->addr が 0xffffffff -// (last_addr がまだ使われていない) なら 現在の PC に置き換える。 -// アドレスに "s:" "u:" が前置してある場合はこれをパースして sa->super に -// 書き込む。指定がなければ sa->super には書き込まない。 -// つまり、呼び出し側で sa->addr に last_addr、sa->super に現在の CPU の -// IsSuper() を書き込んだ状態でこれを呼ぶと全部いろいろやってくれる。 +// アドレスには空間修飾子を前置可能、省略の場合はいずれも現在値を使う。 +// エラーならメッセージを表示して false を返す。 // sa は in/out パラメータ。 bool -Debugger::GetAddr(saddr_t *sa) +Debugger::GetAddr(saddr_t *sa, bool default_data) { if (args.size() < 2) { - // 引数なしなら、呼び出し側が sa->addr にセットした前回の値 - // (たぶん *_last_addr) をそのまま使う。ただしそれが 0xffffffff なら、 - // まだ前回値が一度もセットされていないので、仕方ないので PC を使う。 - if (sa->addr == 0xffffffff) { - sa->addr = md->GetPC(); - } + // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。 return true; } - // 引数ありならパースする。 + // 引数ありならパースする。アドレスが明示的に指定されたので、 + // ここから先の省略箇所は前回値ではなくデフォルト値で補完する。 + std::string& str = args[1]; uint32 addr; - int addrpos = 0; - // スーパーバイザ/ユーザ修飾子があればそれで上書き。 - // なければ何もしない (呼び出し側がセットした既定値を使う)。 - // XXX 将来 CMMU ID ("0:" … "7:") とかもあったほうがいいか - if (strncmp(str.c_str(), "s:", 2) == 0) { - sa->super = true; - addrpos = 2; - } else if (strncmp(str.c_str(), "u:", 2) == 0) { - sa->super = false; - addrpos = 2; + // ':' があればその前はアドレス空間修飾子 + int mod_super = -1; // 'u'/'s' + int mod_prog = -1; // 'd'/'p' or 'd'/'i' + int mod_num = -1; // '0'..'7' + auto addrpos = str.find(':'); + if (addrpos == std::string::npos) { + // なければ先頭からアドレス + addrpos = 0; + } else { + // ':' が途中にあれば、その前が修飾子。 + // ':' が先頭なら修飾子が空文字列という扱いでいいか。 + std::string mod = str.substr(0, addrpos); + addrpos++; + for (auto ch : mod) { + if (ch == 'u') { + if (mod_super == 1) + goto error; + mod_super = 0; + } else if (ch == 's') { + if (mod_super == 0) + goto error; + mod_super = 1; + } else if (ch == 'd') { + if (mod_prog == 1) + goto error; + mod_prog = 0; + } else if (ch == 'p' || ch == 'i') { + // m68k では 'P'rogram space、m88k では 'I'nstruction CMMU… + if (mod_prog == 0) + goto error; + mod_prog = 1; + } else if ('0' <= ch && ch <= '7') { + int num = ch - '0'; + if (md->arch == Arch::M680x0) { + // m68k では FC 指定は Super/User 指定を含んでおり、 + // 値指定は他の修飾子とは同時に指定できない。 + if (mod_super >= 0 || mod_prog >= 0 || + (mod_num >= 0 && mod_num != num)) { + goto error; + } + // 有効な値は 1,2,5,6 のみ + if (num == 0 || num == 3 || num == 4 || num == 7) { + cons->Print("%c: invalid address space\n", ch); + } + mod_num = num; + mod_super = mod_num >> 2; // FC2 + mod_prog = (mod_num & 3) - 1; // FC1,FC0 + } else if (md->arch == Arch::M88xx0) { + // m88k では CMMU 指定と、Super/User 指定は独立。 + if (mod_prog >= 0 || + (mod_num >= 0 && mod_num != num)) { + goto error; + } + mod_num = num; + mod_prog = (mod_num & 1); // CMMU7 が Inst + } + } else { + cons->Print("%c: unknown address space modifier\n", ch); + return false; + } + continue; + + error: + cons->Print("%s: address space modifier '%c' conflicts\n", + mod.c_str(), ch); + return false; + } + + // 数値指定を機種ごとに読み替える + if (mod_num >= 0) { + if (md->arch == Arch::M680x0) { + // m68k なら値指定で全部確定する + // "1:" -> User/Data + // "2:" -> User/Program + // "5:" -> Super/Data + // "6:" -> Super/Program + mod_super = (mod_num & 4) ? true : false; + mod_prog = (mod_num & 3) - 1; + } else if (md->arch == Arch::M88xx0) { + // m88k では mod_num を CMMU ID とする(?)。 + // XXX まだ CPU は1つしかないので雑に処理。 + // "6" -> Data (CPU#0) + // "7" -> Program (CPU#0) + if (mod_num < 6) { + cons->Print("%d: CMMU#%d not installed\n", + mod_num, mod_num); + return false; + } + mod_prog = mod_num - 6; + } + } + } + + // アドレス空間修飾子のうち省略箇所はデフォルト状態で補完 + // - 's'/'u' いずれもなければ現在値。 + // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。 + if (mod_super < 0) { + mod_super = md->IsSuper(); } + if (mod_prog < 0) { + mod_prog = !default_data; + } + sa->SetSuper(mod_super); + sa->SetData(!mod_prog); // アドレス if (ParseAddr(str.c_str() + addrpos, &addr) == false) { return false; } - // 偶数番地に丸める - addr &= 0xfffffffe; - sa->addr = addr; + // 命令境界に丸める + sa->SetAddr(addr & ~(md->inst_bytes - 1)); return true; } @@ -1542,26 +1707,26 @@ Debugger::GetAddr(saddr_t *sa) void Debugger::cmd_d() { - cmd_d_common(MemdumpMode::Logical_atc); + cmd_d_common(MemoryMode::Logical, MMULookupMode::False); } // 逆アセンブル: dt [[SU:]] []] (論理、テーブルサーチあり) void Debugger::cmd_dt() { - cmd_d_common(MemdumpMode::Logical_search); + cmd_d_common(MemoryMode::Logical, MMULookupMode::True); } // 逆アセンブル: D [ []] (物理) void Debugger::cmd_D() { - cmd_d_common(MemdumpMode::Physical); + cmd_d_common(MemoryMode::Physical, MMULookupMode::None); } // 逆アセンブル共通 void -Debugger::cmd_d_common(MemdumpMode mode) +Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode) { saddr_t saddr; int cnt; @@ -1569,9 +1734,8 @@ Debugger::cmd_d_common(MemdumpMode mode) cnt = 10; // 開始アドレス - saddr.addr = d_last_addr; - saddr.super = md->IsSuper(); - if (GetAddr(&saddr) == false) { + saddr = d_last_addr; + if (GetAddr(&saddr, false) == false) { return; } // 2つ目の引数があれば行数 @@ -1579,13 +1743,7 @@ Debugger::cmd_d_common(MemdumpMode mode) cnt = strtol(args[2].c_str(), NULL, 10); } - // XXX そのうちなんとかする - if (mode == MemdumpMode::Logical_search) { - saddr.logical = true; - saddr.search = true; - } else if (mode == MemdumpMode::Logical_atc) { - saddr.logical = true; - } + DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode); for (int i = 0; i < cnt; i++) { std::string addrbuf; @@ -1593,21 +1751,27 @@ Debugger::cmd_d_common(MemdumpMode mode) std::string mnembuf; std::vector local_ir; - if (FormatDumpAddr(saddr, addrbuf)) { - md->Disassemble(saddr, mnemonic, local_ir); - // オフライン用に整形 - mnembuf = md->FormatDisasm(mnemonic, local_ir); + // アドレス + if (mem.FormatAddr(addrbuf) == false) { + // アドレス変換でバスエラーならここで終了 + cons->Print("%s\n", addrbuf.c_str()); + break; } - // アドレス変換でバスエラーが起きてもここは表示する + + // 逆アセンブル + md->Disassemble(mem, mnemonic, local_ir); + // オフライン用に整形 + mnembuf = md->FormatDisasm(mnemonic, local_ir); + cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); // 変換できなければここで終了 if (local_ir.size() == 0) break; - saddr.addr += local_ir.size(); } - d_last_addr = saddr.addr; + // アドレスと Super/User 状態を次回継続用に保存 + d_last_addr = mem.laddr; } // 表示レジスタ選択 @@ -1640,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; } } @@ -1683,84 +1850,119 @@ Debugger::cmd_L() void Debugger::cmd_m() { - cmd_m_common(MemdumpMode::Logical_atc); + cmd_m_common(MemoryMode::Logical, MMULookupMode::False); } // メモリダンプ: mt [ []] (論理、テーブルサーチあり) void Debugger::cmd_mt() { - cmd_m_common(MemdumpMode::Logical_search); + cmd_m_common(MemoryMode::Logical, MMULookupMode::True); } // メモリダンプ: M [ []] (物理) void Debugger::cmd_M() { - cmd_m_common(MemdumpMode::Physical); + cmd_m_common(MemoryMode::Physical, MMULookupMode::None); } // メモリダンプ共通 void -Debugger::cmd_m_common(MemdumpMode mode) +Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode) { saddr_t saddr; - int cnt; + int row; - cnt = 10; + row = 8; // 開始アドレス - saddr.addr = m_last_addr; - saddr.super = md->IsSuper(); - if (GetAddr(&saddr) == false) { + saddr = m_last_addr; + if (GetAddr(&saddr, true) == false) { return; } // 2つ目の引数があれば行数 if (args.size() > 2) { - cnt = strtol(args[2].c_str(), NULL, 10); + row = strtol(args[2].c_str(), NULL, 10); } - // XXX - if (mode == MemdumpMode::Logical_search) { - saddr.logical = true; - saddr.search = true; - } else if (mode == MemdumpMode::Logical_atc) { - saddr.logical = true; + // 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); - for (int i = 0; i < cnt; i++) { + 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); + + screen.Clear(); + + int row = screen.GetRow(); + for (int y = 0; y < row; y++) { std::string addrbuf; std::string dumpbuf; std::string charbuf; - if (FormatDumpAddr(saddr, addrbuf) == false) { - // アドレス変換でバスエラーならダンプもくそもない - cons->Print("%s\n", addrbuf.c_str()); - // ここで終わる? - // XXX 16バイト先を試してみるとか? + // アドレス + bool ok = mem.FormatAddr(addrbuf); + screen.Print(0, y, "%s", addrbuf.c_str()); + if (ok == false) { + // アドレス変換でバスエラーならここで終了 break; } for (int j = 0; j < 8; j++) { - uint64 paddr; - // もう一回アドレス変換するのやや冗長だが仕方ない - if (saddr.IsLogical() && md->MMUEnabled()) { - // アドレス変換 - paddr = md->TranslateAddr(saddr); - if ((int64)paddr < 0) { - // バスエラーが起きたら改行して仕切り直し - break; - } - } else { - paddr = saddr.addr; - } - for (int k = 0; k < 2; k++) { - uint64 b = vm_phys_peek_8(paddr + k); + uint64 b = mem.Fetch8(); if ((int64)b < 0) { dumpbuf += "--"; - charbuf += " "; + charbuf += ' '; } else { uint32 c = (uint32)b; dumpbuf += string_format("%02x", c); @@ -1768,13 +1970,20 @@ Debugger::cmd_m_common(MemdumpMode mode) (0x20 <= c && c <= 0x7e) ? c : '.'); } } - dumpbuf += " "; - saddr.addr += 2; + 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()); + } + + // 次回継続用に書き戻す + if (autoinc) { + union64 udata; + udata.q = screen.userdata; + udata.l = mem.laddr.GetAddr(); + screen.userdata = udata.q; } - m_last_addr = saddr.addr; } // プロンプトに来た最初に表示するいつものやつを再表示する @@ -1791,24 +2000,25 @@ Debugger::cmd_minus() md->ShowRegister(cons, tmpargs); } - // 論理アクセス時、ATC ミスが分かったほうが便利だと思う - saddr_t laddr; - laddr.addr = pc; - laddr.super = md->IsSuper(); - laddr.logical = true; - laddr.search = false; - // 現在の PC の位置を逆アセンブル。ここで ir を更新。 std::string addrbuf; std::string mnemonic; std::string mnembuf; ir.clear(); - // アドレスを表示用に - if (FormatDumpAddr(laddr, addrbuf)) { - md->Disassemble(laddr, mnemonic, ir); - // ライブ表示用に整形 - mnembuf = md->FormatDisasmLive(mnemonic, ir); + + // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし + saddr_t laddr(pc, md->IsSuper()); + DebuggerMemoryStream mem(md, laddr); + // アドレス + if (mem.FormatAddr(addrbuf) == false) { + cons->Print("%s\n", addrbuf.c_str()); + return; } + // 逆アセンブル + md->Disassemble(mem, mnemonic, ir); + // ライブ表示用に整形 + mnembuf = md->FormatDisasmLive(mnemonic, ir); + cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); } @@ -1856,30 +2066,23 @@ Debugger::cmd_n_common(bool trace) void Debugger::SetNBreakpoint() { - saddr_t laddr; - uint32 op; - - // 毎回他と干渉なく現在位置の命令語を取り出すほうが安全か - laddr.addr = md->GetPC(); - laddr.logical = true; - laddr.super = md->IsSuper(); - op = md->PeekFetch(laddr); + saddr_t laddr(md->GetPC(), md->IsSuper()); + DebuggerMemoryStream mem(md, laddr); - // 命令がサブルーチンやトラップなどステップインできる命令か - if (md->IsOpStepIn(op)) { + // 命令がサブルーチンやトラップなどステップインできる命令か。 + if (md->IsOpStepIn(mem)) { // この次の命令にブレークをかける if (md->inst_bytes_fixed != 0) { // 固定長命令 - n_breakaddr = md->GetPC() + md->inst_bytes_fixed; + n_breakaddr = (uint32)mem.laddr; } else { // 可変長なら逆アセンブルしてみるしか std::string mnemonic; std::vector v; // XXX 失敗したらどうすべ - md->Disassemble(laddr, mnemonic, v); - if (v.size() > 0) { - n_breakaddr = md->GetPC() + v.size(); - } + mem.ResetAddr(md->GetPC()); + md->Disassemble(mem, mnemonic, v); + n_breakaddr = (uint32)mem.laddr; } } else { // そうでなければ1命令進める。 @@ -1907,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); } @@ -1951,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; @@ -2145,88 +2389,12 @@ Debugger::ParseAddr(const char *arg, uin return true; } -// (論理、物理)アドレスを表示用に整形。 -// 逆アセンブル表示、メモリダンプ表示等で共通。 -// アドレス変換でバスエラーが起きたら false を返す (この場合も str は有効)。 -// 末尾を空白パディングはしていないので必要なら "%-18s" 等して使うこと。 -// 123456789012345678 -// "00112233:44556677:" -bool -Debugger::FormatDumpAddr(saddr_t laddr, std::string& str) -{ - bool rv = true; - - // laddr は常に表示 - str = string_format("%08x", laddr.addr); - - if (laddr.IsLogical() && md->MMUEnabled()) { - // アドレス変換してみる - uint64 paddr = md->TranslateAddr(laddr); - if ((int64)paddr < 0) { - // MMU 時点でバスエラー - str += ":BusErr"; - rv = false; - } else if (paddr == laddr.addr) { - // PA==VA - str += "=PA"; - } else { - // PA!=VA - str += string_format(":%08x:", (uint32)paddr); - } - } else { - // 物理アクセス - str += ":"; - } - - return rv; -} // // MD // -// 指定のアドレスから1命令語を読み出し、下(右)詰めにして返す。 -// 読み込めなければ (uint64)-1 を返す。 -uint64 -DebuggerMD::PeekFetch(saddr_t laddr) -{ - uint64 paddr; - uint32 data; - - // サーチあり、命令空間は固定 - laddr.search = true; - laddr.data = false; - - if (laddr.IsLogical() && MMUEnabled()) { - paddr = TranslateAddr(laddr); - if ((int64)paddr < 0) - return (uint64)-1; - } else { - paddr = laddr.addr; - } - - // 物理空間から1語読み込む - data = 0; - for (int i = 0; i < inst_bytes; i++) { - data <<= 8; - data |= vm_phys_peek_8(paddr++); - } - return data; -} - - -// -// ブレークポイントモニター -// - -// コンストラクタ -BreakpointMonitor::BreakpointMonitor() -{ - monitor_size = nnSize(55, 9); -} - -void -BreakpointMonitor::MonitorUpdate(TextScreen& monitor) +// デストラクタ +DebuggerMD::~DebuggerMD() { - gDebugger->MonitorBreakpoint(monitor); }