--- nono/debugger/debugger.cpp 2026/04/29 17:05:13 1.1.1.12 +++ nono/debugger/debugger.cpp 2026/04/29 17:05:32 1.1.1.17 @@ -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.Regist(ID_MONITOR_BREAKPOINT); + // ブレークポイントモニタ + bpoint_monitor = gMonitorManager->Regist(ID_MONITOR_BREAKPOINT, this); + bpoint_monitor->func = ToMonitorCallback(&Debugger::MonitorUpdateBpoint); + bpoint_monitor->SetSize(60, 9); - // メモリダンプモニター + // メモリダンプモニタ 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); + uint 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++) { + uint objid = OBJ_XP_MEMDUMP(i); + int monid = ID_MONITOR_XPMEMDUMP(i); + xpmemdump_monitor[i].reset(new MemdumpMonitor(objid, monid)); + } + } } // デストラクタ @@ -98,11 +92,26 @@ Debugger::~Debugger() Close(); if ((bool)hostcom) { - hostcom->SetCallbackDevice(NULL); + hostcom->SetRxCallback(NULL); + hostcom->SetAcceptCallback(NULL); } TerminateThread(); - gDebugger = NULL; +} + +bool +Debugger::Create() +{ + // ホストドライバを作成 + hostcom.reset(new HostCOMDevice(this, "Debugger")); + if ((bool)hostcom == false) { + return false; + } + + hostcom->SetRxCallback(ToDeviceCallback(&Debugger::RxCallback)); + hostcom->SetAcceptCallback(ToDeviceCallback(&Debugger::AcceptCallback)); + + return true; } // ログレベル設定 @@ -117,33 +126,40 @@ Debugger::SetLogLevel(int loglevel_) } } +// 初期化 bool -Debugger::Create() +Debugger::Init() { - // ホストドライバを作成 - try { - hostcom.reset(new HostCOMDevice("Debugger")); - } catch (...) { + if (inherited::Init() == false) { return false; } - hostcom->SetRxCallback(ToDeviceCallback(&Debugger::RxCallback)); - hostcom->SetAcceptCallback(ToDeviceCallback(&Debugger::AcceptCallback)); - hostcom->SetCallbackDevice(this); - - return true; -} + syncer = GetSyncer(); -// 初期化 -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 (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 +168,69 @@ 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 (ParseVerbHex(addrstr.c_str(), &bp.addr)) { + bp.type = BreakpointType::Address; + } else if (addrstr[0] == 'v' + && ParseVector( + ParseCPU(cpustr), &(addrstr.c_str()[1]), (uint32 *)&bp.vec1)) { + bp.type = BreakpointType::Exception; + bp.vec2 = bp.vec1; + } else { 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); + } + + // --bi-exg なら命令ブレークポイントを設定 + // (CPU のチェックはしていない) + if (gMainApp.debug_breakinst_exg) { + breakpoint_t bp; + bp.type = BreakpointType::Instruction; + bp.inst = 0xcf4f0000; + bp.mask = 0xffff0000; + AddBreakpoint(bp, ""); + // 今登録されている命令ブレークの必要命令長を再計算 + RecalcInstMask(); } // 入出力を FILE で扱う @@ -189,6 +247,8 @@ Debugger::Init() void Debugger::ThreadRun() { + SetThreadAffinityHint(AffinityClass::Light); + // disp_regs の初期値設定。 // 再接続でも継続してていいような気がするのでループ外で初期化。 disp_regs.clear(); @@ -229,7 +289,7 @@ Debugger::ThreadRun() } } - LeavePrompt(); + LeavePrompt(false); Close(); } @@ -278,14 +338,16 @@ Debugger::Terminate() static int readfunc(void *cookie, char *buf, int bufsize) { - return ((Debugger *)cookie)->ReadFunc(buf, bufsize); + auto debugger = reinterpret_cast(cookie); + return debugger->ReadFunc(buf, bufsize); } // funopen の write コールバック static int writefunc(void *cookie, const char *buf, int len) { - return ((Debugger *)cookie)->WriteFunc(buf, len); + auto debugger = reinterpret_cast(cookie); + return debugger->WriteFunc(buf, len); } // funopen の read コールバックの本体 @@ -380,24 +442,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 +474,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 < ' ' || c == 0x07f) { + // 他のコントロールコードはとりあえず無視 + } else { + // 通常文字なら、エコーバックして追加 + fputc(c, cons); + fflush(cons); + + cmdbuf.push_back(c); } } } @@ -423,29 +499,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 +537,7 @@ Debugger::LeavePrompt() void Debugger::PrintPrompt() { - fprintf(cons, "> "); + fprintf(cons, "%s> ", curmd->GetName().c_str()); fflush(cons); } @@ -503,7 +579,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 +594,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 +619,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 +645,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 +761,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 +842,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 +883,6 @@ Debugger::CheckAllBreakpoints() i, desc.c_str()); } - // 例外通知は通過ごとに常に下ろしておく - bv_vector = -1; - // ブレークポイントが1つでも成立したかどうかを返す return is_break; } @@ -774,20 +895,22 @@ Debugger::CheckBreakpointInst(breakpoint // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。 // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。 - saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + busaddr laddr = busaddr(bp.md->GetPC()) + | BusAddr::Fetch + | busaddr::SU(bp.md->IsSuper()); + DebuggerMemoryStream mem(bp.md, laddr); // 必要なバイト数に達するまで読み足す bi_inst = 0; while (bi_inst_bytes < bi_need_bytes) { - uint64 data = mem.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 +923,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; } // コマンド一覧。 @@ -826,7 +952,6 @@ Debugger::NotifyException(int vector) { "c", &Debugger::cmd_c, }, { "ct", &Debugger::cmd_ct, }, { "d", &Debugger::cmd_d, }, - { "dt", &Debugger::cmd_dt, }, { "D", &Debugger::cmd_D, }, { "disp", &Debugger::cmd_disp, }, { "exhist", &Debugger::cmd_exhist, }, @@ -836,8 +961,8 @@ Debugger::NotifyException(int vector) { "help", &Debugger::cmd_h, }, { "L", &Debugger::cmd_L, }, { "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 +974,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 +992,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 +1050,7 @@ Debugger::cmd_hb() Debugger::CmdAct Debugger::cmd_hr() { - return ShowHelpList(md->GetHelpListReg()); + return ShowHelpList(curmd->GetHelpListReg()); } // ヘルプ一覧を表示。 @@ -1009,11 +1150,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 +1164,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 +1175,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 +1193,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 +1234,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. )**" }, @@ -1096,12 +1242,11 @@ Debugger::HelpDetails = { { "d", R"**( Command: D
] [] 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 + The second form ("d") interprets
as a logical 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 @@ -1112,11 +1257,7 @@ Debugger::HelpDetails = { 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. )**" }, - { "dt", "=d" }, { "D", "=d" }, //----- @@ -1166,12 +1307,11 @@ Debugger::HelpDetails = { { "m", R"**( Command: M
] [] 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 + The second form ("m") interprets
as a logical 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 @@ -1182,20 +1322,23 @@ Debugger::HelpDetails = { 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. )**" }, - { "mt", "=m" }, { "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 +1390,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 +1447,9 @@ Debugger::ParseCmdbuf() // // ブレークポイント -// b ... 一覧表示 -// b #n ... #n を削除 -// b [] ... 設定 +// b ... 一覧表示 +// b #n ... #n を削除 +// b [,] [] ... 設定 Debugger::CmdAct Debugger::cmd_b() { @@ -1312,12 +1469,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 +1486,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 +1518,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 +1553,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 +1570,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 +1609,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 +1632,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 +1645,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 +1722,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); + return CmdAct::Stay; } + 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 +1802,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 +1824,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 +1895,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 +1961,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 +1980,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); } // ブランチ履歴、例外履歴表示の共通部分。 @@ -1752,8 +2019,8 @@ Debugger::cmd_hist_common(BranchHistory& int lines = std::min(used, maxlines) + 1; // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。 - auto& histmon = hist.monitor; - auto size = histmon.GetSize(); + auto *histmon = hist.monitor; + auto size = histmon->GetSize(); TextScreen screen; screen.Init(size.width, lines); @@ -1776,13 +2043,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 +2070,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; } @@ -1817,7 +2086,7 @@ Debugger::cmd_ct() // エラーならメッセージを表示して false を返す。 // sa は in/out パラメータ。 bool -Debugger::GetAddr(saddr_t *sa, bool default_data) +Debugger::GetAddr(busaddr *sa, bool default_data) { if (args.size() < 2) { // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。 @@ -1863,7 +2132,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 +2146,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 +2154,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 +2171,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 +2179,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,93 +2198,145 @@ 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; } - sa->SetSuper(mod_super); - sa->SetData(!mod_prog); + sa->ChangeSuper(mod_super); + sa->ChangeData(!mod_prog); // アドレス if (ParseAddr(str.c_str() + addrpos, &addr) == false) { return false; } // 命令境界に丸める - sa->SetAddr(addr & ~(md->inst_bytes - 1)); + sa->ChangeAddr(addr & ~(curmd->inst_bytes - 1)); return true; } -// 逆アセンブル: d [[SU:] []] (論理、テーブルサーチなし) +// ターゲット CPU を "mpu" (M680x0/M88xx0) に変更。 Debugger::CmdAct -Debugger::cmd_d() +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() { - return cmd_d_common(MemoryMode::Logical, MMULookupMode::False); + if (curmd != md_xp.get()) { + ChangeMD(md_xp.get()); + ResetStickyAddr(); + cmd_minus(); + } + return CmdAct::Stay; } -// 逆アセンブル: dt [[SU:]] []] (論理、テーブルサーチあり) +// この命令の1つ次のアドレスまでスキップする。 +// dbra とかのループを飛ばす用。 Debugger::CmdAct -Debugger::cmd_dt() +Debugger::cmd_z() { - return cmd_d_common(MemoryMode::Logical, MMULookupMode::True); + // 次のアドレスを求める。 + if (curmd->inst_bytes_fixed) { + // 固定長命令 + step_addr = curmd->GetPC() + curmd->inst_bytes_fixed; + } else { + // 可変長なら逆アセンブルしてみるしか。 + + busaddr laddr = busaddr(curmd->GetPC()) + | BusAddr::Fetch + | busaddr::SU(curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr); + // XXX 失敗したらどうすべ + curmd->Disassemble(mem); + step_addr = mem.laddr.Addr(); + } + step_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 = busaddr(pc) | BusAddr::Read | busaddr::SU(curmd->IsSuper()); + d_last_addr = busaddr(pc) | BusAddr::Fetch | busaddr::SU(curmd->IsSuper()); +} + +// 逆アセンブル: d [[SU:] []] (論理) +Debugger::CmdAct +Debugger::cmd_d() +{ + return cmd_d_common(BusAddr::Logical); } // 逆アセンブル: D [ []] (物理) Debugger::CmdAct Debugger::cmd_D() { - return cmd_d_common(MemoryMode::Physical, MMULookupMode::None); + return cmd_d_common(BusAddr::Physical); } // 逆アセンブル共通 Debugger::CmdAct -Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode) +Debugger::cmd_d_common(busaddr access_mode) { - saddr_t saddr; - int cnt; + busaddr saddr; + int row; - cnt = 10; + row = 10; - // 開始アドレス + // 開始アドレス。S/U は GetAddr() がセットする。 saddr = d_last_addr; if (GetAddr(&saddr, false) == false) { return CmdAct::Stay; } + + // 論理アドレス/物理アドレスはコマンドによって決まる。 + saddr |= access_mode; + // 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 (saddr.IsPhysical()) { + mask = (1ULL << curmd->GetPASize()) - 1; + } else { + mask = (1ULL << curmd->GetLASize()) - 1; } + saddr &= (uint32)mask; - // アドレスと Super/User 状態を次回継続用に保存 - d_last_addr = mem.laddr; + Memdump disasm(curmd, curmd->arch); + disasm.SetAddr(saddr); + disasm.Print(cons, row); + + // アドレスを次回継続用に保存 + d_last_addr = disasm.GetAddr(); return CmdAct::Stay; } @@ -2094,148 +2417,63 @@ Debugger::cmd_L() return CmdAct::Stay; } -// メモリダンプ: m [ []] (論理、テーブルサーチなし) +// メモリダンプ: m [ []] (論理) Debugger::CmdAct Debugger::cmd_m() { - return cmd_m_common(MemoryMode::Logical, MMULookupMode::False); -} - -// メモリダンプ: mt [ []] (論理、テーブルサーチあり) -Debugger::CmdAct -Debugger::cmd_mt() -{ - return cmd_m_common(MemoryMode::Logical, MMULookupMode::True); + return cmd_m_common(BusAddr::Logical); } // メモリダンプ: M [ []] (物理) Debugger::CmdAct Debugger::cmd_M() { - return cmd_m_common(MemoryMode::Physical, MMULookupMode::None); + return cmd_m_common(BusAddr::Physical); } // メモリダンプ共通 Debugger::CmdAct -Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode) +Debugger::cmd_m_common(busaddr access_mode) { - saddr_t saddr; + busaddr saddr; int row; row = 8; - // 開始アドレス + // 開始アドレス。S/U は GetAddr() がセットする。 saddr = m_last_addr; if (GetAddr(&saddr, true) == false) { return CmdAct::Stay; } + + // 論理アドレス/物理アドレスはコマンドによって決まる。 + saddr |= access_mode; + // 2つ目の引数があれば行数 if (args.size() > 2) { row = strtol(args[2].c_str(), NULL, 10); } - // userdata を作成 - uint64 userdata = saddr.GetAddr(); - if (saddr.IsSuper() == false) { - userdata |= (1ULL << 32); - } - if (saddr.IsData()) { - userdata |= (1ULL << 33); - } - if (access_mode == MemoryMode::Logical) { - userdata |= (1ULL << 34); - } - if (lookup_mode == MMULookupMode::True) { - userdata |= (1ULL << 35); + // アドレス幅を制限 (XP用) (ここ?) + uint64 mask; + if (saddr.IsPhysical()) { + mask = (1ULL << curmd->GetPASize()) - 1; + } else { + mask = (1ULL << curmd->GetLASize()) - 1; } - // CLI では毎回次を表示していってほしい - userdata |= (1ULL << 36); + saddr &= (uint32)mask; - TextScreen screen; - screen.Init(m_monitor.GetSize().width, row); - screen.userdata = userdata; - - MONITOR_UPDATE(m_monitor, screen); - ShowTextScreen(screen); + Memdump memdump(curmd); + memdump.SetAddr(saddr); + memdump.SetFormat(Memdump::Word); + memdump.Print(cons, row); - // アドレスを次回継続用に保存 (Super/Data は維持) - m_last_addr = (uint32)screen.userdata; + // アドレスを次回継続用に保存 + 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 +2485,26 @@ 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(); - - // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし - saddr_t laddr(pc, md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + std::string textbuf; + + busaddr laddr = busaddr(pc) + | BusAddr::Fetch + | busaddr::SU(curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr); // アドレス 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 +2528,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 +2538,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 +2555,31 @@ Debugger::cmd_n_common(bool trace) void Debugger::SetNBreakpoint() { - saddr_t laddr(md->GetPC(), md->IsSuper()); - DebuggerMemoryStream mem(md.get(), laddr); + busaddr laddr = busaddr(curmd->GetPC()) + | BusAddr::Fetch + | busaddr::SU(curmd->IsSuper()); + DebuggerMemoryStream mem(curmd, laddr); // 命令がサブルーチンやトラップなどステップインできる命令か。 - 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 = mem.laddr.Addr(); } 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 = mem.laddr.Addr(); } } else { // そうでなければ1命令進める。 - // 0xffffffff は常にアドレスとして不正なのでこれをフラグに使う。 - n_breakaddr = 0xffffffff; + step_addr = (int64)-1; } } @@ -2356,21 +2595,21 @@ 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; + std::vector list; - // 登録されているモニターだけを列挙 + // 登録されているモニタだけを列挙 // (登録されていてもサブウィンドウの ID を持っているものは除外) for (const auto mon : gMonitorManager->GetList()) { - int id = mon->GetId(); + uint id = mon->GetId(); if (id <= ID_MONITOR_END) { list.push_back(id); } @@ -2388,7 +2627,7 @@ Debugger::cmd_show() std::string name = args[1]; for (auto mon : gMonitorManager->GetList()) { - int id = mon->GetId(); + uint id = mon->GetId(); const auto& aliases = gMonitorManager->GetAliases(id); bool matched = false; @@ -2412,7 +2651,7 @@ Debugger::cmd_show() fprintf(cons, "unknown monitor name: \"%s\"\n", name.c_str()); } else if (candidates.size() == 1) { // 確定した - Monitor& mon = *(candidates[0]); + Monitor *mon = candidates[0]; ShowMonitor(mon); } else { // 候補が複数あった @@ -2428,14 +2667,14 @@ Debugger::cmd_show() return CmdAct::Stay; } -// モニターを更新して表示。 +// モニタを更新して表示。 void -Debugger::ShowMonitor(Monitor& monitor) +Debugger::ShowMonitor(Monitor *monitor) { // モニタを更新 TextScreen ts; - auto size = monitor.GetSize(); + auto size = monitor->GetSize(); ts.Init(size.width, size.height); MONITOR_UPDATE(monitor, ts); @@ -2476,7 +2715,10 @@ Debugger::ShowTextScreen(TextScreen& mon *d++ = '['; *d++ = 'm'; break; - case TA::On: + case TA::Reverse: + case TA::ReverseRed: + case TA::ReverseGreen: + case TA::ReverseOrange: *d++ = 0x1b; *d++ = '['; *d++ = '7'; @@ -2533,7 +2775,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 +2785,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 +2813,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,13 +2865,8 @@ 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", - "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc", - ",%msp,%vbr,%srp,%crp"); -#endif fprintf(cons, "invalid register name\n"); return false; } @@ -2693,6 +2932,15 @@ Debugger::ParseTime(const char *arg, uin // MD // +// コンストラクタ +DebuggerMD::DebuggerMD(Debugger *parent_, CPUArch arch_) +{ + parent = parent_; + arch = arch_; + + bv_vector = -1; +} + // デストラクタ DebuggerMD::~DebuggerMD() {