--- nono/debugger/debugger.cpp 2026/04/29 17:04:43 1.1.1.6 +++ nono/debugger/debugger.cpp 2026/04/29 17:05:09 1.1.1.11 @@ -4,77 +4,157 @@ // Licensed under nono-license.txt // -#include "console.h" +// +// デバッガ +// + +// +// VM スレッド デバッガスレッド HostCOM +// condvar +// | | |<--- 入力 +// | |<----------------------| +// | | RxCallback +// | | +// | |---------------------->| +// | | HostCOMDevice::Tx() |---> 出力 + +// |<---------------------------| +// | is_prompt = true; | デバッガスレッドからプロンプトを出したい +// | Message(MPU_TRACE); | 場合は MPU (VM) をトレースモードにする。 +// | | その際 is_prompt を立てておくことで止まる。 +// | | +// |--------------------------->| +// | condvar REQUEST_PROMPT | VM スレッドからプロンプトを出したい場合 +// | | (上述の例も含む) は条件変数で通知。 +// | | +// |<---------------------------| +// | condvar prompt_released | プロンプトを出している間 VM スレッドは +// | | 条件変数で待機しているので、これを起こす +// | | ことで実行再開。 + +#include "debugger.h" #include "debugger_private.h" #include "debugger_m680x0.h" #include "debugger_m88xx0.h" +#include "hostcom.h" #include "mainapp.h" #include "mystring.h" -#include "mythread.h" +#include "power.h" +#include "scheduler.h" +#include "sync.h" +#include "uimessage.h" +#include "vectortable.h" #include -#include +#if defined(HAVE_BSD_STDIO_H) +#include +#endif -static Debugger *gDebugger; -BreakpointMonitor *gBreakpointMonitor; -CVPrompt *gCVPrompt; +static int readfunc(void *, char *, int); +static int writefunc(void *, const char *, int); -static void *debugger_run(void *); +// グローバル参照用 +Debugger *gDebugger; -// 初期化。 -// この時点で VM が初期化されていること。 -void -debugger_init() +// メモリダンプモニタを外部から取得 +Monitor& +debugger_memdump_monitor(int n) +{ + assert(n < MAX_MEMDUMP_MONITOR); + return gDebugger->memdump_monitor[n]; +} + +// コンストラクタ +Debugger::Debugger() + : inherited("Debugger") { - gDebugger = new Debugger(); - gDebugger->Init(); + // ベクタテーブル + pVectorTable.reset(new VectorTable(gMainApp.GetVMType())); + gVectorTable = pVectorTable.get(); + + // ブレークポイントモニター + bpoint_monitor.func = ToMonitorCallback(&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 = ToMonitorCallback(&Debugger::MonitorUpdateMemdump); + mon.SetSize(76, 16); + mon.Regist(ID_MONITOR_MEMDUMP(i)); + } + // m/M コマンド用。こっちは Regist 不要 + m_monitor.func = ToMonitorCallback(&Debugger::MonitorUpdateMemdump); + m_monitor.SetSize(76, 16); +} - gBreakpointMonitor = new BreakpointMonitor(); - gCVPrompt = new CVPrompt(); +// デストラクタ +Debugger::~Debugger() +{ + // fclose(fflush) にあたり hostcom へのアクセスが発生するので、 + // hostcom より先に片付けておかなければならない。 + Close(); + + if ((bool)hostcom) { + hostcom->SetCallbackDevice(NULL); + } - // デバッガスレッド起動 - pthread_t th; - pthread_create(&th, NULL, debugger_run, NULL); + TerminateThread(); + gDebugger = NULL; } -// デバッガスレッドのエントリポイント -static void * -debugger_run(void *dummy) +// ログレベル設定 +void +Debugger::SetLogLevel(int loglevel_) { - PTHREAD_SETNAME("Debugger"); - pthread_detach(pthread_self()); + inherited::SetLogLevel(loglevel_); - gDebugger->ThreadRun(); - return NULL; + // ホストドライバを従属させる + if ((bool)hostcom) { + hostcom->SetLogLevel(loglevel_); + } } -// コンストラクタ -Debugger::Debugger() +bool +Debugger::Create() { - if (gMPU680x0) { - md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU()); - } else if (gMPU88xx0) { - md = new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU()); - } else { - throw "unknown mpu"; + // ホストドライバを作成 + try { + hostcom.reset(new HostCOMDevice("Debugger")); + } catch (...) { + return false; } + + hostcom->SetRxCallback(ToDeviceCallback(&Debugger::RxCallback)); + hostcom->SetAcceptCallback(ToDeviceCallback(&Debugger::AcceptCallback)); + hostcom->SetCallbackDevice(this); + + return true; } -// コマンドライン引数によって動作を決めるところ。 -// 名前がこれでいいのかはあるけど。 -void +// 初期化 +bool Debugger::Init() { + if (gMPU680x0) { + md.reset(new DebuggerMD_m680x0(this, gMPU680x0->GetCPU())); + } else if (gMPU88xx0) { + md.reset(new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU())); + } else { + assertmsg(false, "unknown mpu"); + } + // -d なら CPU 起動時点で停止してプロンプトを待つ if (gMainApp.debug_on_start) { - md->ReqSet(CPU_REQ_PROMPT); + is_pause = true; + // この時点ではまだ MPU にメッセージを送ることはできない } // -b ならブレークポイント設定 for (auto& str : gMainApp.debug_breakaddr) { breakpoint_t bp; - md->ReqSet(CPU_REQ_TRACE); - // , で分離できたら を取り出す int pos = str.find(','); if (pos != std::string::npos) { @@ -87,48 +167,82 @@ Debugger::Init() // そしてどちらにしても を取り出す if (!ParseAddr(str.c_str(), &bp.addr)) { warnx("\"%s\": Invalid breakpoint address", str.c_str()); - return; + return false; } // 登録 bp.type = BreakpointType::Address; AddBreakpoint(bp); } - // どこでやるべか - signal(SIGPIPE, SIG_IGN); + // 入出力を FILE で扱う + cons = funopen(this, readfunc, writefunc, NULL, NULL); + if (cons == NULL) { + warnx("funopen2 failed"); + return false; + } + + return true; } // デバッガスレッド void Debugger::ThreadRun() { - // XXX どこかでコンソールの選択とパラメータの取得 - if (gMainApp.debug_on_console) { - cons = new ConsoleStdio(); - } else { - cons = new ConsoleTCP(); - } - if (cons->Init() == false) { - delete cons; - return; - } - // disp_regs の初期値設定。 // 再接続でも継続してていいような気がするのでループ外で初期化。 disp_regs.clear(); disp_regs.push_back("r"); + // MPU のトレース状態の初期化は vm/mpu* 側のリセット例外で行っている。 + for (;;) { - // 着信待ち - if (Accept() == false) { + // 何か起きるまで待つ + uint32 req; + { + std::unique_lock lock(mtx); + cv_request.wait(lock, [&] { return request != 0; }); + req = request; + request = 0; + } + + if ((req & REQUEST_EXIT)) { break; } - cons->InitEditLine("> "); + if ((req & REQUEST_RXCHAR)) { + // コンソールからの文字入力 + int c; + while ((c = hostcom->Rx()) >= 0) { + Input(c); + } + } + + if ((req & REQUEST_ACCEPT)) { + // TCP 待ち受けに着信があればすぐにプロンプトを出したい + Input('\n'); + } - // デバッガプロンプトにいる間はトレースオン - md->ReqSet(CPU_REQ_TRACE); + if ((req & REQUEST_PROMPT)) { + // VM 停止(したのでプロンプトモードへ) + EnterPrompt(); + } + } + LeavePrompt(); + Close(); +} + +// コンソールをクローズする +void +Debugger::Close() +{ + if (cons) { + fclose(cons); + cons = NULL; + } +} + +#if 0 bool first = true; for (;;) { // 接続後の1回目だけ実行するもの。 @@ -138,198 +252,324 @@ Debugger::ThreadRun() // greeting はプロンプトが取れる前にもう表示したい。 // 何らかの事故でプロンプトが取れなくても、ここまでは接続 // できてることが分かるように。 - cons->Print("This is debugger console\n"); + fprintf(cons, "This is debugger console\n"); // 接続ごとに初期化する値 n_enable = false; s_enable = false; t_enable = true; } + } + } - // プロンプトが取れるのを待つ - if (AcquirePrompt() == false) { - break; - } +#endif - // d/m をいきなり引数なしで実行した時のため、現在地にしておく。 - pc = md->GetPC(); - m_last_addr.Set(pc, md->IsSuper(), true); - d_last_addr.Set(pc, md->IsSuper(), false); - // プロンプトに来た時に表示するいつものやつ - cmd_minus(); +// スレッドに終了指示 +void +Debugger::Terminate() +{ + std::unique_lock lock(mtx); + request |= REQUEST_EXIT; + cv_request.notify_one(); +} - // コマンド処理のメインループ - auto action = MainLoop(); +// funopen の read コールバック +static int +readfunc(void *cookie, char *buf, int bufsize) +{ + return ((Debugger *)cookie)->ReadFunc(buf, bufsize); +} - // 次回との差分のため、今のレジスタセットをバックアップ - md->BackupRegs(); +// funopen の write コールバック +static int +writefunc(void *cookie, const char *buf, int len) +{ + return ((Debugger *)cookie)->WriteFunc(buf, len); +} - // メインループから戻ったということは Leave か Quit なので - // どちらにしても、ここでプロンプトを手放す。 - gCVPrompt->NotifyRelease(); +// funopen の read コールバックの本体 +int +Debugger::ReadFunc(char *buf, int bufsize) +{ + char *d = buf; + char *end = buf + bufsize; - // quit ならここでループを1段抜ける - if (action == CommandAction::Quit) { - break; - } + for (; d < end; ) { + if ((bool)hostcom == false) { + errno = EIO; + return -1; } - // デバッガを抜けるのでトレースオフ - md->ReqClr(CPU_REQ_TRACE); - - cons->Close(); - if (gMainApp.debug_on_console) { + int c = hostcom->Rx(); + if (c < 0) { break; } + *d++ = c; } - - delete cons; + return (d - buf); } -// コンソールに接続されるのを待つ。 -// うーん、なんだこれ。 -// -// +--- debug_on_console (-D オプション) -// | +- debug_on_start (-d オプション) -// | | 動作 -// 0 0 TCP、Accept で待機。 -// 0 1 TCP、Accpet で待機。 -// 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。 -// 1 1 stdio、起動時にプロンプトで止まる。 -bool -Debugger::Accept() +// funopen の write コールバックの本体 +int +Debugger::WriteFunc(const char *buf, int len) { - if (gMainApp.debug_on_console == false) { - // TCP なら accept が成功するのを待つ - cons->Accept(); - } else { - if (gMainApp.debug_on_start == false) { - // stdio、起動時にプロンプトで止まらない。 - // この場合キー入力かブレークポイントなどで止まった時点で - // プロンプトを出したいのでちょっと面倒なことに。 - for (;;) { - bool is_prompt = gCVPrompt->WaitAcquire(200); - if (is_prompt) { - break; - } + const char *s = buf; + const char *end = buf + len; - int r = cons->Poll(200); - if (r < 0) { - cmd_q(); - return false; - } - if (r > 0) { - break; - } + for (; s < end; ) { + if ((bool)hostcom == false) { + errno = EIO; + return -1; + } + + // ホストスレッドのキューが満杯なら空くまで待つ。うーん…。 + // XXX 無期限で大丈夫だろうか + int c = *s++; + if (c == '\n') { + // ここで LF を CRLF にする? + // (TELNET に出力するのに必要) + while (hostcom->Tx('\r') == false) { + usleep(10); } - } else { - // stdio、起動時にプロンプトで止まる。 - // この場合は無条件に成功でよい。 + } + while (hostcom->Tx(c) == false) { + usleep(10); } } + return (s - buf); +} - return true; +// ホストからの1文字受信通知 (HostCOM スレッドから呼ばれる) +void +Debugger::RxCallback() +{ + // デバッガスレッドに通知 + std::unique_lock lock(mtx); + request |= REQUEST_RXCHAR; + cv_request.notify_one(); } -// プロンプトが取れるのを待つ -// 失敗すれば false を返す -bool -Debugger::AcquirePrompt() +// ホストからの着信通知 (HostCOM スレッドから呼ばれる) +void +Debugger::AcceptCallback() { - md->ReqSet(CPU_REQ_PROMPT); + // デバッガスレッドに通知 + std::unique_lock lock(mtx); + request |= REQUEST_ACCEPT; + cv_request.notify_one(); +} - for (;;) { - bool is_prompt = gCVPrompt->WaitAcquire(200); - if (is_prompt) - break; +// ホストからの1文字入力 +void +Debugger::Input(int c) +{ + // '^@' は捨てる + // (意図的にも入力できるが nc が TELNET オプション扱えなくて送ってくる) + if (c == '\0') { + return; + } - // コンソールを定期観測して、入力があればブレーク要求 - if (cons->Poll()) { - // この入力は drop してみる - if (cons->Gets(cmdbuf) == false) { - // EOF or Error - cmd_q(); - return false; + // HostCOM からは改行で CR が来るようなので LF にしておく + if (c == '\r') { + c = '\n'; + } + + if (is_prompt == false) { + // プロンプトでない時は、Enter か ^C でプロンプトを出す + if (c == '\n' || c == '\x03') { + // MPU に一時停止を要求 + is_pause = true; + gScheduler->SendMessage(MessageID::MPU_TRACE, true); + } + } else { + // プロンプト中なら行入力 + + // 先に自力エコーバック?? + fputc(c, cons); + fflush(cons); + + if (c != '\n') { + cmdbuf.push_back(c); + } else { + // Enter ならここでコマンド実行 + + auto act = Command(); + + // 次回との差分のため、今のレジスタセットをバックアップ + md->BackupRegs(); + + switch (act) { + case CommandAction::Stay: + // プロンプトに留まるならここで、次行のプロンプト? + PrintPrompt(); + break; + case CommandAction::Leave: + // プロンプトを抜ける + LeavePrompt(); + break; + case CommandAction::Quit: + // アプリケーション自体を終了 + LeavePrompt(); + UIMessage::Post(UIMessage::APPEXIT); } - md->ReqSet(CPU_REQ_PROMPT); } } - return true; } -// コマンド処理のメインループ。 -// コマンドが Stay ならこの中で処理を継続。 -// コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。 +// コマンドモード(プロンプト)に入る +void +Debugger::EnterPrompt() +{ + is_prompt = true; + + // d/m をいきなり引数なしで実行した時のため、現在地にしておく。 + pc = md->GetPC(); + m_last_addr.Set(pc, md->IsSuper(), true); + d_last_addr.Set(pc, md->IsSuper(), false); + + // ブレークポイント到達メッセージがあればここで表示 + if (bpointmsg.empty() == false) { + fprintf(cons, "%s", bpointmsg.c_str()); + bpointmsg.clear(); + } + + // プロンプトのたびに表示するやつ + cmd_minus(); + + PrintPrompt(); +} + +// コマンドモード(プロンプト)から出る +void +Debugger::LeavePrompt() +{ + // MPU のトレース状態を変更 + gScheduler->SendMessage(MessageID::MPU_TRACE, IsTrace()); + + is_prompt = false; + + // 最後に VM スレッドで待機している Exec() を起こす + { + std::unique_lock lock(mtx); + prompt_released = true; + cv_prompt.notify_one(); + } +} + +// プロンプトを表示 +void +Debugger::PrintPrompt() +{ + fprintf(cons, "> "); + fflush(cons); +} + +// コマンド実行 Debugger::CommandAction -Debugger::MainLoop() +Debugger::Command() { - for (;;) { - cons->Prompt(); - if (cons->Gets(cmdbuf) == false) { - // EOF or Error - return CommandAction::Quit; + string_rtrim(cmdbuf); + + if (cmdbuf.empty()) { + // 空行なら、直前のコマンドをもう一度。 + // 前行がなければ何もせずもう一度プロンプトを表示するかね。 + if (last_cmdbuf.empty()) { + return CommandAction::Stay; } + cmdbuf = last_cmdbuf; + } else { + // 入力された行を次回のために保存 + last_cmdbuf = cmdbuf; + } - string_rtrim(cmdbuf); + // 行を args に分解 + ParseCmdbuf(); + cmdbuf.clear(); - if (!cmdbuf.empty()) { - // コマンドが入力されれば次回のために保存 - last_cmdbuf = cmdbuf; - } else { - // 空行が入力されれば直前のコマンドをもう一度 - // 前行がなければ何もせずもう一度プロンプトを表示するかね - if (last_cmdbuf.empty()) - continue; - cmdbuf = last_cmdbuf; + // 前回の値が有効なのはコマンドが連続した時だけ + + // コマンドをテーブルから探す + auto it = std::find_if(cmdtable.begin(), cmdtable.end(), + [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; }); + if (it != cmdtable.end()) { + auto cmd = *it; + + // 見付かれば実行 + (this->*(cmd.func))(); + + return cmd.action; + } + + // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系 + if (args[0][0] == 'r') { + // ShowRegister() は処理したら true を返す。 + // "r" 系コマンドはすべて Stay。 + if (md->ShowRegister(cons, args)) { + return CommandAction::Stay; } + } - // 行を args に分解 - ParseCmdbuf(); + // 知らないコマンドも Stay 相当。 + fprintf(cons, "%s: unknown command\n", args[0].c_str()); + // この行は次回空エンターで再発行しないでいい。 + last_cmdbuf.clear(); + return CommandAction::Stay; +} - // 前回の値が有効なのはコマンドが連続した時だけ +// ブレークポイントとかを調べる。1命令ごとに呼び出される。 +// (VM スレッドから呼ばれる) +void +Debugger::Exec() +{ + if (Check()) { + // 実時間を停止 + gSync->StopRealTime(); - // コマンドをテーブルから探す - auto it = std::find_if(cmdtable.begin(), cmdtable.end(), - [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; }); - if (it != cmdtable.end()) { - auto cmd = *it; + // 待機 + { + std::unique_lock lock(mtx); - // 見付かれば実行 - (this->*(cmd.func))(); + // notify 前にクリア + prompt_released = false; - // Stay なら引き続きコマンド処理 - if (cmd.action == CommandAction::Stay) { - continue; - } - // それ以外ならメインループ終了 - return cmd.action; - } + // MPU が一時停止したことをデバッガスレッドへ通知する + request |= REQUEST_PROMPT; + cv_request.notify_one(); - // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系 - if (args[0][0] == 'r') { - // ShowRegister() は処理したら true を返す。 - // "r" 系コマンドはすべて Stay。 - if (md->ShowRegister(cons, args)) { - continue; - } + // デバッガスレッドがプロンプト解放するまで待機 + cv_prompt.wait(lock, [&] { return prompt_released; }); } - // 知らないコマンドも Stay 相当。 - cons->Print("%s: unknown command\n", args[0].c_str()); - continue; + // 実時間を再開 + gSync->StartRealTime(); + } else { + // t (トレース表示) が有効な場合は終了条件にマッチしなかったここで + // レジスタを表示。終了条件にマッチした時は EnterPrompt() で表示する。 + if (t_enable) { + pc = md->GetPC(); + cmd_minus(); + // 次回との差分のため今のレジスタセットをバックアップ + md->BackupRegs(); + } } - __unreachable(); } -// ブレークポイントとかを調べる。 -// CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。 +// MPU をトレース状態にすべきなら true を返す。 bool -debugger_check() +Debugger::IsTrace() const { - return gDebugger->Check(); + // 有効なブレークポイントがあれば MPU をトレースにする + for (const auto& bp : bpoint) { + if (bp.type != BreakpointType::Unused) { + return true; + } + } + + // この辺のどれかがあれば MPU をトレースにする + return bc_enable || s_enable || so_enable || n_enable || is_pause; } -// デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。 +// デバッガ実行中なら命令開始前に VM スレッドから呼ばれる。 // プロンプトに降りるなら true を返す。 bool Debugger::Check() @@ -358,34 +598,24 @@ Debugger::Check() // キャンセルする。この場合もブレークポイント側が true なので true を // 返せばよい。 - // t (トレース表示) が enable なら、終了条件にマッチしなくてもここで - // レジスタを表示。終了条件にマッチする場合に表示するとプロンプトで - // もう一回表示されて二重になってしまうので注意。 + // トレース表示に関しては Exec 側でやってある。 - if (s_enable) { // ステップ実行が.. + if (s_enable) { // ステップ実行が成立するか s_count--; - if (s_count == 0) { // 成立 - s_enable = false; - return true; - } else if (is_break) { // 非成立だがブレークが成立 + if (s_count == 0 || is_break) { s_enable = false; - return true; - } else if (t_enable) { // どちらも非成立で trace on - cmd_minus(); + t_enable = false; + is_break = true; } - } else if (so_enable) { // ステップアウトが.. - if (md->IsStepOut()) { // 成立 - so_enable = false; - return true; - } else if (is_break) { // 非成立だがブレークが成立 + } else if (so_enable) { // ステップアウトが成立するか + if (md->IsStepOut() || is_break) { so_enable = false; - return true; - } else if (t_enable) { // どちらも非成立で trace on - cmd_minus(); + t_enable = false; + is_break = true; } - } else if (n_enable) { // 次命令まで実行が.. + } else if (n_enable) { // 指定命令数実行が完了するか if (n_breakaddr != 0xffffffff) { // ステップインをスキップ中 if (n_breakaddr == md->GetPC()) { @@ -395,32 +625,33 @@ Debugger::Check() // 1命令実行 n_count--; } - if (n_count == 0) { // 成立 + if (n_count == 0 || is_break) { n_enable = false; - return true; - } else if (is_break) { // 非成立だがブレークが成立 - n_enable = false; - return true; - } - if (t_enable) { // どちらも非成立で trace on - cmd_minus(); + t_enable = false; + is_break = true; + } else { + // スキップ中でなければ、ステップインが起きるか都度調べる。 + // すでにスキップ中なら到達するまでは何もしない。 + if (n_breakaddr == 0xffffffff) { + SetNBreakpoint(); + } } + } - // スキップ中でなければ、ステップインが起きるか都度調べる。 - // すでにスキップ中なら到達するまでは何もしない。 - if (n_breakaddr == 0xffffffff) { - SetNBreakpoint(); - } + // デバッガから MPU の一時停止が要求されているか + if (is_pause) { + is_pause = false; + is_break = true; } - // ステップ実行系がなければブレークポイントの成否だけ return is_break; } // ブレークポイントがどれかでも成立するかを調べる。 // 1つ以上成立してブレークするなら true を返す。 // 1つも成立しておらずブレークしないなら false を返す。 -// 成立すれば内容をここで表示する。 +// このルーチンから cons への出力は使わないこと。(プロンプトにいない時でも +// 呼ばれるので) bool Debugger::CheckAllBreakpoints() { @@ -494,8 +725,8 @@ Debugger::CheckAllBreakpoints() case BreakpointType::Exception: { desc = string_format("excp $%02x", bv_vector); - const char *name = md->GetExceptionName(bv_vector); - if (name != NULL && name[0] != '\0') { + const char *name = gVectorTable->GetExceptionName(bv_vector); + if (name) { desc += string_format(" \"%s\"", name); } break; @@ -509,7 +740,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()); } // 例外通知は通過ごとに常に下ろしておく @@ -528,7 +765,7 @@ Debugger::CheckBreakpointInst(breakpoint // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。 saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md, laddr); + DebuggerMemoryStream mem(md.get(), laddr); // 必要なバイト数に達するまで読み足す bi_inst = 0; @@ -639,12 +876,12 @@ Debugger::cmd_h() } // そうでなければこれを表示して終了 std::string disp = HelpConvert(dict.second); - cons->Print("%s", disp.c_str()); + fprintf(cons, "%s", disp.c_str()); return; } } } while (try_again); - cons->Print("invalid command name: %s\n", args[1].c_str()); + fprintf(cons, "invalid command name: %s\n", args[1].c_str()); } // hb : ブレークポイント系コマンドの一覧を表示 @@ -667,9 +904,9 @@ Debugger::cmd_hr() void Debugger::ShowHelpList(const HelpMessages& msgs) { - cons->Print("Type \"help \" for indivisual details.\n"); + fprintf(cons, "Type \"help \" for indivisual details.\n"); for (const auto& pair : msgs) { - cons->Print(" %-16s %s\n", pair.first.c_str(), pair.second.c_str()); + fprintf(cons, " %-16s %s\n", pair.first.c_str(), pair.second.c_str()); } } @@ -842,7 +1079,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 +1135,7 @@ Debugger::HelpDetails = { //----- { "L", R"**( - Command: L + Command: L [=][,=[]]... Set loglevel. XXX To be written... )**" }, @@ -1045,21 +1282,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; } @@ -1072,8 +1295,8 @@ void Debugger::cmd_bm() { // XXX m68k では未サポート - if (md->arch == Arch::M680x0) { - cons->Print("bm not supported yet on m68k\n"); + if (md->arch == DebuggerMD::Arch::M680x0) { + fprintf(cons, "bm not supported yet on m68k\n"); return; } cmd_b_set(BreakpointType::Memory); @@ -1086,7 +1309,7 @@ Debugger::cmd_b_set(BreakpointType type) breakpoint_t bp; if (args.size() < 2) { - cons->Print("usage: %s []\n", args[0].c_str()); + fprintf(cons, "usage: %s []\n", args[0].c_str()); return; } @@ -1104,12 +1327,36 @@ Debugger::cmd_b_set(BreakpointType type) bp.type = type; int bi = AddBreakpoint(bp); if (bi == -1) { - cons->Print("no free breakpoints\n"); + fprintf(cons, "no free breakpoints\n"); } else { - cons->Print("breakpoint #%d added\n", bi); + fprintf(cons, "breakpoint #%d added\n", bi); } } +// ブレークポイント個別削除 +// (引数の先頭が '#' なことを判定したところで呼ばれる) +// b # +void +Debugger::cmd_b_delete() +{ + int n = atoi(&args[1][1]); + if (n < 0 || n >= bpoint.size()) { + fprintf(cons, "invalid breakpoint number: #%d\n", n); + return; + } + + auto& bp = bpoint[n]; + if (bp.type == BreakpointType::Unused) { + fprintf(cons, "invalid breakpoint number: #%d\n", n); + return; + } + + fprintf(cons, "breakpoint #%d removed\n", n); + bp.type = BreakpointType::Unused; + // 今登録されている命令ブレークの必要命令長を再計算 + RecalcInstMask(); +} + // 命令ブレークポイントの設定 // bi [:] [] void @@ -1122,7 +1369,7 @@ Debugger::cmd_bi() int masklen; if (args.size() < 2) { - cons->Print("usage: bi [:] []\n"); + fprintf(cons, "usage: bi [:] []\n"); return; } @@ -1143,11 +1390,11 @@ Debugger::cmd_bi() // 命令部チェック if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) { - cons->Print("%s: invalid instruction value\n", args[1].c_str()); + fprintf(cons, "%s: invalid instruction value\n", args[1].c_str()); return; } if (instlen % (md->inst_bytes * 2) != 0) { - cons->Print("%s: invalid instruction length\n", args[1].c_str()); + fprintf(cons, "%s: invalid instruction length\n", args[1].c_str()); return; } @@ -1155,11 +1402,11 @@ Debugger::cmd_bi() bp.mask = 0xffffffff; if (masklen != -1) { if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) { - cons->Print("%s: invalid mask value\n", args[1].c_str()); + fprintf(cons, "%s: invalid mask value\n", args[1].c_str()); return; } if (masklen != instlen) { - cons->Print("%s: inst:mask must be the same length\n", + fprintf(cons, "%s: inst:mask must be the same length\n", args[1].c_str()); return; } @@ -1180,9 +1427,9 @@ Debugger::cmd_bi() bp.type = BreakpointType::Instruction; int bi = AddBreakpoint(bp); if (bi == -1) { - cons->Print("no free breakpoints\n"); + fprintf(cons, "no free breakpoints\n"); } else { - cons->Print("breakpoint #%d added\n", bi); + fprintf(cons, "breakpoint #%d added\n", bi); } // 今登録されている命令ブレークの必要命令長を再計算 @@ -1197,7 +1444,7 @@ Debugger::cmd_bv() breakpoint_t bp; if (args.size() < 2) { - cons->Print("usage: be [-] []\n"); + fprintf(cons, "usage: be [-] []\n"); return; } @@ -1205,7 +1452,7 @@ Debugger::cmd_bv() if (pos == std::string::npos) { // ベクタ番号が1つなら vec1, vec2 を同値にしておく。 if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) { - cons->Print("%s: invalid vector number\n", args[1].c_str()); + fprintf(cons, "%s: invalid vector number\n", args[1].c_str()); return; } bp.vec2 = bp.vec1; @@ -1215,22 +1462,22 @@ Debugger::cmd_bv() std::string str2 = args[1].substr(pos + 1); if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) { - cons->Print("%s: invalid first vector number\n", args[1].c_str()); + fprintf(cons, "%s: invalid first vector number\n", args[1].c_str()); return; } if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) { - cons->Print("%s: invalid last vector number\n", args[1].c_str()); + fprintf(cons, "%s: invalid last vector number\n", args[1].c_str()); return; } } // 範囲チェック - if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) { - cons->Print("$%x: invalid vector number\n", bp.vec1); + if (bp.vec1 < 0 || bp.vec1 >= gVectorTable->Size()) { + fprintf(cons, "$%x: invalid vector number\n", bp.vec1); return; } - if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) { - cons->Print("$%x: invalid last vector number\n", bp.vec2); + if (bp.vec2 < 0 || bp.vec2 >= gVectorTable->Size()) { + fprintf(cons, "$%x: invalid last vector number\n", bp.vec2); return; } @@ -1252,9 +1499,9 @@ Debugger::cmd_bv() bp.type = BreakpointType::Exception; int bi = AddBreakpoint(bp); if (bi == -1) { - cons->Print("no free breakpoints\n"); + fprintf(cons, "no free breakpoints\n"); } else { - cons->Print("breakpoint #%d added\n", bi); + fprintf(cons, "breakpoint #%d added\n", bi); } // すでに来ている例外をクリア。 @@ -1275,12 +1522,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 @@ -1365,7 +1612,7 @@ Debugger::cmd_bx() for (auto& bp : bpoint) { bp.type = BreakpointType::Unused; } - cons->Print(" All breakpoints disabled\n"); + fprintf(cons, " All breakpoints disabled\n"); // 今登録されている命令ブレークの必要命令長を再計算 RecalcInstMask(); @@ -1438,17 +1685,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 +1722,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 [] @@ -1562,7 +1809,7 @@ Debugger::GetAddr(saddr_t *sa, bool defa mod_prog = 1; } else if ('0' <= ch && ch <= '7') { int num = ch - '0'; - if (md->arch == Arch::M680x0) { + if (md->arch == DebuggerMD::Arch::M680x0) { // m68k では FC 指定は Super/User 指定を含んでおり、 // 値指定は他の修飾子とは同時に指定できない。 if (mod_super >= 0 || mod_prog >= 0 || @@ -1571,12 +1818,12 @@ Debugger::GetAddr(saddr_t *sa, bool defa } // 有効な値は 1,2,5,6 のみ if (num == 0 || num == 3 || num == 4 || num == 7) { - cons->Print("%c: invalid address space\n", ch); + fprintf(cons, "%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) { + } else if (md->arch == DebuggerMD::Arch::M88xx0) { // m88k では CMMU 指定と、Super/User 指定は独立。 if (mod_prog >= 0 || (mod_num >= 0 && mod_num != num)) { @@ -1586,20 +1833,20 @@ Debugger::GetAddr(saddr_t *sa, bool defa mod_prog = (mod_num & 1); // CMMU7 が Inst } } else { - cons->Print("%c: unknown address space modifier\n", ch); + fprintf(cons, "%c: unknown address space modifier\n", ch); return false; } continue; error: - cons->Print("%s: address space modifier '%c' conflicts\n", + fprintf(cons, "%s: address space modifier '%c' conflicts\n", mod.c_str(), ch); return false; } // 数値指定を機種ごとに読み替える if (mod_num >= 0) { - if (md->arch == Arch::M680x0) { + if (md->arch == DebuggerMD::Arch::M680x0) { // m68k なら値指定で全部確定する // "1:" -> User/Data // "2:" -> User/Program @@ -1607,13 +1854,13 @@ Debugger::GetAddr(saddr_t *sa, bool defa // "6:" -> Super/Program mod_super = (mod_num & 4) ? true : false; mod_prog = (mod_num & 3) - 1; - } else if (md->arch == Arch::M88xx0) { + } else if (md->arch == DebuggerMD::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", + fprintf(cons, "%d: CMMU#%d not installed\n", mod_num, mod_num); return false; } @@ -1638,8 +1885,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; } @@ -1684,7 +1931,7 @@ Debugger::cmd_d_common(MemoryMode access cnt = strtol(args[2].c_str(), NULL, 10); } - DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode); + DebuggerMemoryStream mem(md.get(), saddr, access_mode, lookup_mode); for (int i = 0; i < cnt; i++) { std::string addrbuf; @@ -1695,7 +1942,7 @@ Debugger::cmd_d_common(MemoryMode access // アドレス if (mem.FormatAddr(addrbuf) == false) { // アドレス変換でバスエラーならここで終了 - cons->Print("%s\n", addrbuf.c_str()); + fprintf(cons, "%s\n", addrbuf.c_str()); break; } @@ -1704,7 +1951,7 @@ Debugger::cmd_d_common(MemoryMode access // オフライン用に整形 mnembuf = md->FormatDisasm(mnemonic, local_ir); - cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); + fprintf(cons, "%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); // 変換できなければここで終了 if (local_ir.size() == 0) @@ -1725,10 +1972,10 @@ Debugger::cmd_disp() // 表示 bool first = true; for (const auto& r : disp_regs) { - cons->Print("%s%s", (first ? "" : ","), r.c_str()); + fprintf(cons, "%s%s", (first ? "" : ","), r.c_str()); first = false; } - cons->Print("\n"); + fprintf(cons, "\n"); return; } @@ -1745,42 +1992,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) { + fprintf(cons, "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) { + fprintf(cons, " %s\n", name.c_str()); + } + return; + } + } + + // ログレベルを設定 + std::string errmsg; + if (MainApp::SetLogopt(items, &errmsg) == false) { + fprintf(cons, "%s\n", errmsg.c_str()); + return; } } @@ -1810,9 +2060,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 +2071,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); } - DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode); + // 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.get(), 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 (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 +2150,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 +2158,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; + } } // プロンプトに来た最初に表示するいつものやつを再表示する @@ -1885,10 +2196,10 @@ Debugger::cmd_minus() // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし saddr_t laddr(pc, md->IsSuper()); - DebuggerMemoryStream mem(md, laddr); + DebuggerMemoryStream mem(md.get(), laddr); // アドレス if (mem.FormatAddr(addrbuf) == false) { - cons->Print("%s\n", addrbuf.c_str()); + fprintf(cons, "%s\n", addrbuf.c_str()); return; } // 逆アセンブル @@ -1896,7 +2207,7 @@ Debugger::cmd_minus() // ライブ表示用に整形 mnembuf = md->FormatDisasmLive(mnemonic, ir); - cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); + fprintf(cons, "%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); } // サブルーチンとループを飛ばしながら count 命令ステップ実行 @@ -1925,7 +2236,7 @@ Debugger::cmd_n_common(bool trace) int count; count = strtol(args[1].c_str(), NULL, 10); if (count < 1) { - cons->Print(" invalid step count: %d\n", count); + fprintf(cons, " invalid step count: %d\n", count); return; } @@ -1944,7 +2255,7 @@ void Debugger::SetNBreakpoint() { saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md, laddr); + DebuggerMemoryStream mem(md.get(), laddr); // 命令がサブルーチンやトラップなどステップインできる命令か。 if (md->IsOpStepIn(mem)) { @@ -1972,14 +2283,14 @@ Debugger::SetNBreakpoint() void Debugger::cmd_q() { - cons->Print("quit debugger console.\n"); + fprintf(cons, "quit debugger console.\n"); } // VM リセット void Debugger::cmd_reset() { - gScheduler->RequestResetHard(); + gPower->MakeResetHard(); } // モニター表示 @@ -1987,37 +2298,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); + fprintf(cons, "usage: show \n"); + fprintf(cons, "%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()) { + // 一致しない + fprintf(cons, "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; } + fprintf(cons, "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 +2383,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; @@ -2088,7 +2440,7 @@ Debugger::ShowTextScreen(TextScreen& mon // XXX 日本語が使われてれば UTF-8 にしないといけないはず - cons->Print("%s\n", sbuf); + fprintf(cons, "%s\n", sbuf); } } @@ -2118,7 +2470,7 @@ Debugger::cmd_s_common(bool trace) int count; count = strtol(args[1].c_str(), NULL, 10); if (count < 1) { - cons->Print(" invalid step count: %d\n", count); + fprintf(cons, " invalid step count: %d\n", count); return; } @@ -2199,11 +2551,11 @@ Debugger::ParseAddr(const char *arg, uin r = md->GetRegAddr(arg + 1); if ((int64)r < 0) { #if 0 // not yet - cons->Print("valid register name are: %s%s\n", + fprintf(cons, "valid register name are: %s%s\n", "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc", ",%msp,%vbr,%srp,%crp"); #endif - cons->Print("invalid register name\n"); + fprintf(cons, "invalid register name\n"); return false; } addr = (uint32)r; @@ -2212,11 +2564,11 @@ Debugger::ParseAddr(const char *arg, uin errno = 0; addr = strtoul(arg, &end, 16); if (arg[0] == '\0' || end[0] != '\0') { - cons->Print("invalid address: '%s'\n", arg); + fprintf(cons, "invalid address: '%s'\n", arg); return false; } if (errno == ERANGE) { - cons->Print("out of range: %s\n", arg); + fprintf(cons, "out of range: %s\n", arg); return false; } } @@ -2234,20 +2586,3 @@ Debugger::ParseAddr(const char *arg, uin DebuggerMD::~DebuggerMD() { } - - -// -// ブレークポイントモニター -// - -// コンストラクタ -BreakpointMonitor::BreakpointMonitor() -{ - monitor_size = nnSize(55, 9); -} - -void -BreakpointMonitor::MonitorUpdate(TextScreen& monitor) -{ - gDebugger->MonitorBreakpoint(monitor); -}