--- nono/debugger/debugger.cpp 2026/04/29 17:05:13 1.1.1.12 +++ nono/debugger/debugger.cpp 2026/04/29 17:05:16 1.1.1.13 @@ -20,7 +20,7 @@ // |<---------------------------| // | is_prompt = true; | デバッガスレッドからプロンプトを出したい -// | Message(MPU_TRACE); | 場合は MPU (VM) をトレースモードにする。 +// | Message(MPU_TRACE_ALL); | 場合は MPU (VM) をトレースモードにする。 // | | その際 is_prompt を立てておくことで止まる。 // | | // |--------------------------->| @@ -33,19 +33,21 @@ // | | ことで実行再開。 #include "debugger.h" -#include "debugger_private.h" +#include "debugger_hd64180.h" #include "debugger_m680x0.h" #include "debugger_m88xx0.h" #include "hostcom.h" #include "mainapp.h" +#include "memdump.h" #include "mystring.h" #include "power.h" #include "scheduler.h" -#include "sync.h" +#include "syncer.h" #include "uimessage.h" #include "vectortable.h" #include #include +#include #if defined(HAVE_BSD_STDIO_H) #include #endif @@ -53,41 +55,33 @@ static int readfunc(void *, char *, int); static int writefunc(void *, const char *, int); -// グローバル参照用 -Debugger *gDebugger; - -// メモリダンプモニタを外部から取得 -Monitor& -debugger_memdump_monitor(int n) -{ - assert(n < MAX_MEMDUMP_MONITOR); - return gDebugger->memdump_monitor[n]; -} - // コンストラクタ Debugger::Debugger() - : inherited("Debugger") + : inherited(OBJ_DEBUGGER) { // ベクタテーブル pVectorTable.reset(new VectorTable(gMainApp.GetVMType())); - gVectorTable = pVectorTable.get(); - // ブレークポイントモニター + // ブレークポイントモニタ bpoint_monitor.func = ToMonitorCallback(&Debugger::MonitorUpdateBpoint); - bpoint_monitor.SetSize(55, 9); + bpoint_monitor.SetSize(60, 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); + int objid = OBJ_MPU_MEMDUMP(i); + int monid = ID_MONITOR_MEMDUMP(i); + memdump_monitor[i].reset(new MemdumpMonitor(objid, monid)); + } + + if (gMainApp.Has(VMCap::LUNA)) { + // XP 空間のメモリダンプモニタ + for (int i = 0, end = xpmemdump_monitor.size(); i < end; i++) { + int objid = OBJ_XP_MEMDUMP(i); + int monid = ID_MONITOR_XPMEMDUMP(i); + xpmemdump_monitor[i].reset(new MemdumpMonitor(objid, monid)); + } + } } // デストラクタ @@ -102,19 +96,6 @@ Debugger::~Debugger() } TerminateThread(); - gDebugger = NULL; -} - -// ログレベル設定 -void -Debugger::SetLogLevel(int loglevel_) -{ - inherited::SetLogLevel(loglevel_); - - // ホストドライバを従属させる - if ((bool)hostcom) { - hostcom->SetLogLevel(loglevel_); - } } bool @@ -134,16 +115,52 @@ Debugger::Create() return true; } +// ログレベル設定 +void +Debugger::SetLogLevel(int loglevel_) +{ + inherited::SetLogLevel(loglevel_); + + // ホストドライバを従属させる + if ((bool)hostcom) { + hostcom->SetLogLevel(loglevel_); + } +} + // 初期化 bool Debugger::Init() { - if (gMPU680x0) { - md.reset(new DebuggerMD_m680x0(this, gMPU680x0->GetCPU())); - } else if (gMPU88xx0) { - md.reset(new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU())); + if (inherited::Init() == false) { + return false; + } + + syncer = GetSyncer(); + + if (gMainApp.Has(VMCap::M88K)) { + md_mpu.reset(new DebuggerMD_m88xx0(this)); } else { - assertmsg(false, "unknown mpu"); + md_mpu.reset(new DebuggerMD_m680x0(this)); + } + if (gMainApp.Has(VMCap::LUNA)) { + md_xp.reset(new DebuggerMD_hd64180(this)); + } + // とりあえずメインプロセッサに固定 + curmd = md_mpu.get(); + + // md_* が用意できたので MemdumpMonitor にセットする。 + // これらは Monitor なので Init() より前に用意してなければならないが、 + // 一方で md は諸々が落ち着いた後のここでないと用意できない。うーん。 + // ここはバス別のモニタなので curmd ではなく md_{mpu,xp} を参照する。 + for (int i = 0, end = memdump_monitor.size(); i < end; i++) { + auto *mem = memdump_monitor[i].get(); + mem->InitMD(md_mpu.get()); + } + if (gMainApp.Has(VMCap::LUNA)) { + for (int i = 0, end = xpmemdump_monitor.size(); i < end; i++) { + auto *mem = xpmemdump_monitor[i].get(); + mem->InitMD(md_xp.get()); + } } // -d なら CPU 起動時点で停止してプロンプトを待つ @@ -152,27 +169,51 @@ Debugger::Init() // この時点ではまだ MPU にメッセージを送ることはできない } - // -b ならブレークポイント設定 + // -b [,][,] ならブレークポイント設定 for (auto& str : gMainApp.debug_breakaddr) { breakpoint_t bp; - - // , で分離できたら を取り出す - int pos = str.find(','); - if (pos != std::string::npos) { - std::string skipstr = str.substr(pos + 1); - bp.skip = atoi(skipstr.c_str()); - - // XXX これ出来るんだっけ? - str[pos] = '\0'; + std::string cpustr; + std::string addrstr; + std::string skipstr; + + // "," で分離 + auto arr = string_split(str, ','); + if (arr.size() < 2) { + // 1個ならアドレス (0 ってことはないはずだが) + addrstr = arr[0]; + } else if (arr.size() == 2) { + // 2個なら cpu か skip のどちらかが省略。 + // 1つ目が16進数っぽいかどうかで判定する。 + char *end; + strtoul(arr[0].c_str(), &end, 16); + if (end == &arr[0][0]) { + cpustr = arr[0]; + addrstr = arr[1]; + } else { + addrstr = arr[0]; + skipstr = arr[1]; + } + } else if (arr.size() == 3) { + cpustr = arr[0]; + addrstr = arr[1]; + skipstr = arr[2]; + } else { + warnx("\"%s\": Invalid breakpoint", str.c_str()); + return false; } - // そしてどちらにしても を取り出す - if (!ParseAddr(str.c_str(), &bp.addr)) { + + // を取り出す + if (!ParseAddr(addrstr.c_str(), &bp.addr)) { warnx("\"%s\": Invalid breakpoint address", str.c_str()); return false; } + // を取り出す + if (skipstr.empty() == false) { + bp.skip = atoi(skipstr.c_str()); + } // 登録 bp.type = BreakpointType::Address; - AddBreakpoint(bp); + AddBreakpoint(bp, cpustr); } // 入出力を FILE で扱う @@ -229,7 +270,7 @@ Debugger::ThreadRun() } } - LeavePrompt(); + LeavePrompt(false); Close(); } @@ -380,24 +421,30 @@ Debugger::Input(int c) if (c == '\n' || c == '\x03') { // MPU に一時停止を要求 is_pause = true; - gScheduler->SendMessage(MessageID::MPU_TRACE, true); + scheduler->SendMessage(MessageID::MPU_TRACE_ALL, true); } } else { // プロンプト中なら行入力 + if (c == '\b') { + if (cmdbuf.empty() == false) { + // 簡易バックスペースを出力。 + // XXX 実際どうするんだこれ + fputc('\b', cons); + fputc(' ', cons); + fputc('\b', cons); + fflush(cons); - // 先に自力エコーバック?? - fputc(c, cons); - fflush(cons); - - if (c != '\n') { - cmdbuf.push_back(c); - } else { + cmdbuf.pop_back(); + } + } else if (c == '\n') { // Enter ならここでコマンド実行 + fputc(c, cons); + fflush(cons); auto act = Command(); // 次回との差分のため、今のレジスタセットをバックアップ - md->BackupRegs(); + curmd->BackupRegs(); switch (act) { case CmdAct::Stay: @@ -406,13 +453,21 @@ Debugger::Input(int c) break; case CmdAct::Leave: // プロンプトを抜ける - LeavePrompt(); + LeavePrompt(IsTrace()); break; case CmdAct::Quit: // アプリケーション自体を終了 - LeavePrompt(); + LeavePrompt(false); UIMessage::Post(UIMessage::APPEXIT); } + } else if (c < ' ') { + // 他のコントロールコードはとりあえず無視 + } else { + // 通常文字なら、エコーバックして追加 + fputc(c, cons); + fflush(cons); + + cmdbuf.push_back(c); } } } @@ -423,29 +478,29 @@ 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(); } + // d/m をいきなり引数なしで実行した時のため、現在地にしておく。 + ResetStickyAddr(); + // プロンプトのたびに表示するやつ cmd_minus(); PrintPrompt(); } -// コマンドモード(プロンプト)から出る +// コマンドモード(プロンプト)から出る。 +// trace はプロンプトから抜ける際のトレース状態の指示。 +// スレッド終了時は false を指定すること。 void -Debugger::LeavePrompt() +Debugger::LeavePrompt(bool trace) { // MPU のトレース状態を変更 - gScheduler->SendMessage(MessageID::MPU_TRACE, IsTrace()); + scheduler->SendMessage(MessageID::MPU_TRACE_ALL, trace); is_prompt = false; @@ -461,7 +516,7 @@ Debugger::LeavePrompt() void Debugger::PrintPrompt() { - fprintf(cons, "> "); + fprintf(cons, "%s> ", curmd->GetName().c_str()); fflush(cons); } @@ -503,7 +558,7 @@ Debugger::Command() if (args[0][0] == 'r') { // ShowRegister() は処理したら true を返す。 // "r" 系コマンドはすべて Stay。 - if (md->ShowRegister(cons, args)) { + if (curmd->ShowRegister(cons, args)) { return CmdAct::Stay; } } @@ -518,11 +573,14 @@ Debugger::Command() // ブレークポイントとかを調べる。1命令ごとに呼び出される。 // (VM スレッドから呼ばれる) void -Debugger::Exec() +Debugger::Exec(DebuggerMD *md) { - if (Check()) { + if (Check(md)) { + // この CPU でブレークしたので、ターゲット CPU をこっちに変更。 + ChangeMD(md); + // 実時間を停止 - gSync->StopRealTime(); + syncer->StopRealTime(); // 待機 { @@ -540,15 +598,16 @@ Debugger::Exec() } // 実時間を再開 - gSync->StartRealTime(); + syncer->StartRealTime(); } else { // t (トレース表示) が有効な場合は終了条件にマッチしなかったここで // レジスタを表示。終了条件にマッチした時は EnterPrompt() で表示する。 - if (t_enable) { - pc = md->GetPC(); + if (t_enable == md) { + assert(md == curmd); + pc = curmd->GetPC(); cmd_minus(); // 次回との差分のため今のレジスタセットをバックアップ - md->BackupRegs(); + curmd->BackupRegs(); } } } @@ -565,86 +624,110 @@ Debugger::IsTrace() const } // この辺のどれかがあれば MPU をトレースにする - return bc_enable || ct_enable || s_enable || so_enable || n_enable || - is_pause; + return is_pause || (step_type != StepType::None); } // デバッガ実行中なら命令開始前に VM スレッドから呼ばれる。 // プロンプトに降りるなら true を返す。 bool -Debugger::Check() +Debugger::Check(DebuggerMD *md) { bool is_break = false; // ブレークポイントは他のチェックとは併用になるので先に調べる。 - if (CheckAllBreakpoints()) { + if (CheckAllBreakpoints(md)) { is_break = true; - } - // アドレス指定付き continue もブレークポイントと似た動作なのでこっち。 - // XXX ブレークポイントとして実装するかどうか - if (bc_enable) { - if (bc_addr == md->GetPC()) { - bc_enable = false; - is_break = true; - } - } - if (ct_enable) { - if (gScheduler->GetVirtTime() >= ct_end_time) { - ct_enable = false; - is_break = true; - - // ブレークポイントメッセージに便乗して表示。 - bpointmsg += string_format("%s has passed.\n", - TimeToStr(ct_timespan).c_str()); - } - } - // XXX 残りは排他動作のはず + // この次に調べる各種終了条件が来ないうちに先にブレークポイントに + // 到達した場合でも、ブレークポイントによりプロンプトに降りる + // わけなので、実行中のステップ実行をキャンセルする。 + // この場合もブレークポイント側が true なので true を返せばよい。 + step_type = StepType::None; + step_md = NULL; + } + + // ステップ実行系は、ターゲット CPU 側でだけ判定を行い、 + // いずれの場合も終了条件にマッチしたらブレークポイントの成否に関わらず + // true を返せばいい。 + // なお、トレース表示に関してはここではなく Exec() 側でやってある。 + + if (step_md == md) { + bool hit = false; + switch (step_type) { + case StepType::None: + break; - // いずれの場合も、ステップ実行が終了条件にマッチしたら、ブレークポイント - // の成否に関わらず true を返せばいい。 - // 終了条件が来ないうちに先にブレークポイントに到達した場合でも、ブレーク - // ポイントによりプロンプトに降りるわけなので、実行中のステップ実行を - // キャンセルする。この場合もブレークポイント側が true なので true を - // 返せばよい。 + case StepType::Count: // 命令数指定 + step_count--; + putlog(1, "Check s: --step_count=%d", step_count); + if (step_count == 0) { + hit = true; + } + break; - // トレース表示に関しては Exec 側でやってある。 + case StepType::CountSkipSub: // 命令数指定 (サブルーチンをスキップ) + if ((int64)step_addr >= 0) { + // アドレスが指定されていれば、ステップインをスキップ中 + if (step_addr == step_md->GetPC()) { + step_count--; + step_addr = (uint64)-1; + } + } else { + step_count--; + } + if (loglevel >= 1) { + if ((int64)step_addr < 0) { + putlogn("Check n: --step_count=%d", step_count); + } else { + putlogn("Check n: step_addr=$%08x", (uint32)step_addr); + } + } + if (step_count == 0 || is_break/*?*/) { + hit = true; + } else { + // スキップ中でなければ、ステップインが起きるか都度調べる。 + // すでにスキップ中なら到達するまでは何もしない。 + if ((int64)step_addr < 0) { + SetNBreakpoint(); + } + } + break; - if (s_enable) { // ステップ実行が成立するか - s_count--; - if (s_count == 0 || is_break) { - s_enable = false; - t_enable = false; - is_break = true; - } + case StepType::StepOut: // ステップアウト + putlog(1, "Check so"); + if (step_md->IsStepOut()) { + hit = true; + } + break; - } else if (so_enable) { // ステップアウトが成立するか - if (md->IsStepOut() || is_break) { - so_enable = false; - t_enable = false; - is_break = true; - } + case StepType::Addr: // アドレス指定 + putlog(1, "Check c: step_addr=$%08x", (uint32)step_addr); + if (step_addr == step_md->GetPC()) { + hit = true; + } + break; - } else if (n_enable) { // 指定命令数実行が完了するか - if (n_breakaddr != 0xffffffff) { - // ステップインをスキップ中 - if (n_breakaddr == md->GetPC()) { - n_count--; + case StepType::Time: // 時間指定 + putlog(1, "Check ct"); + if (scheduler->GetVirtTime() >= step_time) { + hit = true; + + // 時間到達は他のと比べて分かりづらいので、 + // ブレークポイントメッセージに便乗して表示(?) + bpointmsg += string_format("%s has passed.\n", + TimeToStr(ct_timespan).c_str()); } - } else { - // 1命令実行 - n_count--; + break; + + default: + PANIC("StepType %d not supported\n", (int)step_type); } - if (n_count == 0 || is_break) { - n_enable = false; - t_enable = false; + + if (hit) { is_break = true; - } else { - // スキップ中でなければ、ステップインが起きるか都度調べる。 - // すでにスキップ中なら到達するまでは何もしない。 - if (n_breakaddr == 0xffffffff) { - SetNBreakpoint(); - } + step_type = StepType::None; + step_md = NULL; + t_enable = NULL; } } @@ -657,52 +740,65 @@ Debugger::Check() return is_break; } -// ブレークポイントがどれかでも成立するかを調べる。 -// 1つ以上成立してブレークするなら true を返す。 +// md で指定された CPU のブレークポイントがどれかでも成立するかを調べる。 +// 1つ以上成立してブレークするなら、true を返す。 // 1つも成立しておらずブレークしないなら false を返す。 // このルーチンから cons への出力は使わないこと。(プロンプトにいない時でも // 呼ばれるので) bool -Debugger::CheckAllBreakpoints() +Debugger::CheckAllBreakpoints(DebuggerMD *md) { bool is_break = false; + int vector; // 命令ごとにクリアする bi_inst = 0; bi_inst_bytes = 0; + // 例外はここでローカルにコピーしてから、クリアする。 + // 厳密には atomic exchange すべきのような。 + vector = md->bv_vector; + md->bv_vector = -1; + // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが) // 残りの他の条件も成立すればカウントを進める必要があるため、 // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。 for (int i = 0, end = bpoint.size(); i < end; i++) { auto& bp = bpoint[i]; + if (bp.md != md) { + continue; + } + switch (bp.type) { case BreakpointType::Address: - if (bp.addr != md->GetPC()) { - continue; + // 通常動作中でアドレスが一致すればマッチ + if (bp.addr == bp.md->GetPC() && + bp.md->GetCPUState() == CPUState::Normal) + { + break; } - break; + continue; case BreakpointType::Memory: - if (!md->CheckLEA(bp.addr)) { - continue; + if (bp.md->CheckLEA(bp.addr)) { + break; } - break; + continue; case BreakpointType::Exception: - if (bv_vector >= 0) { - if (bp.vec1 <= bv_vector && bv_vector <= bp.vec2) { + if (vector >= 0) { + if (bp.vec1 <= vector && vector <= bp.vec2) { break; } } continue; case BreakpointType::Instruction: - if (CheckBreakpointInst(bp) == false) { - continue; + if (CheckBreakpointInst(bp)) { + break; } - break; + continue; default: continue; @@ -725,24 +821,31 @@ Debugger::CheckAllBreakpoints() is_break = true; std::string desc; + desc = string_format("cpu=%s ", bp.md->GetName().c_str()); switch (bp.type) { case BreakpointType::Address: - desc = string_format("addr $%08x", bp.addr); + desc += string_format("addr=$%08x", bp.addr); break; case BreakpointType::Memory: - desc = string_format("mem $%08x", bp.addr); + desc += string_format("mem=$%08x", bp.addr); break; case BreakpointType::Exception: { - desc = string_format("excp $%02x", bv_vector); - const char *name = gVectorTable->GetExceptionName(bv_vector); + desc += string_format("excp=$%02x", vector); + const char *name; + if (bp.md->arch == CPUArch::HD64180) { + name = MPU64180Device::InterruptName[vector]; + } else { + auto vectortable = pVectorTable.get(); + name = vectortable->GetExceptionName(vector); + } if (name) { desc += string_format(" \"%s\"", name); } break; } case BreakpointType::Instruction: - desc = string_format("inst %0*x", + desc += string_format("inst=%0*x", bi_inst_bytes * 2, bi_inst >> ((4 - bi_inst_bytes) * 8)); break; @@ -759,9 +862,6 @@ Debugger::CheckAllBreakpoints() i, desc.c_str()); } - // 例外通知は通過ごとに常に下ろしておく - bv_vector = -1; - // ブレークポイントが1つでも成立したかどうかを返す return is_break; } @@ -774,20 +874,20 @@ Debugger::CheckBreakpointInst(breakpoint // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。 // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。 - saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + saddr_t laddr(bp.md->GetPC(), bp.md->IsSuper()); + DebuggerMemoryStream mem(bp.md, laddr, MMULookupMode::True); // 必要なバイト数に達するまで読み足す bi_inst = 0; while (bi_inst_bytes < bi_need_bytes) { - uint64 data = mem.FetchInst(); + uint64 data = mem.Read(bp.md->inst_bytes); if ((int64)data < 0) { return false; } - bi_inst <<= md->inst_bytes * 8; + bi_inst <<= bp.md->inst_bytes * 8; bi_inst |= data; - bi_inst_bytes += md->inst_bytes; + bi_inst_bytes += bp.md->inst_bytes; } // 左詰めにする @@ -800,18 +900,21 @@ Debugger::CheckBreakpointInst(breakpoint return false; } -// 例外通知 (MPU からの連絡用) +// 例外通知 (メイン CPU) void -debugger_notify_exception(int vector) +Debugger::NotifyExceptionMain(int vector) { - gDebugger->NotifyException(vector); + auto md = md_mpu.get(); + md->bv_vector = vector; } -// 例外通知 (本体) +// 例外通知 (XP)。 +// ベクタとして割り込み優先順位 (Intmap*) を使う。 void -Debugger::NotifyException(int vector) +Debugger::NotifyExceptionXP(int vector) { - bv_vector = vector; + auto md = md_xp.get(); + md->bv_vector = vector; } // コマンド一覧。 @@ -838,6 +941,7 @@ Debugger::NotifyException(int vector) { "m", &Debugger::cmd_m, }, { "mt", &Debugger::cmd_mt, }, { "M", &Debugger::cmd_M, }, + { "mpu", &Debugger::cmd_mpu, }, { "n", &Debugger::cmd_n, }, { "nt", &Debugger::cmd_nt, }, { "q", &Debugger::cmd_q, }, @@ -849,6 +953,8 @@ Debugger::NotifyException(int vector) { "sot", &Debugger::cmd_sot, }, { "show", &Debugger::cmd_show, }, { "t", &Debugger::cmd_t, }, + { "xp", &Debugger::cmd_xp, }, + { "z", &Debugger::cmd_z, }, { "-", &Debugger::cmd_minus, }, }; @@ -865,34 +971,48 @@ 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 : details) { - if (cmd == dict.first) { - const auto& desc = dict.second; - if (desc[0] == '=') { - // "=" 形式なら を探しなおす。 - // 別名で同じヘルプを指すシンボリックリンクみたいなもの。 - cmd = desc.substr(1); - try_again = true; - break; - } - // そうでなければこれを表示して終了 - std::string disp = HelpConvert(dict.second); - fprintf(cons, "%s", disp.c_str()); - return CmdAct::Stay; - } + const std::string cmd1 = args[1]; + + // curmd の MD 分も併せて検索するため、ローカルに map を作成する。 + // value のほうは実体コピーは不要なのでポインタ。 + std::map helpmap; + for (const auto& pair : HelpDetails) { + helpmap.insert(std::make_pair(pair.first, &pair.second)); + } + for (const auto& pair : curmd->GetHelpReg()) { + helpmap.insert(std::make_pair(pair.first, &pair.second)); + } + + // 検索 + const auto it1 = helpmap.find(cmd1); + if (it1 == helpmap.end()) { + fprintf(cons, "invalid command name: %s\n", cmd1.c_str()); + return CmdAct::Stay; + } + const auto& msg1 = *(it1->second); + + const std::string *msg; + if (msg1[0] != '=') { + // メッセージ本文が "=" から始まってなければこれを採用。 + msg = &msg1; + } else { + // メッセージ本文が "=" の形式なら、 を探し直す。 + // 別名で同じヘルプを指すシンボリックリンクみたいなもの。 + std::string cmd2 = msg1.substr(1); + + // 再検索 (こっちは見付からないはずはない) + const auto it2 = helpmap.find(cmd2); + if (it2 == helpmap.end()) { + fprintf(cons, "Warning: '%s' in '%s' not found\n", + msg1.c_str(), cmd1.c_str()); + return CmdAct::Stay; } - } while (try_again); - fprintf(cons, "invalid command name: %s\n", args[1].c_str()); + const auto& msg2 = *(it2->second); + msg = &msg2; + } + + std::string disp = HelpConvert(*msg); + fprintf(cons, "%s", disp.c_str()); return CmdAct::Stay; } @@ -909,7 +1029,7 @@ Debugger::cmd_hb() Debugger::CmdAct Debugger::cmd_hr() { - return ShowHelpList(md->GetHelpListReg()); + return ShowHelpList(curmd->GetHelpListReg()); } // ヘルプ一覧を表示。 @@ -1009,11 +1129,12 @@ Debugger::HelpDetails = { //----- { "b", R"**( Command: b - Command: b
[] + Command: b [,]
[] Command: b #n The first form (with no arguments) shows all breakpoints. - The second form sets a new breakpoint on
. + The second form sets a new breakpoint at
on . + If is omitted, the current cpu is used. XXX skipcount If is -1, it will never match. It's useful to count the number of times you have passed this address. @@ -1022,9 +1143,10 @@ Debugger::HelpDetails = { //----- { "bm", R"**( - Command: bm
[] + Command: bm [,]
[] - Sets a memory breakpoint on
. + Sets a memory breakpoint at
on . + If is omitted, the current cpu is used. XXX skipcount If is -1, it will never match. It's useful to count the number of times you have passed this address. @@ -1032,16 +1154,17 @@ Debugger::HelpDetails = { //----- { "bi", R"**( - Command: bi [:] [] + Command: bi [,][:] [] - Sets an instruction breakpoint. must be 16 bits or 32 bits on - m68k and must be 32 bits on m88k. can specify the mask. Its - length must be the same as . If the is ommited, all bits - of is used to compare. + Sets an instruction breakpoint on . must be 16 bits or 32 + bits on m68k and must be 32 bits on m88k. can specify the mask. + Its length must be the same as . If the is omitted, all + bits of is used to compare. For example, "bi 4e75" on m68k will stop at 'rts' instruction. "bi 70004e4f:fff0ffff" on m68k will stop at any of 'IOCS #0'..'IOCS #15'. + If is omitted, the current cpu is used. XXX skipcount If is -1, it will never match. It's useful to count the number of times you have passed this address. @@ -1049,13 +1172,15 @@ Debugger::HelpDetails = { //----- { "bv", R"**( - Command: bv [-] [] + Command: bv [,][-] [] - Sets an exception breakpoint. must be specified in hex. + Sets an exception breakpoint on . must be specified in hex. ranges from 0 to ff (255 in decimal) on m68k, and 0 to 1ff (511 in decimal) on m88k. If is specified, it matches the range from to (including themselves). For example, "bv 1-1ff" on m88k means all exceptions but reset. + + If is omitted, the current cpu is used. XXX skipcount If is -1, it will never match. It's useful to count the number of times you have passed this address. @@ -1088,7 +1213,7 @@ Debugger::HelpDetails = { Command: ct [] Continue until specified has elapsed. If the is - ommited, the last value is used again. The unit of is + omitted, the last value is used again. The unit of is seconds in default. You can use "msec", "usec", or "nsec" suffix. )**" }, @@ -1190,12 +1315,19 @@ Debugger::HelpDetails = { { "M", "=m" }, //----- + { "mpu", R"**( + Command: mpu + + Change the target CPU to "mpu"(M68030/M88100). + )**" }, + + //----- { "n", R"**( Command: n [] Command: nt [] Step one (or ) instructions. Unlike "s" command, "n" skips - subroutine. + subroutine (and repeat instruction like as LDIR in HD64*180). If "t" is suffixed, it shows a trace for each instruction (including while skipping). )**" }, @@ -1247,6 +1379,20 @@ Debugger::HelpDetails = { //----- { "t", "=s" }, + + //----- + { "xp", R"**( + Command: xp + + Change the target CPU to "xp"(HD647180). + )**" }, + + //----- + { "z", R"**( + Command: z + + Continue until the next address. + )**" }, }; // cmdbuf を args... に分解する。 @@ -1290,9 +1436,9 @@ Debugger::ParseCmdbuf() // // ブレークポイント -// b ... 一覧表示 -// b #n ... #n を削除 -// b [] ... 設定 +// b ... 一覧表示 +// b #n ... #n を削除 +// b [,] [] ... 設定 Debugger::CmdAct Debugger::cmd_b() { @@ -1312,12 +1458,12 @@ Debugger::cmd_b() } // メモリブレークポイントの設定 -// bm [] +// bm [,] [] Debugger::CmdAct Debugger::cmd_bm() { - // XXX m68k では未サポート - if (md->arch == DebuggerMD::Arch::M680x0) { + // XXX m88k のみ未サポート + if (curmd->arch != CPUArch::M88xx0) { fprintf(cons, "bm not supported yet on m68k\n"); return CmdAct::Stay; } @@ -1329,14 +1475,28 @@ Debugger::CmdAct Debugger::cmd_b_set(BreakpointType type) { breakpoint_t bp; + std::string cpustr; + std::string addrstr; if (args.size() < 2) { - fprintf(cons, "usage: %s []\n", args[0].c_str()); + fprintf(cons, "usage: %s [,] []\n", + args[0].c_str()); return CmdAct::Stay; } + // まず CPU を分離 + auto pos = args[1].find(','); + if (pos == std::string::npos) { + // CPU 指定なし + addrstr = args[1]; + } else { + // CPU 指定あり + cpustr = args[1].substr(0, pos); + addrstr = args[1].substr(pos + 1); + } + // アドレス - if (!ParseAddr(args[1].c_str(), &bp.addr)) { + if (!ParseAddr(addrstr.c_str(), &bp.addr)) { return CmdAct::Stay; } // あればスキップカウント @@ -1347,7 +1507,7 @@ Debugger::cmd_b_set(BreakpointType type) // 空いてるところにセット // (よく似たエントリがあっても干渉しない) bp.type = type; - int bi = AddBreakpoint(bp); + int bi = AddBreakpoint(bp, cpustr); if (bi == -1) { fprintf(cons, "no free breakpoints\n"); } else { @@ -1382,11 +1542,13 @@ Debugger::cmd_b_delete() } // 命令ブレークポイントの設定 -// bi [:] [] +// bi [,][:] [] Debugger::CmdAct Debugger::cmd_bi() { breakpoint_t bp; + std::string cpustr; + std::string argstr; std::string inststr; std::string maskstr; int instlen; @@ -1397,28 +1559,38 @@ Debugger::cmd_bi() return CmdAct::Stay; } - // 引数をまず分離 - auto pos = args[1].find(':'); + // CPU をまず分離 + auto pos = args[1].find(','); + if (pos == std::string::npos) { + // CPU 指定なし + argstr = args[1]; + } else { + // CPU 指定あり + cpustr = args[1].substr(0, pos); + argstr = args[1].substr(pos + 1); + } + + pos = argstr.find(':'); if (pos == std::string::npos) { // マスク指定なし - inststr = args[1]; + inststr = argstr; instlen = inststr.size(); masklen = -1; } else { // マスク指定あり - inststr = args[1].substr(0, pos); + inststr = argstr.substr(0, pos); instlen = inststr.size(); - maskstr = args[1].substr(pos + 1); + maskstr = argstr.substr(pos + 1); masklen = maskstr.size(); } // 命令部チェック if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) { - fprintf(cons, "%s: invalid instruction value\n", args[1].c_str()); + fprintf(cons, "%s: invalid instruction value\n", argstr.c_str()); return CmdAct::Stay; } - if (instlen % (md->inst_bytes * 2) != 0) { - fprintf(cons, "%s: invalid instruction length\n", args[1].c_str()); + if (instlen % (curmd->inst_bytes * 2) != 0) { + fprintf(cons, "%s: invalid instruction length\n", argstr.c_str()); return CmdAct::Stay; } @@ -1426,17 +1598,17 @@ Debugger::cmd_bi() bp.mask = 0xffffffff; if (masklen != -1) { if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) { - fprintf(cons, "%s: invalid mask value\n", args[1].c_str()); + fprintf(cons, "%s: invalid mask value\n", argstr.c_str()); return CmdAct::Stay; } if (masklen != instlen) { fprintf(cons, "%s: inst:mask must be the same length\n", - args[1].c_str()); + argstr.c_str()); return CmdAct::Stay; } } // 8バイト未満なら左詰め。 - if (md->inst_bytes < 4 && instlen < 8) { + if (curmd->inst_bytes < 4 && instlen < 8) { bp.inst <<= 32 - instlen * 4; bp.mask <<= 32 - instlen * 4; } @@ -1449,7 +1621,7 @@ Debugger::cmd_bi() // 空いてるところにセット bp.type = BreakpointType::Instruction; - int bi = AddBreakpoint(bp); + int bi = AddBreakpoint(bp, cpustr); if (bi == -1) { fprintf(cons, "no free breakpoints\n"); } else { @@ -1462,46 +1634,63 @@ Debugger::cmd_bi() } // 例外ブレークポイントの設定 -// bv [-] [] +// bv [,][-] [] Debugger::CmdAct Debugger::cmd_bv() { breakpoint_t bp; + std::string argstr; + std::string cpustr; + DebuggerMD *md; if (args.size() < 2) { - fprintf(cons, "usage: be [-] []\n"); + fprintf(cons, "usage: bv [,][-] []\n"); return CmdAct::Stay; } - auto pos = args[1].find('-'); + // CPU をまず分離 + auto pos = args[1].find(','); + if (pos == std::string::npos) { + // CPU 指定なし + argstr = args[1]; + } else { + // CPU 指定あり + cpustr = args[1].substr(0, pos); + argstr = args[1].substr(pos + 1); + } + // ベクタ名解釈のために必要 + md = ParseCPU(cpustr); + + pos = argstr.find('-'); if (pos == std::string::npos) { // ベクタ番号が1つなら vec1, vec2 を同値にしておく。 - if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) { - fprintf(cons, "%s: invalid vector number\n", args[1].c_str()); + if (ParseVector(md, argstr.c_str(), (uint32 *)&bp.vec1) == false) { + fprintf(cons, "%s: invalid vector number\n", argstr.c_str()); return CmdAct::Stay; } bp.vec2 = bp.vec1; } else { // ベクタ番号(範囲指定 [vec1, vec2]) - std::string str1 = args[1].substr(0, pos); - std::string str2 = args[1].substr(pos + 1); + std::string str1 = argstr.substr(0, pos); + std::string str2 = argstr.substr(pos + 1); - if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) { - fprintf(cons, "%s: invalid first vector number\n", args[1].c_str()); + if (ParseVector(md, str1.c_str(), (uint32 *)&bp.vec1) == false) { + fprintf(cons, "%s: invalid first vector number\n", argstr.c_str()); return CmdAct::Stay; } - if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) { - fprintf(cons, "%s: invalid last vector number\n", args[1].c_str()); + if (ParseVector(md, str2.c_str(), (uint32 *)&bp.vec2) == false) { + fprintf(cons, "%s: invalid last vector number\n", argstr.c_str()); return CmdAct::Stay; } } // 範囲チェック - if (bp.vec1 < 0 || bp.vec1 >= gVectorTable->Size()) { + auto vectortable = pVectorTable.get(); + if (bp.vec1 < 0 || bp.vec1 >= vectortable->Size()) { fprintf(cons, "$%x: invalid vector number\n", bp.vec1); return CmdAct::Stay; } - if (bp.vec2 < 0 || bp.vec2 >= gVectorTable->Size()) { + if (bp.vec2 < 0 || bp.vec2 >= vectortable->Size()) { fprintf(cons, "$%x: invalid last vector number\n", bp.vec2); return CmdAct::Stay; } @@ -1522,29 +1711,72 @@ Debugger::cmd_bv() // 空いてるところにセット bp.type = BreakpointType::Exception; - int bi = AddBreakpoint(bp); + int bi = AddBreakpoint(bp, cpustr); if (bi == -1) { fprintf(cons, "no free breakpoints\n"); } else { fprintf(cons, "breakpoint #%d added\n", bi); } - // すでに来ている例外をクリア。 + // この CPU 側に、すでに来ている例外をクリア。 // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは // 命令間(命令前)に呼ばれるやつ、なのでこうなる。 - // 1. 例外が起きると bv_vector がセットされる + // 1. 例外が起きると md->bv_vector がセットされる // 2. 例外ブレークポイントを設定していないとこれがクリアされない // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で // 1.のベクタが反応してしまう。 // 命令ごととかにクリアしてもいいかも知れないが、ここでブレークポイントを // 設定したのだから、それ以前の事象には反応すべきでない、という意味では // ここでもいいか? - bv_vector = -1; + + // bp.md は AddBreakpoint() で bpoint 登録時にセットされるので + // インデックスから bp をもう一度読み込む。 + bp = bpoint[bi]; + bp.md->bv_vector = -1; return CmdAct::Stay; } +// ベクタ名かベクタ番号をパースする。 +bool +Debugger::ParseVector(DebuggerMD *md, const char *arg, uint32 *valp) +{ + // 数値変換出来るか。 + if (ParseVerbHex(arg, valp)) { + return true; + } + + // 出来なければ、機種ごとにベクタ名と比較する。 + if (md->arch == CPUArch::HD64180) { + static std::vector table = { + "trap", + "nmi", + "int0", + "int1", + "int2", + "inpcap", + "outcmp", + "timeov", + "timer0", + "timer1", + "dma0", + "dma1", + "csio", + "asci0", + "asci1", + }; + for (int i = 0, end = table.size(); i < end; i++) { + if (strcasecmp(arg, table[i]) == 0) { + *valp = i; + return true; + } + } + } + + return false; +} + // ブレークポイント一覧表示 Debugger::CmdAct Debugger::cmd_b_list() @@ -1559,15 +1791,15 @@ Debugger::MonitorUpdateBpoint(Monitor *, { // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890 - // No Type Parameter Matched Skip - // #0 addr $01234567 123456789 123456789/123456789 - // #1 inst 00000000/00000000 - // #2 excp $00-$00 + // No CPU Type Parameter Matched Skip + // #0 xp addr $01234567 123456789 123456789/123456789 + // #1 main inst 00000000/00000000 + // #2 main excp $00-$00 monitor.Clear(); - monitor.Print(0, 0, "No Type Parameter"); - monitor.Print(26, 0, "Matched"); - monitor.Print(37, 0, "Skip"); + monitor.Print(0, 0, "No CPU Type Parameter"); + monitor.Print(31, 0, "Matched"); + monitor.Print(42, 0, "Skip"); for (int i = 0; i < bpoint.size(); i++) { const auto& bp = bpoint[i]; @@ -1581,54 +1813,58 @@ Debugger::MonitorUpdateBpoint(Monitor *, continue; case BreakpointType::Address: - monitor.Print(3, y, "addr $%08x", bp.addr); + monitor.Print(3, y, "%-4s addr $%08x", + bp.md->GetName().c_str(), bp.addr); break; case BreakpointType::Memory: - monitor.Print(3, y, "mem $%08x", bp.addr); + monitor.Print(3, y, "%-4s mem $%08x", + bp.md->GetName().c_str(), bp.addr); break; case BreakpointType::Exception: - monitor.Print(3, y, "excp $%02x", bp.vec1); + monitor.Print(3, y, "%-4s excp $%02x", + bp.md->GetName().c_str(), bp.vec1); if (bp.vec2 != bp.vec1) { - monitor.Print(11, y, "-$%02x", bp.vec2); + monitor.Print(16, y, "-$%02x", bp.vec2); } break; case BreakpointType::Instruction: - monitor.Print(3, y, "inst"); - if (md->inst_bytes == 4) { + monitor.Print(3, y, "%-4s inst", bp.md->GetName().c_str()); + if (bp.md->inst_bytes == 4) { if (bp.mask == 0xffffffff) { - monitor.Print(8, y, "%08x", bp.inst); + monitor.Print(13, y, "%08x", bp.inst); } else { - monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask); + monitor.Print(13, y, "%08x:%08x", bp.inst, bp.mask); } } else { if (bp.mask == 0xffffffff) { - monitor.Print(8, y, "%08x", bp.inst); + monitor.Print(13, y, "%08x", bp.inst); } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) { - monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask); + monitor.Print(13, y, "%08x:%08x", bp.inst, bp.mask); } else if (bp.mask == 0xffff0000) { - monitor.Print(8, y, "%04x", bp.inst >> 16); + monitor.Print(13, y, "%04x", bp.inst >> 16); } else { - monitor.Print(8, y, "%04x:%04x", + monitor.Print(13, y, "%04x:%04x", bp.inst >> 16, bp.mask >> 16); } } break; default: - monitor.Print(3, y, "type=%d", (int)bp.type); + monitor.Print(3, y, "%-4s type=%d", + (bp.md ? bp.md->GetName().c_str() : "?"), (int)bp.type); continue; } // マッチ回数 - monitor.Print(26, y, "%d", bp.matched); + monitor.Print(31, y, "%d", bp.matched); // スキップ if (bp.skip < 0) { - monitor.Print(37, y, "forever"); + monitor.Print(42, y, "forever"); } else if (bp.skip > 0) { - monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip); + monitor.Print(42, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip); } } } @@ -1648,17 +1884,35 @@ Debugger::cmd_bx() return CmdAct::Stay; } +// CPU 文字列から対応する MD を返す。 +// 一致しなければ NULL を返す。 +DebuggerMD * +Debugger::ParseCPU(const std::string& cpustr) const +{ + if (cpustr.empty()) { + return curmd; + } else if (cpustr == "xp") { + return md_xp.get(); + } else if (cpustr == "main") { + return md_mpu.get(); + } + return NULL; +} + // ブレークポイントを設定。 -// new_bp のうち matched, skipremain はこちらで初期化する。 +// new_bp のうち cpu, matched, skipremain はこちらで初期化する。 // それ以外を埋めてから呼ぶこと。 // 設定できればその番号、できなければ -1 を返す。 int -Debugger::AddBreakpoint(const breakpoint_t& new_bp) +Debugger::AddBreakpoint(const breakpoint_t& new_bp, const std::string& cpustr) { + DebuggerMD *md = ParseCPU(cpustr); + for (int i = 0; i < bpoint.size(); i++) { auto& bp = bpoint[i]; if (bp.type == BreakpointType::Unused) { bp = new_bp; + bp.md = md; bp.matched = 0; if (bp.skip > 0) { bp.skipremain = bp.skip; @@ -1696,7 +1950,7 @@ Debugger::RecalcInstMask() // 上位側から数えたマスクに必要なビット数 int mlen = 32 - ntz; // 命令語単位に切り上げる - mlen = roundup(mlen, md->inst_bytes * 8); + mlen = roundup(mlen, curmd->inst_bytes * 8); // バイト数に変換 mlen /= 8; @@ -1715,12 +1969,14 @@ Debugger::RecalcInstMask() Debugger::CmdAct Debugger::cmd_brhist() { - return cmd_hist_common(md->GetBrHist()); + auto brhist = gMainApp.GetObject(OBJ_MPU_BRHIST); + return cmd_hist_common(*brhist); } Debugger::CmdAct Debugger::cmd_exhist() { - return cmd_hist_common(md->GetExHist()); + auto exhist = gMainApp.GetObject(OBJ_MPU_EXHIST); + return cmd_hist_common(*exhist); } // ブランチ履歴、例外履歴表示の共通部分。 @@ -1776,13 +2032,14 @@ Debugger::cmd_c() { if (args.size() > 1) { // 引数があれば - if (!ParseAddr(args[1].c_str(), &bc_addr)) { + uint32 addr; + if (!ParseAddr(args[1].c_str(), &addr)) { return CmdAct::Stay; } - // 偶数番地に丸める - bc_addr &= 0xfffffffe; - - bc_enable = true; + step_type = StepType::Addr; + step_md = curmd; + // ネイティブ命令長に丸める + step_addr = addr & ~(curmd->inst_bytes - 1); } return CmdAct::Leave; } @@ -1802,8 +2059,9 @@ Debugger::cmd_ct() fprintf(cons, "time must be specified\n"); return CmdAct::Stay; } - ct_enable = true; - ct_end_time = gScheduler->GetVirtTime() + ct_timespan; + step_type = StepType::Time; + step_md = curmd; + step_time = scheduler->GetVirtTime() + ct_timespan; return CmdAct::Leave; } @@ -1863,7 +2121,7 @@ Debugger::GetAddr(saddr_t *sa, bool defa mod_prog = 1; } else if ('0' <= ch && ch <= '7') { int num = ch - '0'; - if (md->arch == DebuggerMD::Arch::M680x0) { + if (curmd->arch == CPUArch::M680x0) { // m68k では FC 指定は Super/User 指定を含んでおり、 // 値指定は他の修飾子とは同時に指定できない。 if (mod_super >= 0 || mod_prog >= 0 || @@ -1877,7 +2135,7 @@ Debugger::GetAddr(saddr_t *sa, bool defa mod_num = num; mod_super = mod_num >> 2; // FC2 mod_prog = (mod_num & 3) - 1; // FC1,FC0 - } else if (md->arch == DebuggerMD::Arch::M88xx0) { + } else if (curmd->arch == CPUArch::M88xx0) { // m88k では CMMU 指定と、Super/User 指定は独立。 if (mod_prog >= 0 || (mod_num >= 0 && mod_num != num)) { @@ -1885,6 +2143,8 @@ Debugger::GetAddr(saddr_t *sa, bool defa } mod_num = num; mod_prog = (mod_num & 1); // CMMU7 が Inst + } else { + fprintf(cons, "not yet\n"); } } else { fprintf(cons, "%c: unknown address space modifier\n", ch); @@ -1900,7 +2160,7 @@ Debugger::GetAddr(saddr_t *sa, bool defa // 数値指定を機種ごとに読み替える if (mod_num >= 0) { - if (md->arch == DebuggerMD::Arch::M680x0) { + if (curmd->arch == CPUArch::M680x0) { // m68k なら値指定で全部確定する // "1:" -> User/Data // "2:" -> User/Program @@ -1908,7 +2168,7 @@ 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 == DebuggerMD::Arch::M88xx0) { + } else if (curmd->arch == CPUArch::M88xx0) { // m88k では mod_num を CMMU ID とする(?)。 // XXX まだ CPU は1つしかないので雑に処理。 // "6" -> Data (CPU#0) @@ -1927,7 +2187,7 @@ Debugger::GetAddr(saddr_t *sa, bool defa // - 's'/'u' いずれもなければ現在値。 // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。 if (mod_super < 0) { - mod_super = md->IsSuper(); + mod_super = curmd->IsSuper(); } if (mod_prog < 0) { mod_prog = !default_data; @@ -1940,11 +2200,78 @@ Debugger::GetAddr(saddr_t *sa, bool defa return false; } // 命令境界に丸める - sa->SetAddr(addr & ~(md->inst_bytes - 1)); + sa->SetAddr(addr & ~(curmd->inst_bytes - 1)); return true; } +// ターゲット CPU を "mpu" (M680x0/M88xx0) に変更。 +Debugger::CmdAct +Debugger::cmd_mpu() +{ + if (curmd != md_mpu.get()) { + ChangeMD(md_mpu.get()); + ResetStickyAddr(); + cmd_minus(); + } + return CmdAct::Stay; +} + +// ターゲット CPU を "xp" (HD647180) に変更。 +Debugger::CmdAct +Debugger::cmd_xp() +{ + if (curmd != md_xp.get()) { + ChangeMD(md_xp.get()); + ResetStickyAddr(); + cmd_minus(); + } + return CmdAct::Stay; +} + +// この命令の1つ次のアドレスまでスキップする。 +// dbra とかのループを飛ばす用。 +Debugger::CmdAct +Debugger::cmd_z() +{ + // 次のアドレスを求める。 + if (curmd->inst_bytes_fixed) { + // 固定長命令 + step_addr = curmd->GetPC() + curmd->inst_bytes_fixed; + } else { + // 可変長なら逆アセンブルしてみるしか。 + + saddr_t laddr(curmd->GetPC(), curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr, MMULookupMode::True); + // XXX 失敗したらどうすべ + curmd->Disassemble(mem); + step_addr = (uint32)mem.laddr; + } + step_type = StepType::Addr; + step_md = curmd; + + return CmdAct::Leave; +} + +void +Debugger::ChangeMD(DebuggerMD *newmd) +{ + if (newmd != curmd) { + curmd = newmd; + fprintf(cons, "Target cpu switched to '%s'\n", + curmd->GetName().c_str()); + } +} + +// pc と d/m の初期値をリセット。 +void +Debugger::ResetStickyAddr() +{ + pc = curmd->GetPC(); + m_last_addr.Set(pc, curmd->IsSuper(), true); + d_last_addr.Set(pc, curmd->IsSuper(), false); +} + // 逆アセンブル: d [[SU:] []] (論理、テーブルサーチなし) Debugger::CmdAct Debugger::cmd_d() @@ -1971,9 +2298,9 @@ Debugger::CmdAct Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode) { saddr_t saddr; - int cnt; + int row; - cnt = 10; + row = 10; // 開始アドレス saddr = d_last_addr; @@ -1982,38 +2309,27 @@ Debugger::cmd_d_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.get(), saddr, access_mode, lookup_mode); - - for (int i = 0; i < cnt; i++) { - std::string addrbuf; - std::string mnemonic; - std::string mnembuf; - std::vector local_ir; - - // アドレス - if (mem.FormatAddr(addrbuf) == false) { - // アドレス変換でバスエラーならここで終了 - fprintf(cons, "%s\n", addrbuf.c_str()); - break; - } - - // 逆アセンブル - md->Disassemble(mem, mnemonic, local_ir); - // オフライン用に整形 - mnembuf = md->FormatDisasm(mnemonic, local_ir); - - fprintf(cons, "%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); - - // 変換できなければここで終了 - if (local_ir.size() == 0) - break; + // アドレス幅を制限 (XP用) (ここ?) + uint64 mask; + if (access_mode == MemoryMode::Logical) { + mask = (1ULL << curmd->GetLASize()) - 1; + } else { + mask = (1ULL << curmd->GetPASize()) - 1; } + saddr = (uint32)saddr & (uint32)mask; + + Memdump disasm(curmd, curmd->arch); + disasm.SetAddr(saddr); + disasm.SetAccessMode(access_mode); + disasm.SetLookupMode(lookup_mode); + disasm.Print(cons, row); // アドレスと Super/User 状態を次回継続用に保存 - d_last_addr = mem.laddr; + // (Super/Data はコマンドで都度指定なので気にしなくていい) + d_last_addr = disasm.GetAddr(); return CmdAct::Stay; } @@ -2134,108 +2450,29 @@ Debugger::cmd_m_common(MemoryMode access 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); - } + // アドレス幅を制限 (XP用) (ここ?) + uint64 mask; if (access_mode == MemoryMode::Logical) { - userdata |= (1ULL << 34); - } - if (lookup_mode == MMULookupMode::True) { - userdata |= (1ULL << 35); + mask = (1ULL << curmd->GetLASize()) - 1; + } else { + mask = (1ULL << curmd->GetPASize()) - 1; } - // CLI では毎回次を表示していってほしい - userdata |= (1ULL << 36); - - TextScreen screen; - screen.Init(m_monitor.GetSize().width, row); - screen.userdata = userdata; - - MONITOR_UPDATE(m_monitor, screen); - ShowTextScreen(screen); + saddr = (uint32)saddr & (uint32)mask; - // アドレスを次回継続用に保存 (Super/Data は維持) - m_last_addr = (uint32)screen.userdata; + Memdump memdump(curmd); + memdump.SetAddr(saddr); + memdump.SetAccessMode(access_mode); + memdump.SetLookupMode(lookup_mode); + memdump.SetFormat(Memdump::Word); + memdump.Print(cons, row); + + // アドレスを次回継続用に保存 + // (Super/Data はコマンドで都度指定なので気にしなくていい) + m_last_addr = memdump.GetAddr(); return CmdAct::Stay; } -// メモリダンプモニタ -// -// 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; - - // アドレス - bool ok = mem.FormatAddr(addrbuf); - screen.Print(0, y, "%s", addrbuf.c_str()); - if (ok == false) { - // アドレス変換でバスエラーならここで終了 - break; - } - - for (int j = 0; j < 8; j++) { - for (int k = 0; k < 2; k++) { - uint64 b = mem.Fetch8(); - - if ((int64)b < 0) { - dumpbuf += "--"; - charbuf += ' '; - } else { - uint32 c = (uint32)b; - dumpbuf += string_format("%02x", c); - charbuf += string_format("%c", - (0x20 <= c && c <= 0x7e) ? c : '.'); - } - } - dumpbuf += ' '; - } - - 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; - } -} - // プロンプトに来た最初に表示するいつものやつを再表示する Debugger::CmdAct Debugger::cmd_minus() @@ -2247,29 +2484,25 @@ Debugger::cmd_minus() for (const auto& reg : disp_regs) { std::vector tmpargs; tmpargs.push_back(reg); - md->ShowRegister(cons, tmpargs); + curmd->ShowRegister(cons, tmpargs); } - // 現在の PC の位置を逆アセンブル。ここで ir を更新。 + // 現在の PC の位置を逆アセンブル。 std::string addrbuf; - std::string mnemonic; - std::string mnembuf; - ir.clear(); + std::string textbuf; // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし - saddr_t laddr(pc, md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + saddr_t laddr(pc, curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr, MMULookupMode::False); // アドレス if (mem.FormatAddr(addrbuf) == false) { fprintf(cons, "%s\n", addrbuf.c_str()); return CmdAct::Stay; } - // 逆アセンブル - md->Disassemble(mem, mnemonic, ir); - // ライブ表示用に整形 - mnembuf = md->FormatDisasmLive(mnemonic, ir); + // オンライン用に逆アセンブル + textbuf = curmd->Disassemble(mem); - fprintf(cons, "%-18s %s\n", addrbuf.c_str(), mnembuf.c_str()); + fprintf(cons, "%-18s %s\n", addrbuf.c_str(), textbuf.c_str()); return CmdAct::Stay; } @@ -2293,7 +2526,7 @@ Debugger::cmd_nt() Debugger::CmdAct Debugger::cmd_n_common(bool trace) { - t_enable = trace; + t_enable = trace ? curmd : NULL; if (args.size() > 1) { int count; @@ -2303,11 +2536,12 @@ Debugger::cmd_n_common(bool trace) return CmdAct::Stay; } - n_count = count; + step_count = count; } else { - n_count = 1; + step_count = 1; } - n_enable = true; + step_type = StepType::CountSkipSub; + step_md = curmd; SetNBreakpoint(); return CmdAct::Leave; @@ -2319,28 +2553,29 @@ Debugger::cmd_n_common(bool trace) void Debugger::SetNBreakpoint() { - saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + saddr_t laddr(curmd->GetPC(), curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr, MMULookupMode::True); // 命令がサブルーチンやトラップなどステップインできる命令か。 - if (md->IsOpStepIn(mem)) { + if (curmd->IsOpStepIn(mem)) { // この次の命令にブレークをかける - if (md->inst_bytes_fixed != 0) { + if (curmd->inst_bytes_fixed != 0) { // 固定長命令 - n_breakaddr = (uint32)mem.laddr; + step_addr = (uint32)mem.laddr; } else { - // 可変長なら逆アセンブルしてみるしか - std::string mnemonic; - std::vector v; + // 可変長なら逆アセンブルしてみるしか。 + // 表示用文字列は作る必要ないけど、処理分けるのも手間だし + // ここは人間がコマンド打った時しかこないので気にしない。 + + // IsOpStepIn() が mem を進めるので戻す。 + mem.ResetAddr(curmd->GetPC()); // XXX 失敗したらどうすべ - mem.ResetAddr(md->GetPC()); - md->Disassemble(mem, mnemonic, v); - n_breakaddr = (uint32)mem.laddr; + curmd->Disassemble(mem); + step_addr = (uint32)mem.laddr; } } else { // そうでなければ1命令進める。 - // 0xffffffff は常にアドレスとして不正なのでこれをフラグに使う。 - n_breakaddr = 0xffffffff; + step_addr = (int64)-1; } } @@ -2356,18 +2591,18 @@ Debugger::cmd_q() Debugger::CmdAct Debugger::cmd_reset() { - gPower->MakeResetHard(); + GetPowerDevice()->MakeResetHard(); return CmdAct::Leave; } -// モニター表示 +// モニタ表示 Debugger::CmdAct Debugger::cmd_show() { if (args.size() != 2) { std::vector list; - // 登録されているモニターだけを列挙 + // 登録されているモニタだけを列挙 // (登録されていてもサブウィンドウの ID を持っているものは除外) for (const auto mon : gMonitorManager->GetList()) { int id = mon->GetId(); @@ -2428,7 +2663,7 @@ Debugger::cmd_show() return CmdAct::Stay; } -// モニターを更新して表示。 +// モニタを更新して表示。 void Debugger::ShowMonitor(Monitor& monitor) { @@ -2533,7 +2768,7 @@ Debugger::cmd_st() Debugger::CmdAct Debugger::cmd_s_common(bool trace) { - t_enable = trace; + t_enable = trace ? curmd : NULL; if (args.size() > 1) { int count; @@ -2543,11 +2778,12 @@ Debugger::cmd_s_common(bool trace) return CmdAct::Stay; } - s_count = count; + step_count = count; } else { - s_count = 1; + step_count = 1; } - s_enable = true; + step_type = StepType::Count; + step_md = curmd; return CmdAct::Leave; } @@ -2570,10 +2806,11 @@ Debugger::cmd_sot() Debugger::CmdAct Debugger::cmd_so_common(bool trace) { - t_enable = trace; + t_enable = trace ? curmd : NULL; - so_enable = true; - md->SetStepOut(); + step_type = StepType::StepOut; + step_md = curmd; + step_md->SetStepOut(); return CmdAct::Leave; } @@ -2621,7 +2858,7 @@ Debugger::ParseAddr(const char *arg, uin if (arg[0] == '%') { // '%' から始まればレジスタ uint64 r; - r = md->GetRegAddr(arg + 1); + r = curmd->GetRegAddr(arg + 1); if ((int64)r < 0) { #if 0 // not yet fprintf(cons, "valid register name are: %s%s\n", @@ -2693,6 +2930,15 @@ Debugger::ParseTime(const char *arg, uin // MD // +// コンストラクタ +DebuggerMD::DebuggerMD(Debugger *parent_, CPUArch arch_) +{ + parent = parent_; + arch = arch_; + + bv_vector = -1; +} + // デストラクタ DebuggerMD::~DebuggerMD() {