|
|
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.1.4 ! root 18: BreakpointMonitor *gBreakpointMonitor;
1.1 root 19: CVPrompt *gCVPrompt;
20:
21: static void *debugger_run(void *);
22:
23: // 初期化。
24: // この時点で VM が初期化されていること。
25: void
26: debugger_init()
27: {
28: gDebugger = new Debugger();
29: gDebugger->Init();
30:
1.1.1.4 ! root 31: gBreakpointMonitor = new BreakpointMonitor();
1.1 root 32: gCVPrompt = new CVPrompt();
33:
34: // デバッガスレッド起動
35: pthread_t th;
36: pthread_create(&th, NULL, debugger_run, NULL);
37: }
38:
39: // デバッガスレッドのエントリポイント
40: static void *
41: debugger_run(void *dummy)
42: {
1.1.1.3 root 43: PTHREAD_SETNAME("Debugger");
1.1 root 44: pthread_detach(pthread_self());
45:
46: gDebugger->ThreadRun();
47: return NULL;
48: }
49:
50: // コンストラクタ
51: Debugger::Debugger()
52: {
1.1.1.2 root 53: if (gMPU680x0) {
54: md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU());
55: } else if (gMPU88xx0) {
56: md = new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU());
57: } else {
58: throw "unknown mpu";
59: }
1.1 root 60: }
61:
62: // コマンドライン引数によって動作を決めるところ。
63: // 名前がこれでいいのかはあるけど。
64: void
65: Debugger::Init()
66: {
67: // -d なら CPU 起動時点で停止してプロンプトを待つ
1.1.1.2 root 68: if (gMainApp.debug_on_start) {
69: md->ReqSet(CPU_REQ_PROMPT);
1.1 root 70: }
71:
72: // -b <addr> ならブレークポイント設定
1.1.1.4 ! root 73: for (auto& str : gMainApp.debug_breakaddr) {
! 74: breakpoint_t bp;
! 75:
1.1.1.2 root 76: md->ReqSet(CPU_REQ_TRACE);
1.1.1.4 ! root 77:
! 78: // <addr>,<skip> で分離できたら <skip> を取り出す
! 79: int pos = str.find(',');
! 80: if (pos != std::string::npos) {
! 81: std::string skipstr = str.substr(pos + 1);
! 82: bp.skip = atoi(skipstr.c_str());
! 83:
! 84: // XXX これ出来るんだっけ?
! 85: str[pos] = '\0';
! 86: }
! 87: // そしてどちらにしても <addr> を取り出す
! 88: if (!ParseAddr(str.c_str(), &bp.addr)) {
! 89: warnx("\"%s\": Invalid breakpoint address", str.c_str());
! 90: return;
! 91: }
! 92: // 登録
! 93: bp.type = BreakpointType::Address;
! 94: AddBreakpoint(bp);
1.1 root 95: }
96:
97: // どこでやるべか
98: signal(SIGPIPE, SIG_IGN);
99: }
100:
101: // デバッガスレッド
102: void
103: Debugger::ThreadRun()
104: {
105: // XXX どこかでコンソールの選択とパラメータの取得
1.1.1.2 root 106: if (gMainApp.debug_on_console) {
107: cons = new ConsoleStdio();
108: } else {
109: cons = new ConsoleTCP();
110: }
1.1 root 111: if (cons->Init() == false) {
1.1.1.3 root 112: delete cons;
1.1 root 113: return;
114: }
115:
116: for (;;) {
1.1.1.3 root 117: // 着信待ち
118: if (Accept() == false) {
119: break;
120: }
121:
1.1.1.2 root 122: cons->InitEditLine("> ");
1.1 root 123:
1.1.1.3 root 124: // デバッガプロンプトにいる間はトレースオン
125: md->ReqSet(CPU_REQ_TRACE);
126:
127: bool first = true;
128: for (;;) {
129: // 接続後の1回目だけ実行するもの。
130: if (first) {
131: first = false;
132:
133: // greeting はプロンプトが取れる前にもう表示したい。
134: // 何らかの事故でプロンプトが取れなくても、ここまでは接続
135: // できてることが分かるように。
136: cons->Print("This is debugger console\n");
137:
138: // 接続ごとに初期化する値
139: // XXX もうちょっときれいにしたい
140: d_last_addr = 0xffffffff;
141: m_last_addr = 0xffffffff;
142: n_enable = false;
143: s_enable = false;
144: t_enable = false;
145: }
146:
147: // プロンプトが取れるのを待つ
148: if (AcquirePrompt() == false) {
149: break;
150: }
151:
152: // プロンプトに来た時に表示するいつものやつ
153: pc = md->GetPC();
154: d_last_addr = pc;
155: cmd_minus();
156:
157: // コマンド処理のメインループ
158: auto action = MainLoop();
159:
160: // 次回との差分のため、今のレジスタセットをバックアップ
161: md->BackupRegs();
162:
163: // メインループから戻ったということは Leave か Quit なので
164: // どちらにしても、ここでプロンプトを手放す。
165: gCVPrompt->NotifyRelease();
166:
167: // quit ならここでループを1段抜ける
168: if (action == CommandAction::Quit) {
169: break;
170: }
171: }
172:
173: // デバッガを抜けるのでトレースオフ
1.1.1.2 root 174: md->ReqClr(CPU_REQ_TRACE);
1.1 root 175:
176: cons->Close();
1.1.1.3 root 177: if (gMainApp.debug_on_console) {
178: break;
179: }
1.1 root 180: }
181:
182: delete cons;
183: }
184:
1.1.1.3 root 185: // コンソールに接続されるのを待つ。
186: // うーん、なんだこれ。
187: //
188: // +--- debug_on_console (-D オプション)
189: // | +- debug_on_start (-d オプション)
190: // | | 動作
191: // 0 0 TCP、Accept で待機。
192: // 0 1 TCP、Accpet で待機。
193: // 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。
194: // 1 1 stdio、起動時にプロンプトで止まる。
1.1 root 195: bool
1.1.1.3 root 196: Debugger::Accept()
197: {
198: if (gMainApp.debug_on_console == false) {
199: // TCP なら accept が成功するのを待つ
200: cons->Accept();
201: } else {
202: if (gMainApp.debug_on_start == false) {
203: // stdio、起動時にプロンプトで止まらない。
204: // この場合キー入力かブレークポイントなどで止まった時点で
205: // プロンプトを出したいのでちょっと面倒なことに。
206: for (;;) {
207: bool is_prompt = gCVPrompt->WaitAcquire(200);
208: if (is_prompt) {
209: break;
210: }
211:
212: int r = cons->Poll(200);
213: if (r < 0) {
214: cmd_q();
215: return false;
216: }
217: if (r > 0) {
218: break;
219: }
220: }
221: } else {
222: // stdio、起動時にプロンプトで止まる。
223: // この場合は無条件に成功でよい。
224: }
225: }
226:
227: return true;
228: }
229:
230: // プロンプトが取れるのを待つ
231: // 失敗すれば false を返す
232: bool
233: Debugger::AcquirePrompt()
1.1 root 234: {
1.1.1.3 root 235: md->ReqSet(CPU_REQ_PROMPT);
236:
1.1.1.2 root 237: for (;;) {
238: bool is_prompt = gCVPrompt->WaitAcquire(200);
1.1.1.3 root 239: if (is_prompt)
240: break;
1.1.1.2 root 241:
242: // コンソールを定期観測して、入力があればブレーク要求
243: if (cons->Poll()) {
244: // この入力は drop してみる
245: if (cons->Gets(cmdbuf) == false) {
246: // EOF or Error
247: cmd_q();
248: return false;
249: }
250: md->ReqSet(CPU_REQ_PROMPT);
251: }
252: }
1.1.1.3 root 253: return true;
254: }
1.1 root 255:
1.1.1.3 root 256: // コマンド処理のメインループ。
257: // コマンドが Stay ならこの中で処理を継続。
258: // コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。
259: Debugger::CommandAction
260: Debugger::MainLoop()
261: {
262: for (;;) {
263: cons->Prompt();
264: if (cons->Gets(cmdbuf) == false) {
265: // EOF or Error
266: return CommandAction::Quit;
267: }
1.1 root 268:
1.1.1.3 root 269: string_rtrim(cmdbuf);
1.1 root 270:
1.1.1.3 root 271: if (!cmdbuf.empty()) {
272: // コマンドが入力されれば次回のために保存
273: last_cmdbuf = cmdbuf;
274: } else {
275: // 空行が入力されれば直前のコマンドをもう一度
276: // 前行がなければ何もせずもう一度プロンプトを表示するかね
277: if (last_cmdbuf.empty())
278: continue;
279: cmdbuf = last_cmdbuf;
280: }
281:
282: // 行を args に分解
283: ParseCmdbuf();
284:
285: // 前回の値が有効なのはコマンドが連続した時だけ
286:
287: // コマンドをテーブルから探す
288: auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
289: [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; });
290: if (it != cmdtable.end()) {
291: auto cmd = *it;
292:
293: // 見付かれば実行
294: (this->*(cmd.func))();
295:
296: // Stay なら引き続きコマンド処理
297: if (cmd.action == CommandAction::Stay) {
298: continue;
299: }
300: // それ以外ならメインループ終了
301: return cmd.action;
302: }
1.1 root 303:
1.1.1.3 root 304: // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
305: if (args[0][0] == 'r') {
306: // ShowRegister() は処理したら true を返す。
307: // "r" 系コマンドはすべて Stay。
308: if (md->ShowRegister(cons, args)) {
309: continue;
310: }
311: }
1.1.1.2 root 312:
1.1.1.3 root 313: // 知らないコマンドも Stay 相当。
314: cons->Print("%s: unknown command\n", args[0].c_str());
315: continue;
316: }
317: __unreachable();
1.1 root 318: }
319:
320: // ブレークポイントとかを調べる。
321: // CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。
322: bool
323: debugger_check()
324: {
325: return gDebugger->Check();
326: }
327:
328: // デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。
329: // プロンプトに降りるなら true を返す。
330: bool
331: Debugger::Check()
332: {
333: if (t_enable) {
334: t_count--;
335: if (t_count == 0) {
336: t_enable = false;
337: return true;
338: }
339: }
340:
341: if (s_enable) {
342: s_count--;
343: if (s_count == 0) {
344: s_enable = false;
345: return true;
346: }
347: }
348:
349: // n による1命令実行のブレークポイントか
1.1.1.2 root 350: if (n_enable && n_breakenable && n_breakaddr == md->GetPC()) {
1.1 root 351: n_breakenable = false;
352: return true;
353: }
354:
1.1.1.4 ! root 355: // ブレークポイント
! 356: if (CheckAllBreakpoints()) {
! 357: return true;
1.1 root 358: }
359:
360: // ステップアウト
361: if (so_enable) {
1.1.1.2 root 362: if (md->IsStepOut()) {
1.1 root 363: so_enable = false;
364: return true;
365: }
366: }
367:
368: // アドレス指定付き continue
369: if (bc_enable) {
1.1.1.2 root 370: if (bc_addr == md->GetPC()) {
1.1 root 371: bc_enable = false;
372: return true;
373: }
374: }
375:
376: return false;
377: }
378:
1.1.1.4 ! root 379: // ブレークポイントがどれかでも成立するかを調べる。
! 380: // 1つ以上成立してブレークするなら true を返す。
! 381: // 1つも成立しておらずブレークしないなら false を返す。
! 382: bool
! 383: Debugger::CheckAllBreakpoints()
! 384: {
! 385: bool is_break = false;
! 386:
! 387: // 命令ごとにクリアする
! 388: bi_inst = 0;
! 389: bi_inst_bytes = 0;
! 390:
! 391: // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが)
! 392: // 残りの他の条件も成立すればカウントを進める必要があるため、
! 393: // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。
! 394: for (auto& bp : bpoint) {
! 395: switch (bp.type) {
! 396: case BreakpointType::Address:
! 397: if (bp.addr != md->GetPC()) {
! 398: continue;
! 399: }
! 400: break;
! 401:
! 402: case BreakpointType::Memory:
! 403: if (!md->CheckLEA(bp.addr)) {
! 404: continue;
! 405: }
! 406: break;
! 407:
! 408: case BreakpointType::Exception:
! 409: if (bv_vector >= 0) {
! 410: if (bp.vec1 <= bv_vector && bv_vector <= bp.vec2) {
! 411: break;
! 412: }
! 413: }
! 414: continue;
! 415:
! 416: case BreakpointType::Instruction:
! 417: if (CheckBreakpointInst(bp) == false) {
! 418: continue;
! 419: }
! 420: break;
! 421:
! 422: default:
! 423: continue;
! 424: }
! 425:
! 426: // 条件は成立したのでカウントとスキップ
! 427: bp.matched++;
! 428:
! 429: // skip == -1 は常にブレークしない (カウントするだけ)
! 430: if (bp.skip < 0) {
! 431: continue;
! 432: }
! 433:
! 434: // skip が 1 以上なら、remain を減算
! 435: if (bp.skip > 0 && --bp.skipremain > 0) {
! 436: continue;
! 437: }
! 438:
! 439: // ブレーク成立
! 440: is_break = true;
! 441: }
! 442:
! 443: // 例外通知は通過ごとに常に下ろしておく
! 444: bv_vector = -1;
! 445:
! 446: // ブレークポイントが1つでも成立したかどうかを返す
! 447: return is_break;
! 448: }
! 449:
! 450: // 命令ブレークポイントが成立するか調べる。
! 451: bool
! 452: Debugger::CheckBreakpointInst(breakpoint_t& bp)
! 453: {
! 454: saddr_t laddr;
! 455: uint64 paddr;
! 456:
! 457: // uint32 bi_inst が現在の PC 位置の命令データ (左詰め)
! 458: // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。
! 459: // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。
! 460:
! 461: laddr.addr = md->GetPC();
! 462: laddr.super = md->IsSuper();
! 463: laddr.logical = true;
! 464: laddr.search = true;
! 465: // 必要なバイト数に達するまで読み足す
! 466: while (bi_inst_bytes < bi_need_bytes) {
! 467: // アドレスを求める。
! 468: // 論理アクセスで ATC になければテーブルサーチも試みる。
! 469: if (md->MMUEnabled()) {
! 470: paddr = md->TranslateAddr(laddr);
! 471: if ((int64)paddr < 0) {
! 472: // MMU 時点でバスエラー
! 473: return false;
! 474: }
! 475: } else {
! 476: paddr = laddr.addr;
! 477: }
! 478:
! 479: // 命令長ずつ読み込む。
! 480: for (int i = 0; i < md->inst_bits / 8; i++) {
! 481: uint64 data = vm_phys_peek_8(paddr);
! 482: if ((int64)data < 0) {
! 483: return false;
! 484: }
! 485: bi_inst = (bi_inst << 8) | data;
! 486: bi_inst_bytes++;
! 487: laddr.addr++;
! 488: paddr++;
! 489: }
! 490: }
! 491: // 左詰めにする
! 492: bi_inst <<= (4 - bi_inst_bytes) * 8;
! 493:
! 494: // 必要なバイト数取得できたので比較
! 495: if ((bi_inst & bp.mask) == bp.inst) {
! 496: return true;
! 497: }
! 498: return false;
! 499: }
! 500:
! 501: // 例外通知 (MPU からの連絡用)
! 502: void
! 503: debugger_notifyexception(int vector)
! 504: {
! 505: gDebugger->NotifyException(vector);
! 506: }
! 507:
! 508: // 例外通知 (本体)
! 509: void
! 510: Debugger::NotifyException(int vector)
! 511: {
! 512: bv_vector = vector;
! 513: }
! 514:
1.1 root 515: // コマンド一覧。
1.1.1.3 root 516: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
517: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
1.1.1.4 ! root 518: { "bi", &Debugger::cmd_bi, Debugger::CommandAction::Stay },
! 519: { "bm", &Debugger::cmd_bm, Debugger::CommandAction::Stay },
! 520: { "bv", &Debugger::cmd_bv, Debugger::CommandAction::Stay },
1.1.1.3 root 521: { "bx", &Debugger::cmd_bx, Debugger::CommandAction::Stay },
522: { "b", &Debugger::cmd_b, Debugger::CommandAction::Stay },
523: { "brhist", &Debugger::cmd_brhist, Debugger::CommandAction::Stay },
524: { "c", &Debugger::cmd_c, Debugger::CommandAction::Leave },
525: { "d", &Debugger::cmd_d, Debugger::CommandAction::Stay },
526: { "dt", &Debugger::cmd_dt, Debugger::CommandAction::Stay },
527: { "D", &Debugger::cmd_D, Debugger::CommandAction::Stay },
1.1.1.4 ! root 528: { "exhist", &Debugger::cmd_exhist, Debugger::CommandAction::Stay },
1.1.1.3 root 529: { "h", &Debugger::cmd_h, Debugger::CommandAction::Stay },
530: { "help", &Debugger::cmd_h, Debugger::CommandAction::Stay },
531: { "L", &Debugger::cmd_L, Debugger::CommandAction::Stay },
532: { "m", &Debugger::cmd_m, Debugger::CommandAction::Stay },
533: { "mt", &Debugger::cmd_mt, Debugger::CommandAction::Stay },
534: { "M", &Debugger::cmd_M, Debugger::CommandAction::Stay },
535: { "n", &Debugger::cmd_n, Debugger::CommandAction::Leave },
536: { "q", &Debugger::cmd_q, Debugger::CommandAction::Quit },
537: { "quit", &Debugger::cmd_q, Debugger::CommandAction::Quit },
538: { "s", &Debugger::cmd_s, Debugger::CommandAction::Leave },
539: { "so", &Debugger::cmd_so, Debugger::CommandAction::Leave },
540: { "show", &Debugger::cmd_show, Debugger::CommandAction::Stay },
541: { "t", &Debugger::cmd_t, Debugger::CommandAction::Leave },
542: { "-", &Debugger::cmd_minus, Debugger::CommandAction::Stay },
1.1 root 543: };
544:
545: // ヘルプ
546: void
547: Debugger::cmd_h()
548: {
1.1.1.3 root 549: if (args.size() > 1) {
550: if (args[1] == "r") {
551: Help(md->GetRegisterHelp());
552: return;
553: }
554: }
555: // 引数なしか、あっても対応してないやつなら通常ヘルプを表示
556: Help(HelpMsgMain);
557: return;
558: }
559:
560: /*static*/ const HelpMessages
561: Debugger::HelpMsgMain = {
562: { "b", "ブレークポイント一覧表示" },
563: { "b #n", "ブレークポイント n を削除" },
1.1.1.4 ! root 564: { "b <addr> [<skip>]", "ブレークポイント設定" },
! 565: { "bm <addr> [<skip>]", "メモリブレークポイント設定" },
! 566: { "bi <inst>[:<mask>] [<skip>]", "命令ブレークポイント設定" },
! 567: { "bv <vec>[-<vec>] [<skip>]", "例外ブレークポイント設定" },
1.1.1.3 root 568: { "bx", "ブレークポイント全削除" },
569: { "brhist [<lines>]", "ブランチ履歴表示" },
570: { "c [<addr>]", "実行再開(continue)" },
571: { "d [<addr> [<lines>]", "逆アセンブル(論理、テーブルサーチなし)" },
572: { "dt [<addr> [<lines>]", "逆アセンブル(論理、テーブルサーチあり)" },
573: { "D [<addr> [<lines>]", "逆アセンブル(物理)" },
1.1.1.4 ! root 574: { "exhist [<lines>]", "例外履歴表示" },
1.1.1.3 root 575: { "h", "ヘルプ(help)" },
576: { "L <name>=<lv>", "ログレベル設定" },
577: { "m [<addr> [<lines>]", "メモリダンプ(論理、テーブルサーチなし)" },
578: { "mt [<addr> [<lines>]", "メモリダンプ(論理、テーブルサーチあり)" },
579: { "M [<addr> [<lines>]", "メモリダンプ(物理)" },
580: { "n <cnt?:1>", "ステップ実行 (サブルーチンを飛ばす)" },
581: { "q", "quit" },
582: { "r*", "各種レジスタを表示 (see \"help r\")" },
1.1.1.4 ! root 583: { "s [<cnt?:1>]", "ステップ実行 (途中レジスタを表示しない)" },
1.1.1.3 root 584: { "so", "ステップアウト" },
585: { "show <monitor>", "モニター表示" },
1.1.1.4 ! root 586: { "t [<cnt?:1>]", "トレース実行 (命令ごとにレジスタを表示)" },
1.1.1.3 root 587: };
588:
589: void
590: Debugger::Help(HelpMessages msgs)
591: {
592: for (const auto& pair : msgs) {
593: cons->Print(" %-23s %s\n", pair.first.c_str(), pair.second.c_str());
1.1 root 594: }
595: }
596:
1.1.1.3 root 597: // cmdbuf を args... に分解する。
1.1 root 598: void
1.1.1.3 root 599: Debugger::ParseCmdbuf()
1.1 root 600: {
1.1.1.3 root 601: int pos;
602: int start;
603:
604: pos = 0;
605: args.clear();
1.1 root 606:
607: // 引数を空白で分解
1.1.1.3 root 608: for (;;) {
609: // 先頭の空白はスキップ
610: for (; cmdbuf[pos] != '\0'; pos++) {
611: if (!isspace((unsigned int)cmdbuf[pos]))
612: break;
613: }
614: if (cmdbuf[pos] == '\0')
1.1 root 615: break;
616:
1.1.1.3 root 617: // 終わりを探す
618: start = pos;
619: for (; cmdbuf[pos] != '\0'; pos++) {
620: if (isspace((unsigned int)cmdbuf[pos]))
1.1 root 621: break;
622: }
1.1.1.3 root 623: args.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2 root 624: }
625: // デバッグ表示
626: if (0) {
1.1.1.3 root 627: for (int i = 0 ; i < args.size(); i++) {
628: printf("args[%d]=|%s|\n", i, args[i].c_str());
1.1.1.2 root 629: }
630: }
1.1 root 631: }
632:
633: //
634: // デバッガコマンド
635: //
636:
637: // ブレークポイント
1.1.1.4 ! root 638: // b ... 一覧表示
! 639: // b #n ... #n を削除
! 640: // b <addr> [<skip>] ... 設定
1.1 root 641: void
642: Debugger::cmd_b()
643: {
644: // 引数なしなら一覧表示
1.1.1.3 root 645: if (args.size() < 2) {
1.1 root 646: return cmd_b_list();
647: }
648:
649: // 引数取得
1.1.1.3 root 650: if (args[1][0] == '#') {
1.1 root 651: // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.3 root 652: int i = atoi(&args[1][1]);
653: if (i < 0 || i >= bpoint.size()) {
1.1.1.4 ! root 654: cons->Print("invaild breakpoint number: #%d\n", i);
1.1 root 655: return;
656: }
1.1.1.3 root 657: auto& bp = bpoint[i];
1.1.1.4 ! root 658: if (bp.type == BreakpointType::Unused) {
! 659: cons->Print("invalid breakpoint number: #%d\n", i);
! 660: return;
1.1 root 661: }
1.1.1.4 ! root 662: cons->Print("breakpoint #%d (%08x) removed\n",
! 663: i, bp.addr);
! 664: bp.type = BreakpointType::Unused;
! 665: // 今登録されている命令ブレークの必要命令長を再計算
! 666: RecalcInstMask();
1.1 root 667: return;
668: }
669:
1.1.1.4 ! root 670: return cmd_b_set(BreakpointType::Address);
! 671: }
! 672:
! 673: // メモリブレークポイントの設定
! 674: // bm <addr> [<skip>]
! 675: void
! 676: Debugger::cmd_bm()
! 677: {
! 678: // XXX m68k では未サポート
! 679: if (dynamic_cast<DebuggerMD_m680x0 *>(md)) {
! 680: cons->Print("bm not supported yet on m68k\n");
! 681: return;
! 682: }
! 683: cmd_b_set(BreakpointType::Memory);
! 684: }
! 685:
! 686: // type が違うだけの各種ブレークポイント設定の共通部分。
! 687: void
! 688: Debugger::cmd_b_set(BreakpointType type)
! 689: {
! 690: breakpoint_t bp;
! 691:
! 692: if (args.size() < 2) {
! 693: cons->Print("usage: %s <addr> [<skipcount>]\n", args[0].c_str());
! 694: return;
! 695: }
! 696:
! 697: // アドレス
! 698: if (!ParseAddr(args[1].c_str(), &bp.addr)) {
! 699: return;
! 700: }
! 701: // あればスキップカウント
! 702: if (args.size() > 2) {
! 703: bp.skip = atoi(args[2].c_str());
! 704: }
! 705:
! 706: // 空いてるところにセット
! 707: // (よく似たエントリがあっても干渉しない)
! 708: bp.type = type;
! 709: int bi = AddBreakpoint(bp);
! 710: if (bi == -1) {
! 711: cons->Print("no free breakpoints\n");
1.1 root 712: } else {
1.1.1.4 ! root 713: cons->Print("breakpoint #%d added\n", bi);
1.1.1.3 root 714: }
1.1.1.4 ! root 715: }
! 716:
! 717: // 命令ブレークポイントの設定
! 718: // bi <insn>[:<mask>] [<skip>]
! 719: void
! 720: Debugger::cmd_bi()
! 721: {
! 722: breakpoint_t bp;
! 723: std::string inststr;
! 724: std::string maskstr;
! 725: int instlen;
! 726: int masklen;
! 727:
! 728: if (args.size() < 2) {
! 729: cons->Print("usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.3 root 730: return;
1.1 root 731: }
732:
1.1.1.4 ! root 733: // 引数をまず分離
! 734: auto pos = args[1].find(':');
! 735: if (pos == std::string::npos) {
! 736: // マスク指定なし
! 737: inststr = args[1];
! 738: instlen = inststr.size();
! 739: masklen = -1;
! 740: } else {
! 741: // マスク指定あり
! 742: inststr = args[1].substr(0, pos);
! 743: instlen = inststr.size();
! 744: maskstr = args[1].substr(pos + 1);
! 745: masklen = maskstr.size();
! 746: }
! 747:
! 748: // 命令部チェック
! 749: if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
! 750: cons->Print("%s: invalid instruction value\n", args[1].c_str());
! 751: return;
! 752: }
! 753: if (instlen % (md->inst_bits / 4) != 0) {
! 754: cons->Print("%s: invalid instruction length\n", args[1].c_str());
! 755: return;
! 756: }
! 757:
! 758: // マスク部チェック
! 759: bp.mask = 0xffffffff;
! 760: if (masklen != -1) {
! 761: if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
! 762: cons->Print("%s: invalid mask value\n", args[1].c_str());
! 763: return;
! 764: }
! 765: if (masklen != instlen) {
! 766: cons->Print("%s: inst:mask must be the same length\n",
! 767: args[1].c_str());
1.1 root 768: return;
769: }
770: }
1.1.1.4 ! root 771: // 8バイト未満なら左詰め。
! 772: if (md->inst_bits < 32 && instlen < 8) {
! 773: bp.inst <<= 32 - instlen * 4;
! 774: bp.mask <<= 32 - instlen * 4;
! 775: }
! 776:
! 777: // あればスキップカウント
! 778: bp.skip = 0;
! 779: if (args.size() > 2) {
! 780: bp.skip = atoi(args[2].c_str());
! 781: }
! 782:
! 783: // 空いてるところにセット
! 784: bp.type = BreakpointType::Instruction;
! 785: int bi = AddBreakpoint(bp);
! 786: if (bi == -1) {
! 787: cons->Print("no free breakpoints\n");
! 788: } else {
! 789: cons->Print("breakpoint #%d added\n", bi);
! 790: }
! 791:
! 792: // 今登録されている命令ブレークの必要命令長を再計算
! 793: RecalcInstMask();
! 794: }
! 795:
! 796: // 例外ブレークポイントの設定
! 797: // bv <vector_number>[-<end_number>] [<skip>]
! 798: void
! 799: Debugger::cmd_bv()
! 800: {
! 801: breakpoint_t bp;
! 802:
! 803: if (args.size() < 2) {
! 804: cons->Print("usage: be <vec#>[-<end#>] [<skipcount>]\n");
! 805: return;
! 806: }
! 807:
! 808: auto pos = args[1].find('-');
! 809: if (pos == std::string::npos) {
! 810: // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
! 811: if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) {
! 812: cons->Print("%s: invalid vector number\n", args[1].c_str());
! 813: return;
! 814: }
! 815: bp.vec2 = bp.vec1;
! 816: } else {
! 817: // ベクタ番号(範囲指定 [vec1, vec2])
! 818: std::string str1 = args[1].substr(0, pos);
! 819: std::string str2 = args[1].substr(pos + 1);
! 820:
! 821: if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) {
! 822: cons->Print("%s: invalid first vector number\n", args[1].c_str());
! 823: return;
! 824: }
! 825: if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) {
! 826: cons->Print("%s: invalid last vector number\n", args[1].c_str());
! 827: return;
! 828: }
! 829: }
! 830:
! 831: // 範囲チェック
! 832: if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) {
! 833: cons->Print("$%x: invalid vector number\n", bp.vec1);
! 834: return;
! 835: }
! 836: if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) {
! 837: cons->Print("$%x: invalid last vector number\n", bp.vec2);
! 838: return;
! 839: }
! 840:
! 841: // 大小が逆なら入れ替える?
! 842: if (bp.vec1 > bp.vec2) {
! 843: int tmp;
! 844: tmp = bp.vec1;
! 845: bp.vec1 = bp.vec2;
! 846: bp.vec2 = tmp;
! 847: }
! 848:
! 849: // あればスキップカウント
! 850: bp.skip = 0;
! 851: if (args.size() > 2) {
! 852: bp.skip = atoi(args[2].c_str());
! 853: }
1.1 root 854:
855: // 空いてるところにセット
1.1.1.4 ! root 856: bp.type = BreakpointType::Exception;
! 857: int bi = AddBreakpoint(bp);
1.1 root 858: if (bi == -1) {
1.1.1.4 ! root 859: cons->Print("no free breakpoints\n");
1.1 root 860: } else {
1.1.1.4 ! root 861: cons->Print("breakpoint #%d added\n", bi);
1.1 root 862: }
863: }
864:
865: // ブレークポイント一覧表示
866: void
867: Debugger::cmd_b_list()
868: {
1.1.1.4 ! root 869: ShowMonitor(*gBreakpointMonitor);
! 870: }
! 871:
! 872: // ブレークポイント一覧 (モニタ)
! 873: void
! 874: Debugger::MonitorBreakpoint(TextScreen& monitor)
! 875: {
! 876: // 0 1 2 3 4 5 6
! 877: // 0123456789012345678901234567890123456789012345678901234567890
! 878: // No Type Parameter Matched Skip
! 879: // #0 addr $01234567 123456789 123456789/123456789
! 880: // #1 inst 00000000/00000000
! 881: // #2 excp $00-$00
! 882:
! 883: monitor.Clear();
! 884: monitor.Print(0, 0, "No Type Parameter");
! 885: monitor.Print(26, 0, "Matched");
! 886: monitor.Print(37, 0, "Skip");
1.1 root 887:
1.1.1.3 root 888: for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 ! root 889: const auto& bp = bpoint[i];
! 890: int y = i + 1;
! 891:
! 892: monitor.Print(0, y, "#%d", i);
! 893:
! 894: // 種別ごとの表示
! 895: switch (bp.type) {
! 896: case BreakpointType::Unused:
! 897: continue;
! 898:
! 899: case BreakpointType::Address:
! 900: monitor.Print(3, y, "addr $%08x", bp.addr);
! 901: break;
! 902: case BreakpointType::Memory:
! 903: monitor.Print(3, y, "mem $%08x", bp.addr);
! 904: break;
! 905:
! 906: case BreakpointType::Exception:
! 907: monitor.Print(3, y, "excp $%02x", bp.vec1);
! 908: if (bp.vec2 != bp.vec1) {
! 909: monitor.Print(11, y, "-$%02x", bp.vec2);
! 910: }
! 911: break;
! 912:
! 913: case BreakpointType::Instruction:
! 914: monitor.Print(3, y, "inst");
! 915: if (md->inst_bits == 32) {
! 916: if (bp.mask == 0xffffffff) {
! 917: monitor.Print(8, y, "%08x", bp.inst);
! 918: } else {
! 919: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
! 920: }
! 921: } else {
! 922: if (bp.mask == 0xffffffff) {
! 923: monitor.Print(8, y, "%08x", bp.inst);
! 924: } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) {
! 925: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
! 926: } else if (bp.mask == 0xffff0000) {
! 927: monitor.Print(8, y, "%04x", bp.inst >> 16);
! 928: } else {
! 929: monitor.Print(8, y, "%04x:%04x",
! 930: bp.inst >> 16, bp.mask >> 16);
! 931: }
! 932: }
! 933: break;
! 934:
! 935: default:
! 936: monitor.Print(3, y, "type=%d", (int)bp.type);
! 937: continue;
! 938: }
! 939:
! 940: // マッチ回数
! 941: monitor.Print(26, y, "%d", bp.matched);
! 942:
! 943: // スキップ
! 944: if (bp.skip < 0) {
! 945: monitor.Print(37, y, "forever");
! 946: } else if (bp.skip > 0) {
! 947: monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1 root 948: }
949: }
950: }
951:
952: // ブレークポイント全削除
953: void
954: Debugger::cmd_bx()
955: {
1.1.1.3 root 956: for (auto& bp : bpoint) {
1.1.1.4 ! root 957: bp.type = BreakpointType::Unused;
1.1 root 958: }
959: cons->Print(" All breakpoints disabled\n");
1.1.1.4 ! root 960:
! 961: // 今登録されている命令ブレークの必要命令長を再計算
! 962: RecalcInstMask();
1.1 root 963: }
964:
965: // ブレークポイントを設定。
1.1.1.4 ! root 966: // new_bp のうち matched, skipremain はこちらで初期化する。
! 967: // それ以外を埋めてから呼ぶこと。
! 968: // 設定できればその番号、できなければ -1 を返す。
1.1 root 969: int
1.1.1.4 ! root 970: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1 root 971: {
1.1.1.3 root 972: for (int i = 0; i < bpoint.size(); i++) {
973: auto& bp = bpoint[i];
1.1.1.4 ! root 974: if (bp.type == BreakpointType::Unused) {
! 975: bp = new_bp;
! 976: bp.matched = 0;
! 977: if (bp.skip > 0) {
! 978: bp.skipremain = bp.skip;
! 979: } else {
! 980: bp.skipremain = 0;
! 981: }
! 982:
1.1 root 983: return i;
984: }
985: }
986: return -1;
987: }
988:
1.1.1.4 ! root 989: // すべての命令ブレークのマスクのうち最長のものを再計算する。
! 990: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
! 991: void
! 992: Debugger::RecalcInstMask()
! 993: {
! 994: uint32 mask;
! 995: bi_need_bytes = 0;
! 996:
! 997: // 登録されている命令ブレークの最長マスクを求める
! 998: mask = 0;
! 999: for (const auto& bp : bpoint) {
! 1000: if (bp.type == BreakpointType::Instruction) {
! 1001: mask |= bp.mask;
! 1002: }
! 1003: }
! 1004:
! 1005: // mask の Number of Trailing Zero を求める。
! 1006: // (x & -x) で x の最も下の立ってるビットだけを立てる、
! 1007: // (x & -x) -1 でそれより下の全ビットを立てる、
! 1008: // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
! 1009: int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
! 1010: // 上位側から数えたマスクに必要なビット数
! 1011: int mlen = 32 - ntz;
! 1012: // 命令語単位に切り上げる
! 1013: mlen = roundup(mlen, md->inst_bits);
! 1014: // バイト数に変換
! 1015: mlen /= 8;
! 1016:
! 1017: if (mlen > bi_need_bytes) {
! 1018: bi_need_bytes = mlen;
! 1019: }
! 1020: }
! 1021:
! 1022: // ブランチ履歴、例外履歴表示
! 1023: // brhist [<maxlines>]
! 1024: // exhist [<maxlines>]
! 1025: // <maxlines> は表示する最大エントリ数。
! 1026: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
! 1027: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
! 1028: // する。
1.1 root 1029: void
1030: Debugger::cmd_brhist()
1031: {
1.1.1.4 ! root 1032: cmd_hist_common(md->GetBrHist(), 0);
! 1033: }
! 1034: void
! 1035: Debugger::cmd_exhist()
! 1036: {
! 1037: cmd_hist_common(md->GetExHist(), BranchHistory::ExHist);
! 1038: }
1.1.1.3 root 1039:
1.1.1.4 ! root 1040: // ブランチ履歴、例外履歴表示の共通部分。
! 1041: void
! 1042: Debugger::cmd_hist_common(BranchHistory& hist, uint64 flag)
! 1043: {
! 1044: // 表示最大行数(と向き)
! 1045: // 向きは bottom_to_top = true が新しいほうを下とする方向。
! 1046: int maxlines = 20;
! 1047: bool bottom_to_top = true;
1.1.1.3 root 1048: if (args.size() > 1) {
1.1.1.4 ! root 1049: maxlines = atoi(args[1].c_str());
! 1050: if (maxlines < 0) {
! 1051: bottom_to_top = false;
! 1052: maxlines = -maxlines;
1.1.1.3 root 1053: }
1.1.1.4 ! root 1054:
! 1055: if (maxlines < 1)
! 1056: maxlines = 1;
! 1057: if (maxlines > 256)
! 1058: maxlines = 256;
! 1059: }
! 1060:
! 1061: // コンソールでは空エントリの行は表示したくないので、
! 1062: // 先にエントリ数を調べる。
! 1063: int used = hist.GetUsed();
! 1064:
! 1065: // 表示行数 + 1行はヘッダ分
! 1066: int lines = std::min(used, maxlines) + 1;
! 1067:
! 1068: // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
! 1069: auto size = hist.GetMonitorSize();
! 1070: TextScreen tscr;
! 1071: tscr.Init(size.width, lines);
! 1072:
! 1073: // コンソールでは下が新しいの順のほうがいい
! 1074: if (bottom_to_top) {
! 1075: flag |= BranchHistory::BottomToTop;
1.1.1.3 root 1076: }
1.1.1.4 ! root 1077: tscr.userdata = flag;
! 1078:
! 1079: // 表示
! 1080: hist.MonitorUpdate(tscr);
! 1081: ShowTextScreen(tscr);
1.1 root 1082: }
1083:
1084: // 実行再開(continue): c [<addr>]
1085: // <addr> 指定があれば <addr> まで実行。
1086: void
1087: Debugger::cmd_c()
1088: {
1.1.1.3 root 1089: if (args.size() > 1) {
1.1 root 1090: // 引数があれば
1.1.1.3 root 1091: if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1 root 1092: return;
1093: }
1094: // 偶数番地に丸める
1095: bc_addr &= 0xfffffffe;
1096:
1097: bc_enable = true;
1098: }
1099: }
1100:
1101: // 引数などからアドレスを取得する共通ルーチン。
1.1.1.3 root 1102: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1 root 1103: // なければ、lastaddr が指定されていればそれを使う。
1.1.1.2 root 1104: // それもなければ現在の PC を使う。
1.1 root 1105: bool
1106: Debugger::GetAddr(uint32& addr, uint32& lastaddr)
1107: {
1.1.1.3 root 1108: if (args.size() > 1) {
1.1 root 1109: // 1つ目の引数があれば、開始アドレス
1.1.1.3 root 1110: if (ParseAddr(args[1].c_str(), &addr) == false) {
1.1 root 1111: return false;
1112: }
1113: // 偶数番地からに丸める
1114: addr &= 0xfffffffe;
1115: } else if (lastaddr != 0xffffffff) {
1116: // 引数なしで前回値があれば前回の続き
1117: addr = lastaddr;
1118: } else {
1119: // そうでなければ、今の PC
1.1.1.2 root 1120: addr = md->GetPC();
1.1 root 1121: }
1122: return true;
1123: }
1124:
1125: // 逆アセンブル: d [<addr> [<cnt>]] (論理、テーブルサーチなし)
1126: void
1127: Debugger::cmd_d()
1128: {
1129: cmd_d_common(MemdumpMode::Logical_atc);
1130: }
1131:
1132: // 逆アセンブル: dt [<addr>] [<cnt>]] (論理、テーブルサーチあり)
1133: void
1134: Debugger::cmd_dt()
1135: {
1136: cmd_d_common(MemdumpMode::Logical_search);
1137: }
1138:
1139: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
1140: void
1141: Debugger::cmd_D()
1142: {
1143: cmd_d_common(MemdumpMode::Physical);
1144: }
1145:
1146: // 逆アセンブル共通
1147: void
1148: Debugger::cmd_d_common(MemdumpMode mode)
1149: {
1150: uint32 addr;
1.1.1.2 root 1151: saddr_t saddr;
1.1 root 1152: int cnt;
1153:
1154: cnt = 10;
1155:
1156: // 開始アドレス
1157: if (GetAddr(addr, d_last_addr) == false) {
1158: return;
1159: }
1160: // 2つ目の引数があれば行数
1.1.1.3 root 1161: if (args.size() > 2) {
1162: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1163: }
1164:
1.1.1.2 root 1165: saddr.addr = addr;
1166: saddr.super = md->IsSuper();
1167:
1168: // XXX そのうちなんとかする
1169: if (mode == MemdumpMode::Logical_search) {
1170: saddr.logical = true;
1171: saddr.search = true;
1172: } else if (mode == MemdumpMode::Logical_atc) {
1173: saddr.logical = true;
1174: }
1175:
1.1 root 1176: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1177: std::string addrbuf;
1178: std::string mnemonic;
1179: std::string mnembuf;
1180: std::vector<uint8> local_ir;
1181:
1182: if (FormatDumpAddr(saddr, addrbuf)) {
1183: md->Disassemble(saddr, mnemonic, local_ir);
1184: // オフライン用に整形
1185: mnembuf = md->FormatDisasm(mnemonic, local_ir);
1.1 root 1186: }
1.1.1.2 root 1187: // アドレス変換でバスエラーが起きてもここは表示する
1188: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1189:
1190: // 変換できなければここで終了
1191: if (local_ir.size() == 0)
1192: break;
1193: saddr.addr += local_ir.size();
1.1 root 1194: }
1.1.1.2 root 1195:
1196: d_last_addr = saddr.addr;
1.1 root 1197: }
1198:
1199: // ログレベル設定: L <name> <level>
1200: void
1201: Debugger::cmd_L()
1202: {
1203: const char *name;
1204: int level;
1205:
1.1.1.3 root 1206: if (args.size() < 3) {
1.1 root 1207: cons->Print("L <name> <level>\n");
1208: return;
1209: }
1210:
1.1.1.3 root 1211: name = args[1].c_str();
1212: level = atoi(args[2].c_str());
1.1 root 1213:
1214: // 短縮形とかエイリアスとか
1215: // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい
1216: if (strcmp(name, "sch") == 0)
1217: name = "scheduler";
1218: if (strcmp(name, "scc") == 0)
1219: name = "sio";
1220:
1.1.1.3 root 1221: // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1 root 1222: // all はどうするか
1223: std::string sname = name;
1224: bool found = false;
1225: for (auto& obj : gObjects) {
1226: if (obj->logname == sname) {
1227: obj->loglevel = level;
1228: cons->Print("%s=%d\n", name, obj->loglevel);
1229: found = true;
1230: }
1231: }
1232: // 見付からない場合はエラー
1233: if (!found) {
1234: cons->Print("%s: invalid logname\n", name);
1235: }
1236: }
1237:
1238: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
1239: void
1240: Debugger::cmd_m()
1241: {
1242: cmd_m_common(MemdumpMode::Logical_atc);
1243: }
1244:
1245: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
1246: void
1247: Debugger::cmd_mt()
1248: {
1249: cmd_m_common(MemdumpMode::Logical_search);
1250: }
1251:
1252: // メモリダンプ: M [<addr> [<cnt>]] (物理)
1253: void
1254: Debugger::cmd_M()
1255: {
1256: cmd_m_common(MemdumpMode::Physical);
1257: }
1258:
1259: // メモリダンプ共通
1260: void
1261: Debugger::cmd_m_common(MemdumpMode mode)
1262: {
1263: uint32 addr;
1.1.1.2 root 1264: saddr_t saddr;
1.1 root 1265: int cnt;
1266:
1267: cnt = 10;
1268:
1269: // 開始アドレス
1270: if (GetAddr(addr, m_last_addr) == false) {
1271: return;
1272: }
1273: // 2つ目の引数があれば行数
1.1.1.3 root 1274: if (args.size() > 2) {
1275: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1276: }
1277:
1.1.1.2 root 1278: saddr.addr = addr;
1279: saddr.super = md->IsSuper();
1280: // XXX
1281: if (mode == MemdumpMode::Logical_search) {
1282: saddr.logical = true;
1283: saddr.search = true;
1284: } else if (mode == MemdumpMode::Logical_atc) {
1285: saddr.logical = true;
1286: }
1287:
1.1 root 1288: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1289: std::string addrbuf;
1290: std::string dumpbuf;
1291: std::string charbuf;
1292:
1293: if (FormatDumpAddr(saddr, addrbuf) == false) {
1294: // アドレス変換でバスエラーならダンプもくそもない
1295: cons->Print("%s\n", addrbuf.c_str());
1296: // ここで終わる?
1297: // XXX 16バイト先を試してみるとか?
1298: break;
1299: }
1300:
1301: for (int j = 0; j < 8; j++) {
1302: uint64 paddr;
1303: // もう一回アドレス変換するのやや冗長だが仕方ない
1304: if (saddr.IsLogical() && md->MMUEnabled()) {
1305: // アドレス変換
1306: paddr = md->TranslateAddr(saddr);
1307: if ((int64)paddr < 0) {
1308: // バスエラーが起きたら改行して仕切り直し
1309: break;
1310: }
1311: } else {
1312: paddr = saddr.addr;
1313: }
1314:
1315: for (int k = 0; k < 2; k++) {
1316: uint64 b = vm_phys_peek_8(paddr + k);
1317:
1318: if ((int64)b < 0) {
1319: dumpbuf += "--";
1320: charbuf += " ";
1321: } else {
1322: uint32 c = (uint32)b;
1323: dumpbuf += string_format("%02x", c);
1324: charbuf += string_format("%c",
1325: (0x20 <= c && c <= 0x7e) ? c : '.');
1326: }
1327: }
1328: dumpbuf += " ";
1329: saddr.addr += 2;
1330: }
1331: cons->Print("%-18s %-40s %s\n",
1332: addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str());
1.1 root 1333: }
1.1.1.2 root 1334: m_last_addr = saddr.addr;
1.1 root 1335: }
1336:
1337: // プロンプトに来た最初に表示するいつものやつを再表示する
1338: void
1339: Debugger::cmd_minus()
1340: {
1.1.1.3 root 1341: // 通常レジスタを表示
1342: std::vector<std::string> tmpargs {{ "r" }};
1343: md->ShowRegister(cons, tmpargs);
1.1 root 1344:
1345: // 論理アクセス時、ATC ミスが分かったほうが便利だと思う
1.1.1.2 root 1346: saddr_t laddr;
1347: laddr.addr = pc;
1348: laddr.super = md->IsSuper();
1349: laddr.logical = true;
1350: laddr.search = false;
1351:
1352: // 現在の PC の位置を逆アセンブル。ここで ir を更新。
1353: std::string addrbuf;
1354: std::string mnemonic;
1355: std::string mnembuf;
1356: ir.clear();
1357: // アドレスを表示用に
1358: if (FormatDumpAddr(laddr, addrbuf)) {
1359: md->Disassemble(laddr, mnemonic, ir);
1360: // ライブ表示用に整形
1361: mnembuf = md->FormatDisasmLive(mnemonic, ir);
1362: }
1363: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1364:
1365: // XXX m88k?
1366: nextpc = pc + ir.size();
1.1 root 1367: }
1368:
1369: // ステップ実行: n[<count>?=1] (サブルーチンを飛ばす)
1370: void
1371: Debugger::cmd_n()
1372: {
1.1.1.3 root 1373: if (args.size() > 1) {
1.1 root 1374: int count;
1.1.1.3 root 1375: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1376: if (count < 1) {
1377: cons->Print(" invalid step count: %d\n", count);
1378: return;
1379: }
1380:
1381: n_count = count;
1382: } else {
1383: n_count = 1;
1384: }
1385: n_enable = true;
1386: SetNBreakpoint();
1387: }
1388:
1389: // n コマンド用のサブルーチンを飛ばすブレークポイントを設定。
1390: // 戻り値は、プロンプトをスキップするなら true。
1391: bool
1392: Debugger::SetNBreakpoint()
1393: {
1394: // n コマンドは、通常はブレークポイントを次の命令に仕掛けながら
1395: // 実行するが、いくつかの命令ではブレークポイントではなく、
1396: // 命令トレース (t コマンド) のように動作させる。
1397: // トレース動作する命令は以下の通り。
1398: // Bcc, BRA
1399: // JMP
1400: // RTD, RTR, RTS
1401: // RTE
1402: // FBcc
1403: // FDBcc (2ワード目無視)
1404: // (RTM は 68020 専用なので対応しない)
1405: #define IsBcc(x) ((((x) & 0xf000) == 0x6000) && (((x) & 0xff00) != 0x6100))
1406: #define IsJMP(x) (((x) & 0xffc0) == 0x4ec0)
1407: #define IsRTD(x) (((x) & 0xffff) == 0x4e74)
1408: #define IsRTE(x) (((x) & 0xffff) == 0x4e73)
1409: #define IsRTR(x) (((x) & 0xffff) == 0x4e77)
1410: #define IsRTS(x) (((x) & 0xffff) == 0x4e75)
1411: #define IsFBcc(x) (((x) & 0xf180) == 0xf080)
1412: #define IsFDBcc(x) (((x) & 0xf1f8) == 0xf048)
1413:
1414: uint16 op;
1415:
1416: // XXX この辺統一的にしたほうがいい
1417: // XXX logical peek のような気がする
1.1.1.2 root 1418: op = (((uint16)vm_phys_peek_8(pc)) << 8)
1419: | vm_phys_peek_8(pc + 1);
1.1 root 1420:
1421: if (IsBcc(op)
1422: || IsJMP(op)
1423: || IsRTD(op)
1424: || IsRTE(op)
1425: || IsRTR(op)
1426: || IsRTS(op)
1427: || IsFBcc(op)
1428: || IsFDBcc(op)
1429: ) {
1430: n_breakenable = false;
1431: s_enable = true;
1432: s_count = 1;
1433: return false;
1434: } else {
1435: n_breakenable = true;
1436: n_breakaddr = nextpc;
1437: return true;
1438: }
1439: }
1440:
1.1.1.2 root 1441: // デバッガ終了コマンド
1442: void
1443: Debugger::cmd_q()
1444: {
1445: cons->Print("quit debugger console.\n");
1.1 root 1446: }
1447:
1448: // モニター表示
1449: void
1450: Debugger::cmd_show()
1451: {
1.1.1.3 root 1452: if (args.size() != 2) {
1.1 root 1453: cons->Print("usage: show <monitor>\n");
1454: for (auto& d : gObjects) {
1455: if (d->logname != "?") {
1456: cons->Print(" %s\n", d->logname.c_str());
1457: }
1458: }
1459: return;
1460: }
1461:
1.1.1.3 root 1462: std::string name = args[1];
1.1 root 1463: for (auto& d : gObjects) {
1464: if (d->logname == name) {
1.1.1.3 root 1465: ShowMonitor(*d);
1.1 root 1466: return;
1467: }
1468: }
1469: // 一致しない
1470: cons->Print("show: no such objects\n");
1471: }
1472:
1.1.1.3 root 1473: // モニターを更新して表示。
1474: void
1.1.1.4 ! root 1475: Debugger::ShowMonitor(IMonitor& monitor)
1.1.1.3 root 1476: {
1477: // モニタを更新
1.1.1.4 ! root 1478: TextScreen ts;
! 1479:
! 1480: auto size = monitor.GetMonitorSize();
! 1481: ts.Init(size.width, size.height);
! 1482:
! 1483: monitor.MonitorUpdate(ts);
! 1484: ShowTextScreen(ts);
1.1.1.3 root 1485: }
1486:
1487: // テキストスクリーンを表示。
1.1 root 1488: void
1.1.1.3 root 1489: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 1490: {
1491: char sbuf[1024]; // 適当
1492:
1493: // monitor 内部バッファから ShiftJIS バッファを作成。
1494: // その際属性もエスケープシーケンスで再現する。
1495: int col = monitor.GetCol();
1496: int row = monitor.GetRow();
1497: const uint16 *src = monitor.GetBuf();
1498:
1499: for (int y = 0; y < row; y++) {
1500: int sy = y * col;
1501: int sx;
1502: uint attr;
1503: char *d;
1504:
1505: memset(sbuf, 0, sizeof(sbuf));
1506: d = sbuf;
1507: attr = TA::Normal;
1508: for (sx = 0; sx < col; sx++) {
1509: uint a = src[sy + sx] & 0xff00;
1510: uint ch = src[sy + sx] & 0x00ff;
1511:
1512: // 属性の変わり目
1513: if (attr != a) {
1514: switch (a) {
1515: case TA::Normal:
1516: case TA::Off:
1517: *d++ = 0x1b;
1518: *d++ = '[';
1519: *d++ = 'm';
1520: break;
1521: case TA::On:
1522: *d++ = 0x1b;
1523: *d++ = '[';
1524: *d++ = '7';
1525: *d++ = 'm';
1526: break;
1527: case TA::Disable:
1528: *d++ = 0x1b;
1529: *d++ = '[';
1530: *d++ = '2';
1531: *d++ = 'm';
1532: break;
1533: case TA::Em:
1534: *d++ = 0x1b;
1535: *d++ = '[';
1536: *d++ = '1';
1537: *d++ = 'm';
1538: break;
1539: }
1540: attr = a;
1541: }
1542: *d++ = ch;
1543: }
1.1.1.3 root 1544: // 行の終わりで一旦属性を戻しておく
1545: if (attr != TA::Normal) {
1546: *d++ = 0x1b;
1547: *d++ = '[';
1548: *d++ = 'm';
1549: }
1.1 root 1550: *d = '\0';
1551:
1552: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
1553:
1554: cons->Print("%s\n", sbuf);
1555: }
1556: }
1557:
1558: // ステップ実行: s[<count>?:1]
1.1.1.4 ! root 1559: // 途中レジスタを表示せずに <count> 命令実行する
1.1 root 1560: void
1561: Debugger::cmd_s()
1562: {
1.1.1.3 root 1563: if (args.size() > 1) {
1.1 root 1564: int count;
1.1.1.3 root 1565: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1566: if (count < 1) {
1567: cons->Print(" invalid step count: %d\n", count);
1568: return;
1569: }
1570:
1571: s_count = count;
1572: } else {
1573: s_count = 1;
1574: }
1575: s_enable = true;
1576: }
1577:
1578: // ステップアウト
1579: void
1580: Debugger::cmd_so()
1581: {
1582: so_enable = true;
1.1.1.2 root 1583: md->SetStepOut();
1.1 root 1584: }
1585:
1586: // トレース実行: t[<count>?:1]
1.1.1.4 ! root 1587: // レジスタを表示しながら <count> 命令実行する
1.1 root 1588: void
1589: Debugger::cmd_t()
1590: {
1.1.1.3 root 1591: if (args.size() > 1) {
1.1 root 1592: int count;
1.1.1.3 root 1593: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1594: if (count < 1) {
1595: cons->Print(" invalid trace count: %d\n", count);
1596: return;
1597: }
1598: t_count = count;
1599: } else {
1600: t_count = 1;
1601: }
1602: t_enable = true;
1603: }
1604:
1.1.1.4 ! root 1605: // 引数で示されるプレフィックスなしの16進数値を返す。
! 1606: // 正しく変換できれば値を valp に格納して true を返す。
! 1607: // そうでなければ false を返す。
! 1608: bool
! 1609: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
! 1610: {
! 1611: uint32 val;
! 1612: char *end;
! 1613:
! 1614: errno = 0;
! 1615: val = strtoul(arg, &end, 16);
! 1616: if (arg[0] == '\0' || end[0] != '\0') {
! 1617: return false;
! 1618: }
! 1619: if (errno == ERANGE) {
! 1620: return false;
! 1621: }
! 1622:
! 1623: *valp = val;
! 1624: return true;
! 1625: }
! 1626:
1.1 root 1627: // 引数で示されるアドレスを返す。
1628: // '%' で始まればレジスタ。
1629: // そうでなければ16進数値。
1630: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
1631: // そうでなければエラーメッセージを表示して偽を返す。
1632: bool
1.1.1.4 ! root 1633: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1 root 1634: {
1.1.1.4 ! root 1635: uint32 addr;
1.1 root 1636: char *end;
1637:
1638: if (arg[0] == '%') {
1.1.1.2 root 1639: // '%' から始まればレジスタ
1640: uint64 r;
1641: r = md->GetRegAddr(arg + 1);
1642: if ((int64)r < 0) {
1643: #if 0 // not yet
1.1 root 1644: cons->Print("valid register name are: %s%s\n",
1645: "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
1646: ",%msp,%vbr,%srp,%crp");
1.1.1.2 root 1647: #endif
1648: cons->Print("invalid register name\n");
1.1 root 1649: return false;
1650: }
1.1.1.2 root 1651: addr = (uint32)r;
1.1 root 1652: } else {
1653: /* そうでなければ番地指定 */
1654: errno = 0;
1655: addr = strtoul(arg, &end, 16);
1656: if (arg[0] == '\0' || end[0] != '\0') {
1657: cons->Print("invalid address: '%s'\n", arg);
1658: return false;
1659: }
1660: if (errno == ERANGE) {
1661: cons->Print("out of range: %s\n", arg);
1662: return false;
1663: }
1664: }
1665:
1666: *addrp = addr;
1667: return true;
1668: }
1669:
1.1.1.2 root 1670: // (論理、物理)アドレスを表示用に整形。
1671: // 逆アセンブル表示、メモリダンプ表示等で共通。
1672: // アドレス変換でバスエラーが起きたら false を返す (この場合も str は有効)。
1673: // 末尾を空白パディングはしていないので必要なら "%-18s" 等して使うこと。
1674: // 123456789012345678
1675: // "00112233:44556677:"
1.1 root 1676: bool
1.1.1.2 root 1677: Debugger::FormatDumpAddr(saddr_t laddr, std::string& str)
1.1 root 1678: {
1.1.1.2 root 1679: bool rv = true;
1.1 root 1680:
1.1.1.2 root 1681: // laddr は常に表示
1682: str = string_format("%08x", laddr.addr);
1.1 root 1683:
1.1.1.2 root 1684: if (laddr.IsLogical() && md->MMUEnabled()) {
1685: // アドレス変換してみる
1686: uint64 paddr = md->TranslateAddr(laddr);
1687: if ((int64)paddr < 0) {
1688: // MMU 時点でバスエラー
1689: str += ":BusErr";
1690: rv = false;
1691: } else if (paddr == laddr.addr) {
1692: // PA==VA
1693: str += "=PA";
1.1 root 1694: } else {
1.1.1.2 root 1695: // PA!=VA
1696: str += string_format(":%08x:", (uint32)paddr);
1.1 root 1697: }
1.1.1.2 root 1698: } else {
1699: // 物理アクセス
1700: str += ":";
1.1 root 1701: }
1702:
1.1.1.2 root 1703: return rv;
1.1 root 1704: }
1.1.1.4 ! root 1705:
! 1706: //
! 1707: // ブレークポイントモニター
! 1708: //
! 1709:
! 1710: // コンストラクタ
! 1711: BreakpointMonitor::BreakpointMonitor()
! 1712: {
! 1713: monitor_size = nnSize(55, 9);
! 1714: }
! 1715:
! 1716: void
! 1717: BreakpointMonitor::MonitorUpdate(TextScreen& monitor)
! 1718: {
! 1719: gDebugger->MonitorBreakpoint(monitor);
! 1720: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.