|
|
1.1 root 1: //
2: // nono
1.1.1.3 ! root 3: // Copyright (C) 2020 nono project
! 4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.2 root 7: #include "console.h"
8: #include "debugger_private.h"
9: #include "debugger_m680x0.h"
10: #include "debugger_m88xx0.h"
11: #include "mainapp.h"
1.1 root 12: #include "mystring.h"
1.1.1.3 ! root 13: #include "mythread.h"
! 14: #include <algorithm>
1.1 root 15: #include <csignal>
16:
1.1.1.3 ! root 17: static Debugger *gDebugger;
1.1 root 18: CVPrompt *gCVPrompt;
19:
20: static void *debugger_run(void *);
21:
22: // 初期化。
23: // この時点で VM が初期化されていること。
24: void
25: debugger_init()
26: {
27: gDebugger = new Debugger();
28: gDebugger->Init();
29:
30: gCVPrompt = new CVPrompt();
31:
32: // デバッガスレッド起動
33: pthread_t th;
34: pthread_create(&th, NULL, debugger_run, NULL);
35: }
36:
37: // デバッガスレッドのエントリポイント
38: static void *
39: debugger_run(void *dummy)
40: {
1.1.1.3 ! root 41: PTHREAD_SETNAME("Debugger");
1.1 root 42: pthread_detach(pthread_self());
43:
44: gDebugger->ThreadRun();
45: return NULL;
46: }
47:
48: // コンストラクタ
49: Debugger::Debugger()
50: {
1.1.1.2 root 51: if (gMPU680x0) {
52: md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU());
53: } else if (gMPU88xx0) {
54: md = new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU());
55: } else {
56: throw "unknown mpu";
57: }
1.1 root 58: }
59:
60: // コマンドライン引数によって動作を決めるところ。
61: // 名前がこれでいいのかはあるけど。
62: void
63: Debugger::Init()
64: {
65: // -d なら CPU 起動時点で停止してプロンプトを待つ
1.1.1.2 root 66: if (gMainApp.debug_on_start) {
67: md->ReqSet(CPU_REQ_PROMPT);
1.1 root 68: }
69:
70: // -b <addr> ならブレークポイント設定
1.1.1.3 ! root 71: for (auto& b : gMainApp.debug_breakaddr) {
1.1.1.2 root 72: md->ReqSet(CPU_REQ_TRACE);
1.1.1.3 ! root 73: AddBreakpoint(b, false);
1.1 root 74: }
75:
76: // どこでやるべか
77: signal(SIGPIPE, SIG_IGN);
78: }
79:
80: // デバッガスレッド
81: void
82: Debugger::ThreadRun()
83: {
84: // XXX どこかでコンソールの選択とパラメータの取得
1.1.1.2 root 85: if (gMainApp.debug_on_console) {
86: cons = new ConsoleStdio();
87: } else {
88: cons = new ConsoleTCP();
89: }
1.1 root 90: if (cons->Init() == false) {
1.1.1.3 ! root 91: delete cons;
1.1 root 92: return;
93: }
94:
95: for (;;) {
1.1.1.3 ! root 96: // 着信待ち
! 97: if (Accept() == false) {
! 98: break;
! 99: }
! 100:
1.1.1.2 root 101: cons->InitEditLine("> ");
1.1 root 102:
1.1.1.3 ! root 103: // デバッガプロンプトにいる間はトレースオン
! 104: md->ReqSet(CPU_REQ_TRACE);
! 105:
! 106: bool first = true;
! 107: for (;;) {
! 108: // 接続後の1回目だけ実行するもの。
! 109: if (first) {
! 110: first = false;
! 111:
! 112: // greeting はプロンプトが取れる前にもう表示したい。
! 113: // 何らかの事故でプロンプトが取れなくても、ここまでは接続
! 114: // できてることが分かるように。
! 115: cons->Print("This is debugger console\n");
! 116:
! 117: // 接続ごとに初期化する値
! 118: // XXX もうちょっときれいにしたい
! 119: d_last_addr = 0xffffffff;
! 120: m_last_addr = 0xffffffff;
! 121: n_enable = false;
! 122: s_enable = false;
! 123: t_enable = false;
! 124: }
! 125:
! 126: // プロンプトが取れるのを待つ
! 127: if (AcquirePrompt() == false) {
! 128: break;
! 129: }
! 130:
! 131: // プロンプトに来た時に表示するいつものやつ
! 132: pc = md->GetPC();
! 133: d_last_addr = pc;
! 134: cmd_minus();
! 135:
! 136: // コマンド処理のメインループ
! 137: auto action = MainLoop();
! 138:
! 139: // 次回との差分のため、今のレジスタセットをバックアップ
! 140: md->BackupRegs();
! 141:
! 142: // メインループから戻ったということは Leave か Quit なので
! 143: // どちらにしても、ここでプロンプトを手放す。
! 144: gCVPrompt->NotifyRelease();
! 145:
! 146: // quit ならここでループを1段抜ける
! 147: if (action == CommandAction::Quit) {
! 148: break;
! 149: }
! 150: }
! 151:
! 152: // デバッガを抜けるのでトレースオフ
1.1.1.2 root 153: md->ReqClr(CPU_REQ_TRACE);
1.1 root 154:
155: cons->Close();
1.1.1.3 ! root 156: if (gMainApp.debug_on_console) {
! 157: break;
! 158: }
1.1 root 159: }
160:
161: delete cons;
162: }
163:
1.1.1.3 ! root 164: // コンソールに接続されるのを待つ。
! 165: // うーん、なんだこれ。
! 166: //
! 167: // +--- debug_on_console (-D オプション)
! 168: // | +- debug_on_start (-d オプション)
! 169: // | | 動作
! 170: // 0 0 TCP、Accept で待機。
! 171: // 0 1 TCP、Accpet で待機。
! 172: // 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。
! 173: // 1 1 stdio、起動時にプロンプトで止まる。
1.1 root 174: bool
1.1.1.3 ! root 175: Debugger::Accept()
! 176: {
! 177: if (gMainApp.debug_on_console == false) {
! 178: // TCP なら accept が成功するのを待つ
! 179: cons->Accept();
! 180: } else {
! 181: if (gMainApp.debug_on_start == false) {
! 182: // stdio、起動時にプロンプトで止まらない。
! 183: // この場合キー入力かブレークポイントなどで止まった時点で
! 184: // プロンプトを出したいのでちょっと面倒なことに。
! 185: for (;;) {
! 186: bool is_prompt = gCVPrompt->WaitAcquire(200);
! 187: if (is_prompt) {
! 188: break;
! 189: }
! 190:
! 191: int r = cons->Poll(200);
! 192: if (r < 0) {
! 193: cmd_q();
! 194: return false;
! 195: }
! 196: if (r > 0) {
! 197: break;
! 198: }
! 199: }
! 200: } else {
! 201: // stdio、起動時にプロンプトで止まる。
! 202: // この場合は無条件に成功でよい。
! 203: }
! 204: }
! 205:
! 206: return true;
! 207: }
! 208:
! 209: // プロンプトが取れるのを待つ
! 210: // 失敗すれば false を返す
! 211: bool
! 212: Debugger::AcquirePrompt()
1.1 root 213: {
1.1.1.3 ! root 214: md->ReqSet(CPU_REQ_PROMPT);
! 215:
1.1.1.2 root 216: for (;;) {
217: bool is_prompt = gCVPrompt->WaitAcquire(200);
1.1.1.3 ! root 218: if (is_prompt)
! 219: break;
1.1.1.2 root 220:
221: // コンソールを定期観測して、入力があればブレーク要求
222: if (cons->Poll()) {
223: // この入力は drop してみる
224: if (cons->Gets(cmdbuf) == false) {
225: // EOF or Error
226: cmd_q();
227: return false;
228: }
229: md->ReqSet(CPU_REQ_PROMPT);
230: }
231: }
1.1.1.3 ! root 232: return true;
! 233: }
1.1 root 234:
1.1.1.3 ! root 235: // コマンド処理のメインループ。
! 236: // コマンドが Stay ならこの中で処理を継続。
! 237: // コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。
! 238: Debugger::CommandAction
! 239: Debugger::MainLoop()
! 240: {
! 241: for (;;) {
! 242: cons->Prompt();
! 243: if (cons->Gets(cmdbuf) == false) {
! 244: // EOF or Error
! 245: return CommandAction::Quit;
! 246: }
1.1 root 247:
1.1.1.3 ! root 248: string_rtrim(cmdbuf);
1.1 root 249:
1.1.1.3 ! root 250: if (!cmdbuf.empty()) {
! 251: // コマンドが入力されれば次回のために保存
! 252: last_cmdbuf = cmdbuf;
! 253: } else {
! 254: // 空行が入力されれば直前のコマンドをもう一度
! 255: // 前行がなければ何もせずもう一度プロンプトを表示するかね
! 256: if (last_cmdbuf.empty())
! 257: continue;
! 258: cmdbuf = last_cmdbuf;
! 259: }
! 260:
! 261: // 行を args に分解
! 262: ParseCmdbuf();
! 263:
! 264: // 前回の値が有効なのはコマンドが連続した時だけ
! 265:
! 266: // コマンドをテーブルから探す
! 267: auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
! 268: [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; });
! 269: if (it != cmdtable.end()) {
! 270: auto cmd = *it;
! 271:
! 272: // 見付かれば実行
! 273: (this->*(cmd.func))();
! 274:
! 275: // Stay なら引き続きコマンド処理
! 276: if (cmd.action == CommandAction::Stay) {
! 277: continue;
! 278: }
! 279: // それ以外ならメインループ終了
! 280: return cmd.action;
! 281: }
1.1 root 282:
1.1.1.3 ! root 283: // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
! 284: if (args[0][0] == 'r') {
! 285: // ShowRegister() は処理したら true を返す。
! 286: // "r" 系コマンドはすべて Stay。
! 287: if (md->ShowRegister(cons, args)) {
! 288: continue;
! 289: }
! 290: }
1.1.1.2 root 291:
1.1.1.3 ! root 292: // 知らないコマンドも Stay 相当。
! 293: cons->Print("%s: unknown command\n", args[0].c_str());
! 294: continue;
! 295: }
! 296: __unreachable();
1.1 root 297: }
298:
299: // ブレークポイントとかを調べる。
300: // CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。
301: bool
302: debugger_check()
303: {
304: return gDebugger->Check();
305: }
306:
307: // デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。
308: // プロンプトに降りるなら true を返す。
309: bool
310: Debugger::Check()
311: {
312: if (t_enable) {
313: t_count--;
314: if (t_count == 0) {
315: t_enable = false;
316: return true;
317: }
318: }
319:
320: if (s_enable) {
321: s_count--;
322: if (s_count == 0) {
323: s_enable = false;
324: return true;
325: }
326: }
327:
328: // n による1命令実行のブレークポイントか
1.1.1.2 root 329: if (n_enable && n_breakenable && n_breakaddr == md->GetPC()) {
1.1 root 330: n_breakenable = false;
331: return true;
332: }
333:
334: // アドレス
1.1.1.3 ! root 335: for (auto& bp : bpoint) {
! 336: if (bp.enable) {
! 337: if (bp.ismemory) {
! 338: if (md->CheckLEA(bp.addr)) {
! 339: bp.count++;
1.1.1.2 root 340: return true;
341: }
342: } else {
1.1.1.3 ! root 343: if (bp.addr == md->GetPC()) {
! 344: bp.count++;
1.1.1.2 root 345: return true;
346: }
1.1 root 347: }
348: }
349: }
350:
351: // ステップアウト
352: if (so_enable) {
1.1.1.2 root 353: if (md->IsStepOut()) {
1.1 root 354: so_enable = false;
355: return true;
356: }
357: }
358:
359: // アドレス指定付き continue
360: if (bc_enable) {
1.1.1.2 root 361: if (bc_addr == md->GetPC()) {
1.1 root 362: bc_enable = false;
363: return true;
364: }
365: }
366:
367: return false;
368: }
369:
370: // コマンド一覧。
1.1.1.3 ! root 371: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
! 372: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
! 373: { "bx", &Debugger::cmd_bx, Debugger::CommandAction::Stay },
! 374: { "b", &Debugger::cmd_b, Debugger::CommandAction::Stay },
! 375: { "brhist", &Debugger::cmd_brhist, Debugger::CommandAction::Stay },
! 376: { "c", &Debugger::cmd_c, Debugger::CommandAction::Leave },
! 377: { "d", &Debugger::cmd_d, Debugger::CommandAction::Stay },
! 378: { "dt", &Debugger::cmd_dt, Debugger::CommandAction::Stay },
! 379: { "D", &Debugger::cmd_D, Debugger::CommandAction::Stay },
! 380: { "h", &Debugger::cmd_h, Debugger::CommandAction::Stay },
! 381: { "help", &Debugger::cmd_h, Debugger::CommandAction::Stay },
! 382: { "L", &Debugger::cmd_L, Debugger::CommandAction::Stay },
! 383: { "m", &Debugger::cmd_m, Debugger::CommandAction::Stay },
! 384: { "mt", &Debugger::cmd_mt, Debugger::CommandAction::Stay },
! 385: { "M", &Debugger::cmd_M, Debugger::CommandAction::Stay },
! 386: { "n", &Debugger::cmd_n, Debugger::CommandAction::Leave },
! 387: { "q", &Debugger::cmd_q, Debugger::CommandAction::Quit },
! 388: { "quit", &Debugger::cmd_q, Debugger::CommandAction::Quit },
! 389: { "s", &Debugger::cmd_s, Debugger::CommandAction::Leave },
! 390: { "so", &Debugger::cmd_so, Debugger::CommandAction::Leave },
! 391: { "show", &Debugger::cmd_show, Debugger::CommandAction::Stay },
! 392: { "t", &Debugger::cmd_t, Debugger::CommandAction::Leave },
! 393: { "-", &Debugger::cmd_minus, Debugger::CommandAction::Stay },
1.1 root 394: };
395:
396: // ヘルプ
397: void
398: Debugger::cmd_h()
399: {
1.1.1.3 ! root 400: if (args.size() > 1) {
! 401: if (args[1] == "r") {
! 402: Help(md->GetRegisterHelp());
! 403: return;
! 404: }
! 405: }
! 406: // 引数なしか、あっても対応してないやつなら通常ヘルプを表示
! 407: Help(HelpMsgMain);
! 408: return;
! 409: }
! 410:
! 411: /*static*/ const HelpMessages
! 412: Debugger::HelpMsgMain = {
! 413: { "b", "ブレークポイント一覧表示" },
! 414: { "b #n", "ブレークポイント n を削除" },
! 415: { "b $<addr>", "メモリブレークポイント設定" },
! 416: { "b <addr>", "ブレークポイント設定" },
! 417: { "bx", "ブレークポイント全削除" },
! 418: { "brhist [<lines>]", "ブランチ履歴表示" },
! 419: { "c [<addr>]", "実行再開(continue)" },
! 420: { "d [<addr> [<lines>]", "逆アセンブル(論理、テーブルサーチなし)" },
! 421: { "dt [<addr> [<lines>]", "逆アセンブル(論理、テーブルサーチあり)" },
! 422: { "D [<addr> [<lines>]", "逆アセンブル(物理)" },
! 423: { "h", "ヘルプ(help)" },
! 424: { "L <name>=<lv>", "ログレベル設定" },
! 425: { "m [<addr> [<lines>]", "メモリダンプ(論理、テーブルサーチなし)" },
! 426: { "mt [<addr> [<lines>]", "メモリダンプ(論理、テーブルサーチあり)" },
! 427: { "M [<addr> [<lines>]", "メモリダンプ(物理)" },
! 428: { "n <cnt?:1>", "ステップ実行 (サブルーチンを飛ばす)" },
! 429: { "q", "quit" },
! 430: { "r*", "各種レジスタを表示 (see \"help r\")" },
! 431: { "so", "ステップアウト" },
! 432: { "show <monitor>", "モニター表示" },
! 433: { "t <cnt?:1>", "トレース実行" },
! 434: };
! 435:
! 436: void
! 437: Debugger::Help(HelpMessages msgs)
! 438: {
! 439: for (const auto& pair : msgs) {
! 440: cons->Print(" %-23s %s\n", pair.first.c_str(), pair.second.c_str());
1.1 root 441: }
442: }
443:
1.1.1.3 ! root 444: // cmdbuf を args... に分解する。
1.1 root 445: void
1.1.1.3 ! root 446: Debugger::ParseCmdbuf()
1.1 root 447: {
1.1.1.3 ! root 448: int pos;
! 449: int start;
! 450:
! 451: pos = 0;
! 452: args.clear();
1.1 root 453:
454: // 引数を空白で分解
1.1.1.3 ! root 455: for (;;) {
! 456: // 先頭の空白はスキップ
! 457: for (; cmdbuf[pos] != '\0'; pos++) {
! 458: if (!isspace((unsigned int)cmdbuf[pos]))
! 459: break;
! 460: }
! 461: if (cmdbuf[pos] == '\0')
1.1 root 462: break;
463:
1.1.1.3 ! root 464: // 終わりを探す
! 465: start = pos;
! 466: for (; cmdbuf[pos] != '\0'; pos++) {
! 467: if (isspace((unsigned int)cmdbuf[pos]))
1.1 root 468: break;
469: }
1.1.1.3 ! root 470: args.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2 root 471: }
472: // デバッグ表示
473: if (0) {
1.1.1.3 ! root 474: for (int i = 0 ; i < args.size(); i++) {
! 475: printf("args[%d]=|%s|\n", i, args[i].c_str());
1.1.1.2 root 476: }
477: }
1.1 root 478: }
479:
480: //
481: // デバッガコマンド
482: //
483:
484: // ブレークポイント
485: void
486: Debugger::cmd_b()
487: {
488: // 引数なしなら一覧表示
1.1.1.3 ! root 489: if (args.size() < 2) {
1.1 root 490: return cmd_b_list();
491: }
492:
493: // 引数取得
1.1.1.3 ! root 494: if (args[1][0] == '#') {
1.1 root 495: // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.3 ! root 496: int i = atoi(&args[1][1]);
! 497: if (i < 0 || i >= bpoint.size()) {
1.1 root 498: cons->Print("invaild break point number: #%d\n", i);
499: return;
500: }
1.1.1.3 ! root 501: auto& bp = bpoint[i];
! 502: if (bp.enable) {
1.1 root 503: cons->Print("%sbreakpoint #%d (%08x) removed\n",
1.1.1.3 ! root 504: bp.ismemory ? "memory " : "", i, bp.addr);
! 505: bp.enable = false;
1.1 root 506: } else {
507: cons->Print("breakpoint #%d not enabled\n", i);
508: }
509: return;
510: }
511:
512: bool ismemory;
513: uint32 addr;
1.1.1.3 ! root 514: bool rv;
! 515: if (args[1][0] == '$') {
1.1 root 516: // $<n> 形式ならメモリブレークポイント
517: ismemory = true;
1.1.1.3 ! root 518: rv = ParseAddr(&args[1][1], &addr);
1.1 root 519: } else {
520: // そうでなければアドレス指定
521: ismemory = false;
1.1.1.3 ! root 522: rv = ParseAddr(&args[1][0], &addr);
! 523: }
! 524: if (rv == false) {
! 525: return;
1.1 root 526: }
527:
528: // すでにあればそのブレークポイントを削除
1.1.1.3 ! root 529: for (int i = 0; i < bpoint.size(); i++) {
! 530: auto& bp = bpoint[i];
! 531: if (bp.enable && bp.ismemory == ismemory && bp.addr == addr) {
! 532: bp.enable = false;
1.1 root 533: cons->Print("%sbreakpoint #%d (%08x) removed\n",
1.1.1.3 ! root 534: bp.ismemory ? "memory " : "", i, bp.addr);
1.1 root 535: return;
536: }
537: }
538:
539: // 空いてるところにセット
540: int bi = AddBreakpoint(addr, ismemory);
541: if (bi == -1) {
542: cons->Print("no free breakpoints!\n");
543: } else {
1.1.1.3 ! root 544: auto& bp = bpoint[bi];
1.1 root 545: cons->Print("%sbreakpoint #%d (%08x) added\n",
1.1.1.3 ! root 546: bp.ismemory ? "memory " : "", bi, bp.addr);
1.1 root 547: }
548: }
549:
550: // ブレークポイント一覧表示
551: void
552: Debugger::cmd_b_list()
553: {
554: int found = 0;
555:
1.1.1.3 ! root 556: for (int i = 0; i < bpoint.size(); i++) {
! 557: auto& bp = bpoint[i];
! 558: if (bp.enable) {
1.1 root 559: cons->Print(" #%d %s%08x %d\n",
560: i,
1.1.1.3 ! root 561: bp.ismemory ? "memory $" : "",
! 562: bp.addr, bp.count);
1.1 root 563: found = 1;
564: }
565: }
566: if (found == 0) {
567: cons->Print(" No breakpoints enabled\n");
568: }
569: }
570:
571: // ブレークポイント全削除
572: void
573: Debugger::cmd_bx()
574: {
1.1.1.3 ! root 575: for (auto& bp : bpoint) {
! 576: bp.enable = false;
1.1 root 577: }
578: cons->Print(" All breakpoints disabled\n");
579: }
580:
581: // ブレークポイントを設定。
582: // 設定できればその番号、できなければ -1 を返す
583: // TODO 重複の処理とか
584: int
585: Debugger::AddBreakpoint(uint32 addr, bool ismemory)
586: {
1.1.1.3 ! root 587: for (int i = 0; i < bpoint.size(); i++) {
! 588: auto& bp = bpoint[i];
! 589: if (!bp.enable) {
! 590: bp.enable = true;
! 591: bp.addr = addr;
! 592: bp.ismemory = ismemory;
! 593: bp.count = 0;
1.1 root 594: return i;
595: }
596: }
597: return -1;
598: }
599:
600: // ブランチ履歴表示
601: void
602: Debugger::cmd_brhist()
603: {
1.1.1.3 ! root 604: int n = 20;
! 605:
! 606: if (args.size() > 1) {
! 607: n = atoi(args[1].c_str());
! 608: if (n <= 0) n = 1;
! 609: if (n > 255) n = 256;
! 610: }
! 611: // コンソールでは古い順に表示。最下行が最新。
! 612: auto& bh = (BranchHistory&)md->GetBrHist();
! 613: uint8 t = bh.top;
! 614:
! 615: t = t - n;
! 616: for (int i = 0; i < n; i++) {
! 617: auto& e = bh.entry[++t];
! 618: if (e.count == 0) continue;
! 619: cons->Print("%03d:%08x->%08x", n - i - 1, e.from, e.to);
! 620: if (e.count > 1) {
! 621: cons->Print(" x %u\n", e.count);
! 622: } else {
! 623: cons->Print("\n");
! 624: }
! 625: }
1.1 root 626: }
627:
628: // 実行再開(continue): c [<addr>]
629: // <addr> 指定があれば <addr> まで実行。
630: void
631: Debugger::cmd_c()
632: {
1.1.1.3 ! root 633: if (args.size() > 1) {
1.1 root 634: // 引数があれば
1.1.1.3 ! root 635: if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1 root 636: return;
637: }
638: // 偶数番地に丸める
639: bc_addr &= 0xfffffffe;
640:
641: bc_enable = true;
642: }
643: }
644:
645: // 引数などからアドレスを取得する共通ルーチン。
1.1.1.3 ! root 646: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1 root 647: // なければ、lastaddr が指定されていればそれを使う。
1.1.1.2 root 648: // それもなければ現在の PC を使う。
1.1 root 649: bool
650: Debugger::GetAddr(uint32& addr, uint32& lastaddr)
651: {
1.1.1.3 ! root 652: if (args.size() > 1) {
1.1 root 653: // 1つ目の引数があれば、開始アドレス
1.1.1.3 ! root 654: if (ParseAddr(args[1].c_str(), &addr) == false) {
1.1 root 655: return false;
656: }
657: // 偶数番地からに丸める
658: addr &= 0xfffffffe;
659: } else if (lastaddr != 0xffffffff) {
660: // 引数なしで前回値があれば前回の続き
661: addr = lastaddr;
662: } else {
663: // そうでなければ、今の PC
1.1.1.2 root 664: addr = md->GetPC();
1.1 root 665: }
666: return true;
667: }
668:
669: // 逆アセンブル: d [<addr> [<cnt>]] (論理、テーブルサーチなし)
670: void
671: Debugger::cmd_d()
672: {
673: cmd_d_common(MemdumpMode::Logical_atc);
674: }
675:
676: // 逆アセンブル: dt [<addr>] [<cnt>]] (論理、テーブルサーチあり)
677: void
678: Debugger::cmd_dt()
679: {
680: cmd_d_common(MemdumpMode::Logical_search);
681: }
682:
683: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
684: void
685: Debugger::cmd_D()
686: {
687: cmd_d_common(MemdumpMode::Physical);
688: }
689:
690: // 逆アセンブル共通
691: void
692: Debugger::cmd_d_common(MemdumpMode mode)
693: {
694: uint32 addr;
1.1.1.2 root 695: saddr_t saddr;
1.1 root 696: int cnt;
697:
698: cnt = 10;
699:
700: // 開始アドレス
701: if (GetAddr(addr, d_last_addr) == false) {
702: return;
703: }
704: // 2つ目の引数があれば行数
1.1.1.3 ! root 705: if (args.size() > 2) {
! 706: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 707: }
708:
1.1.1.2 root 709: saddr.addr = addr;
710: saddr.super = md->IsSuper();
711:
712: // XXX そのうちなんとかする
713: if (mode == MemdumpMode::Logical_search) {
714: saddr.logical = true;
715: saddr.search = true;
716: } else if (mode == MemdumpMode::Logical_atc) {
717: saddr.logical = true;
718: }
719:
1.1 root 720: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 721: std::string addrbuf;
722: std::string mnemonic;
723: std::string mnembuf;
724: std::vector<uint8> local_ir;
725:
726: if (FormatDumpAddr(saddr, addrbuf)) {
727: md->Disassemble(saddr, mnemonic, local_ir);
728: // オフライン用に整形
729: mnembuf = md->FormatDisasm(mnemonic, local_ir);
1.1 root 730: }
1.1.1.2 root 731: // アドレス変換でバスエラーが起きてもここは表示する
732: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
733:
734: // 変換できなければここで終了
735: if (local_ir.size() == 0)
736: break;
737: saddr.addr += local_ir.size();
1.1 root 738: }
1.1.1.2 root 739:
740: d_last_addr = saddr.addr;
1.1 root 741: }
742:
743: // ログレベル設定: L <name> <level>
744: void
745: Debugger::cmd_L()
746: {
747: const char *name;
748: int level;
749:
1.1.1.3 ! root 750: if (args.size() < 3) {
1.1 root 751: cons->Print("L <name> <level>\n");
752: return;
753: }
754:
1.1.1.3 ! root 755: name = args[1].c_str();
! 756: level = atoi(args[2].c_str());
1.1 root 757:
758: // 短縮形とかエイリアスとか
759: // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい
760: if (strcmp(name, "sch") == 0)
761: name = "scheduler";
762: if (strcmp(name, "scc") == 0)
763: name = "sio";
764:
1.1.1.3 ! root 765: // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1 root 766: // all はどうするか
767: std::string sname = name;
768: bool found = false;
769: for (auto& obj : gObjects) {
770: if (obj->logname == sname) {
771: obj->loglevel = level;
772: cons->Print("%s=%d\n", name, obj->loglevel);
773: found = true;
774: }
775: }
776: // 見付からない場合はエラー
777: if (!found) {
778: cons->Print("%s: invalid logname\n", name);
779: }
780: }
781:
782: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
783: void
784: Debugger::cmd_m()
785: {
786: cmd_m_common(MemdumpMode::Logical_atc);
787: }
788:
789: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
790: void
791: Debugger::cmd_mt()
792: {
793: cmd_m_common(MemdumpMode::Logical_search);
794: }
795:
796: // メモリダンプ: M [<addr> [<cnt>]] (物理)
797: void
798: Debugger::cmd_M()
799: {
800: cmd_m_common(MemdumpMode::Physical);
801: }
802:
803: // メモリダンプ共通
804: void
805: Debugger::cmd_m_common(MemdumpMode mode)
806: {
807: uint32 addr;
1.1.1.2 root 808: saddr_t saddr;
1.1 root 809: int cnt;
810:
811: cnt = 10;
812:
813: // 開始アドレス
814: if (GetAddr(addr, m_last_addr) == false) {
815: return;
816: }
817: // 2つ目の引数があれば行数
1.1.1.3 ! root 818: if (args.size() > 2) {
! 819: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 820: }
821:
1.1.1.2 root 822: saddr.addr = addr;
823: saddr.super = md->IsSuper();
824: // XXX
825: if (mode == MemdumpMode::Logical_search) {
826: saddr.logical = true;
827: saddr.search = true;
828: } else if (mode == MemdumpMode::Logical_atc) {
829: saddr.logical = true;
830: }
831:
1.1 root 832: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 833: std::string addrbuf;
834: std::string dumpbuf;
835: std::string charbuf;
836:
837: if (FormatDumpAddr(saddr, addrbuf) == false) {
838: // アドレス変換でバスエラーならダンプもくそもない
839: cons->Print("%s\n", addrbuf.c_str());
840: // ここで終わる?
841: // XXX 16バイト先を試してみるとか?
842: break;
843: }
844:
845: for (int j = 0; j < 8; j++) {
846: uint64 paddr;
847: // もう一回アドレス変換するのやや冗長だが仕方ない
848: if (saddr.IsLogical() && md->MMUEnabled()) {
849: // アドレス変換
850: paddr = md->TranslateAddr(saddr);
851: if ((int64)paddr < 0) {
852: // バスエラーが起きたら改行して仕切り直し
853: break;
854: }
855: } else {
856: paddr = saddr.addr;
857: }
858:
859: for (int k = 0; k < 2; k++) {
860: uint64 b = vm_phys_peek_8(paddr + k);
861:
862: if ((int64)b < 0) {
863: dumpbuf += "--";
864: charbuf += " ";
865: } else {
866: uint32 c = (uint32)b;
867: dumpbuf += string_format("%02x", c);
868: charbuf += string_format("%c",
869: (0x20 <= c && c <= 0x7e) ? c : '.');
870: }
871: }
872: dumpbuf += " ";
873: saddr.addr += 2;
874: }
875: cons->Print("%-18s %-40s %s\n",
876: addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str());
1.1 root 877: }
1.1.1.2 root 878: m_last_addr = saddr.addr;
1.1 root 879: }
880:
881: // プロンプトに来た最初に表示するいつものやつを再表示する
882: void
883: Debugger::cmd_minus()
884: {
1.1.1.3 ! root 885: // 通常レジスタを表示
! 886: std::vector<std::string> tmpargs {{ "r" }};
! 887: md->ShowRegister(cons, tmpargs);
1.1 root 888:
889: // 論理アクセス時、ATC ミスが分かったほうが便利だと思う
1.1.1.2 root 890: saddr_t laddr;
891: laddr.addr = pc;
892: laddr.super = md->IsSuper();
893: laddr.logical = true;
894: laddr.search = false;
895:
896: // 現在の PC の位置を逆アセンブル。ここで ir を更新。
897: std::string addrbuf;
898: std::string mnemonic;
899: std::string mnembuf;
900: ir.clear();
901: // アドレスを表示用に
902: if (FormatDumpAddr(laddr, addrbuf)) {
903: md->Disassemble(laddr, mnemonic, ir);
904: // ライブ表示用に整形
905: mnembuf = md->FormatDisasmLive(mnemonic, ir);
906: }
907: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
908:
909: // XXX m88k?
910: nextpc = pc + ir.size();
1.1 root 911: }
912:
913: // ステップ実行: n[<count>?=1] (サブルーチンを飛ばす)
914: void
915: Debugger::cmd_n()
916: {
1.1.1.3 ! root 917: if (args.size() > 1) {
1.1 root 918: int count;
1.1.1.3 ! root 919: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 920: if (count < 1) {
921: cons->Print(" invalid step count: %d\n", count);
922: return;
923: }
924:
925: n_count = count;
926: } else {
927: n_count = 1;
928: }
929: n_enable = true;
930: SetNBreakpoint();
931: }
932:
933: // n コマンド用のサブルーチンを飛ばすブレークポイントを設定。
934: // 戻り値は、プロンプトをスキップするなら true。
935: bool
936: Debugger::SetNBreakpoint()
937: {
938: // n コマンドは、通常はブレークポイントを次の命令に仕掛けながら
939: // 実行するが、いくつかの命令ではブレークポイントではなく、
940: // 命令トレース (t コマンド) のように動作させる。
941: // トレース動作する命令は以下の通り。
942: // Bcc, BRA
943: // JMP
944: // RTD, RTR, RTS
945: // RTE
946: // FBcc
947: // FDBcc (2ワード目無視)
948: // (RTM は 68020 専用なので対応しない)
949: #define IsBcc(x) ((((x) & 0xf000) == 0x6000) && (((x) & 0xff00) != 0x6100))
950: #define IsJMP(x) (((x) & 0xffc0) == 0x4ec0)
951: #define IsRTD(x) (((x) & 0xffff) == 0x4e74)
952: #define IsRTE(x) (((x) & 0xffff) == 0x4e73)
953: #define IsRTR(x) (((x) & 0xffff) == 0x4e77)
954: #define IsRTS(x) (((x) & 0xffff) == 0x4e75)
955: #define IsFBcc(x) (((x) & 0xf180) == 0xf080)
956: #define IsFDBcc(x) (((x) & 0xf1f8) == 0xf048)
957:
958: uint16 op;
959:
960: // XXX この辺統一的にしたほうがいい
961: // XXX logical peek のような気がする
1.1.1.2 root 962: op = (((uint16)vm_phys_peek_8(pc)) << 8)
963: | vm_phys_peek_8(pc + 1);
1.1 root 964:
965: if (IsBcc(op)
966: || IsJMP(op)
967: || IsRTD(op)
968: || IsRTE(op)
969: || IsRTR(op)
970: || IsRTS(op)
971: || IsFBcc(op)
972: || IsFDBcc(op)
973: ) {
974: n_breakenable = false;
975: s_enable = true;
976: s_count = 1;
977: return false;
978: } else {
979: n_breakenable = true;
980: n_breakaddr = nextpc;
981: return true;
982: }
983: }
984:
1.1.1.2 root 985: // デバッガ終了コマンド
986: void
987: Debugger::cmd_q()
988: {
989: cons->Print("quit debugger console.\n");
1.1 root 990: }
991:
992: // モニター表示
993: void
994: Debugger::cmd_show()
995: {
1.1.1.3 ! root 996: if (args.size() != 2) {
1.1 root 997: cons->Print("usage: show <monitor>\n");
998: for (auto& d : gObjects) {
999: if (d->logname != "?") {
1000: cons->Print(" %s\n", d->logname.c_str());
1001: }
1002: }
1003: return;
1004: }
1005:
1.1.1.3 ! root 1006: std::string name = args[1];
1.1 root 1007: for (auto& d : gObjects) {
1008: if (d->logname == name) {
1.1.1.3 ! root 1009: ShowMonitor(*d);
1.1 root 1010: return;
1011: }
1012: }
1013: // 一致しない
1014: cons->Print("show: no such objects\n");
1015: }
1016:
1.1.1.3 ! root 1017: // モニターを更新して表示。
! 1018: void
! 1019: Debugger::ShowMonitor(Object& obj)
! 1020: {
! 1021: // モニタを更新
! 1022: if (obj.MonitorUpdate() == false) {
! 1023: cons->Print("show: no monitor\n");
! 1024: return;
! 1025: }
! 1026: ShowTextScreen(obj.monitor);
! 1027: }
! 1028:
! 1029: // テキストスクリーンを表示。
1.1 root 1030: void
1.1.1.3 ! root 1031: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 1032: {
1033: char sbuf[1024]; // 適当
1034:
1035: // monitor 内部バッファから ShiftJIS バッファを作成。
1036: // その際属性もエスケープシーケンスで再現する。
1037: int col = monitor.GetCol();
1038: int row = monitor.GetRow();
1039: const uint16 *src = monitor.GetBuf();
1040:
1041: for (int y = 0; y < row; y++) {
1042: int sy = y * col;
1043: int sx;
1044: uint attr;
1045: char *d;
1046:
1047: memset(sbuf, 0, sizeof(sbuf));
1048: d = sbuf;
1049: attr = TA::Normal;
1050: for (sx = 0; sx < col; sx++) {
1051: uint a = src[sy + sx] & 0xff00;
1052: uint ch = src[sy + sx] & 0x00ff;
1053:
1054: // 属性の変わり目
1055: if (attr != a) {
1056: switch (a) {
1057: case TA::Normal:
1058: case TA::Off:
1059: *d++ = 0x1b;
1060: *d++ = '[';
1061: *d++ = 'm';
1062: break;
1063: case TA::On:
1064: *d++ = 0x1b;
1065: *d++ = '[';
1066: *d++ = '7';
1067: *d++ = 'm';
1068: break;
1069: case TA::Disable:
1070: *d++ = 0x1b;
1071: *d++ = '[';
1072: *d++ = '2';
1073: *d++ = 'm';
1074: break;
1075: case TA::Em:
1076: *d++ = 0x1b;
1077: *d++ = '[';
1078: *d++ = '1';
1079: *d++ = 'm';
1080: break;
1081: }
1082: attr = a;
1083: }
1084: *d++ = ch;
1085: }
1.1.1.3 ! root 1086: // 行の終わりで一旦属性を戻しておく
! 1087: if (attr != TA::Normal) {
! 1088: *d++ = 0x1b;
! 1089: *d++ = '[';
! 1090: *d++ = 'm';
! 1091: }
1.1 root 1092: *d = '\0';
1093:
1094: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
1095:
1096: cons->Print("%s\n", sbuf);
1097: }
1098: }
1099:
1100: // ステップ実行: s[<count>?:1]
1101: void
1102: Debugger::cmd_s()
1103: {
1.1.1.3 ! root 1104: if (args.size() > 1) {
1.1 root 1105: int count;
1.1.1.3 ! root 1106: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1107: if (count < 1) {
1108: cons->Print(" invalid step count: %d\n", count);
1109: return;
1110: }
1111:
1112: s_count = count;
1113: } else {
1114: s_count = 1;
1115: }
1116: s_enable = true;
1117: }
1118:
1119: // ステップアウト
1120: void
1121: Debugger::cmd_so()
1122: {
1123: so_enable = true;
1.1.1.2 root 1124: md->SetStepOut();
1.1 root 1125: }
1126:
1127: // トレース実行: t[<count>?:1]
1128: // レジスタを表示しながら実行する
1129: void
1130: Debugger::cmd_t()
1131: {
1.1.1.3 ! root 1132: if (args.size() > 1) {
1.1 root 1133: int count;
1.1.1.3 ! root 1134: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1135: if (count < 1) {
1136: cons->Print(" invalid trace count: %d\n", count);
1137: return;
1138: }
1139: t_count = count;
1140: } else {
1141: t_count = 1;
1142: }
1143: t_enable = true;
1144: }
1145:
1146: // 引数で示されるアドレスを返す。
1147: // '%' で始まればレジスタ。
1148: // そうでなければ16進数値。
1149: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
1150: // そうでなければエラーメッセージを表示して偽を返す。
1151: bool
1152: Debugger::ParseAddr(const char *arg, uint32_t *addrp)
1153: {
1154: uint32_t addr;
1155: char *end;
1156:
1157: if (arg[0] == '%') {
1.1.1.2 root 1158: // '%' から始まればレジスタ
1159: uint64 r;
1160: r = md->GetRegAddr(arg + 1);
1161: if ((int64)r < 0) {
1162: #if 0 // not yet
1.1 root 1163: cons->Print("valid register name are: %s%s\n",
1164: "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
1165: ",%msp,%vbr,%srp,%crp");
1.1.1.2 root 1166: #endif
1167: cons->Print("invalid register name\n");
1.1 root 1168: return false;
1169: }
1.1.1.2 root 1170: addr = (uint32)r;
1.1 root 1171: } else {
1172: /* そうでなければ番地指定 */
1173: errno = 0;
1174: addr = strtoul(arg, &end, 16);
1175: if (arg[0] == '\0' || end[0] != '\0') {
1176: cons->Print("invalid address: '%s'\n", arg);
1177: return false;
1178: }
1179: if (errno == ERANGE) {
1180: cons->Print("out of range: %s\n", arg);
1181: return false;
1182: }
1183: }
1184:
1185: *addrp = addr;
1186: return true;
1187: }
1188:
1.1.1.2 root 1189: // (論理、物理)アドレスを表示用に整形。
1190: // 逆アセンブル表示、メモリダンプ表示等で共通。
1191: // アドレス変換でバスエラーが起きたら false を返す (この場合も str は有効)。
1192: // 末尾を空白パディングはしていないので必要なら "%-18s" 等して使うこと。
1193: // 123456789012345678
1194: // "00112233:44556677:"
1.1 root 1195: bool
1.1.1.2 root 1196: Debugger::FormatDumpAddr(saddr_t laddr, std::string& str)
1.1 root 1197: {
1.1.1.2 root 1198: bool rv = true;
1.1 root 1199:
1.1.1.2 root 1200: // laddr は常に表示
1201: str = string_format("%08x", laddr.addr);
1.1 root 1202:
1.1.1.2 root 1203: if (laddr.IsLogical() && md->MMUEnabled()) {
1204: // アドレス変換してみる
1205: uint64 paddr = md->TranslateAddr(laddr);
1206: if ((int64)paddr < 0) {
1207: // MMU 時点でバスエラー
1208: str += ":BusErr";
1209: rv = false;
1210: } else if (paddr == laddr.addr) {
1211: // PA==VA
1212: str += "=PA";
1.1 root 1213: } else {
1.1.1.2 root 1214: // PA!=VA
1215: str += string_format(":%08x:", (uint32)paddr);
1.1 root 1216: }
1.1.1.2 root 1217: } else {
1218: // 物理アクセス
1219: str += ":";
1.1 root 1220: }
1221:
1.1.1.2 root 1222: return rv;
1.1 root 1223: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.