|
|
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:
1.1.1.5 root 116: // disp_regs の初期値設定。
117: // 再接続でも継続してていいような気がするのでループ外で初期化。
118: disp_regs.clear();
119: disp_regs.push_back("r");
120:
1.1 root 121: for (;;) {
1.1.1.3 root 122: // 着信待ち
123: if (Accept() == false) {
124: break;
125: }
126:
1.1.1.2 root 127: cons->InitEditLine("> ");
1.1 root 128:
1.1.1.3 root 129: // デバッガプロンプトにいる間はトレースオン
130: md->ReqSet(CPU_REQ_TRACE);
131:
132: bool first = true;
133: for (;;) {
134: // 接続後の1回目だけ実行するもの。
135: if (first) {
136: first = false;
137:
138: // greeting はプロンプトが取れる前にもう表示したい。
139: // 何らかの事故でプロンプトが取れなくても、ここまでは接続
140: // できてることが分かるように。
141: cons->Print("This is debugger console\n");
142:
143: // 接続ごとに初期化する値
144: n_enable = false;
145: s_enable = false;
1.1.1.5 root 146: t_enable = true;
1.1.1.3 root 147: }
148:
149: // プロンプトが取れるのを待つ
150: if (AcquirePrompt() == false) {
151: break;
152: }
153:
1.1.1.6 ! root 154: // d/m をいきなり引数なしで実行した時のため、現在地にしておく。
1.1.1.3 root 155: pc = md->GetPC();
1.1.1.6 ! root 156: m_last_addr.Set(pc, md->IsSuper(), true);
! 157: d_last_addr.Set(pc, md->IsSuper(), false);
! 158: // プロンプトに来た時に表示するいつものやつ
1.1.1.3 root 159: cmd_minus();
160:
161: // コマンド処理のメインループ
162: auto action = MainLoop();
163:
164: // 次回との差分のため、今のレジスタセットをバックアップ
165: md->BackupRegs();
166:
167: // メインループから戻ったということは Leave か Quit なので
168: // どちらにしても、ここでプロンプトを手放す。
169: gCVPrompt->NotifyRelease();
170:
171: // quit ならここでループを1段抜ける
172: if (action == CommandAction::Quit) {
173: break;
174: }
175: }
176:
177: // デバッガを抜けるのでトレースオフ
1.1.1.2 root 178: md->ReqClr(CPU_REQ_TRACE);
1.1 root 179:
180: cons->Close();
1.1.1.3 root 181: if (gMainApp.debug_on_console) {
182: break;
183: }
1.1 root 184: }
185:
186: delete cons;
187: }
188:
1.1.1.3 root 189: // コンソールに接続されるのを待つ。
190: // うーん、なんだこれ。
191: //
192: // +--- debug_on_console (-D オプション)
193: // | +- debug_on_start (-d オプション)
194: // | | 動作
195: // 0 0 TCP、Accept で待機。
196: // 0 1 TCP、Accpet で待機。
197: // 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。
198: // 1 1 stdio、起動時にプロンプトで止まる。
1.1 root 199: bool
1.1.1.3 root 200: Debugger::Accept()
201: {
202: if (gMainApp.debug_on_console == false) {
203: // TCP なら accept が成功するのを待つ
204: cons->Accept();
205: } else {
206: if (gMainApp.debug_on_start == false) {
207: // stdio、起動時にプロンプトで止まらない。
208: // この場合キー入力かブレークポイントなどで止まった時点で
209: // プロンプトを出したいのでちょっと面倒なことに。
210: for (;;) {
211: bool is_prompt = gCVPrompt->WaitAcquire(200);
212: if (is_prompt) {
213: break;
214: }
215:
216: int r = cons->Poll(200);
217: if (r < 0) {
218: cmd_q();
219: return false;
220: }
221: if (r > 0) {
222: break;
223: }
224: }
225: } else {
226: // stdio、起動時にプロンプトで止まる。
227: // この場合は無条件に成功でよい。
228: }
229: }
230:
231: return true;
232: }
233:
234: // プロンプトが取れるのを待つ
235: // 失敗すれば false を返す
236: bool
237: Debugger::AcquirePrompt()
1.1 root 238: {
1.1.1.3 root 239: md->ReqSet(CPU_REQ_PROMPT);
240:
1.1.1.2 root 241: for (;;) {
242: bool is_prompt = gCVPrompt->WaitAcquire(200);
1.1.1.3 root 243: if (is_prompt)
244: break;
1.1.1.2 root 245:
246: // コンソールを定期観測して、入力があればブレーク要求
247: if (cons->Poll()) {
248: // この入力は drop してみる
249: if (cons->Gets(cmdbuf) == false) {
250: // EOF or Error
251: cmd_q();
252: return false;
253: }
254: md->ReqSet(CPU_REQ_PROMPT);
255: }
256: }
1.1.1.3 root 257: return true;
258: }
1.1 root 259:
1.1.1.3 root 260: // コマンド処理のメインループ。
261: // コマンドが Stay ならこの中で処理を継続。
262: // コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。
263: Debugger::CommandAction
264: Debugger::MainLoop()
265: {
266: for (;;) {
267: cons->Prompt();
268: if (cons->Gets(cmdbuf) == false) {
269: // EOF or Error
270: return CommandAction::Quit;
271: }
1.1 root 272:
1.1.1.3 root 273: string_rtrim(cmdbuf);
1.1 root 274:
1.1.1.3 root 275: if (!cmdbuf.empty()) {
276: // コマンドが入力されれば次回のために保存
277: last_cmdbuf = cmdbuf;
278: } else {
279: // 空行が入力されれば直前のコマンドをもう一度
280: // 前行がなければ何もせずもう一度プロンプトを表示するかね
281: if (last_cmdbuf.empty())
282: continue;
283: cmdbuf = last_cmdbuf;
284: }
285:
286: // 行を args に分解
287: ParseCmdbuf();
288:
289: // 前回の値が有効なのはコマンドが連続した時だけ
290:
291: // コマンドをテーブルから探す
292: auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
293: [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; });
294: if (it != cmdtable.end()) {
295: auto cmd = *it;
296:
297: // 見付かれば実行
298: (this->*(cmd.func))();
299:
300: // Stay なら引き続きコマンド処理
301: if (cmd.action == CommandAction::Stay) {
302: continue;
303: }
304: // それ以外ならメインループ終了
305: return cmd.action;
306: }
1.1 root 307:
1.1.1.3 root 308: // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
309: if (args[0][0] == 'r') {
310: // ShowRegister() は処理したら true を返す。
311: // "r" 系コマンドはすべて Stay。
312: if (md->ShowRegister(cons, args)) {
313: continue;
314: }
315: }
1.1.1.2 root 316:
1.1.1.3 root 317: // 知らないコマンドも Stay 相当。
318: cons->Print("%s: unknown command\n", args[0].c_str());
319: continue;
320: }
321: __unreachable();
1.1 root 322: }
323:
324: // ブレークポイントとかを調べる。
325: // CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。
326: bool
327: debugger_check()
328: {
329: return gDebugger->Check();
330: }
331:
332: // デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。
333: // プロンプトに降りるなら true を返す。
334: bool
335: Debugger::Check()
336: {
1.1.1.5 root 337: bool is_break = false;
338:
339: // ブレークポイントは他のチェックとは併用になるので先に調べる。
340: if (CheckAllBreakpoints()) {
341: is_break = true;
342: }
343: // アドレス指定付き continue もブレークポイントと似た動作なのでこっち。
344: // XXX ブレークポイントとして実装するかどうか
345: if (bc_enable) {
346: if (bc_addr == md->GetPC()) {
347: bc_enable = false;
348: is_break = true;
1.1 root 349: }
350: }
351:
1.1.1.5 root 352: // XXX 残りは排他動作のはず
353:
354: // いずれの場合も、ステップ実行が終了条件にマッチしたら、ブレークポイント
355: // の成否に関わらず true を返せばいい。
356: // 終了条件が来ないうちに先にブレークポイントに到達した場合でも、ブレーク
357: // ポイントによりプロンプトに降りるわけなので、実行中のステップ実行を
358: // キャンセルする。この場合もブレークポイント側が true なので true を
359: // 返せばよい。
360:
361: // t (トレース表示) が enable なら、終了条件にマッチしなくてもここで
362: // レジスタを表示。終了条件にマッチする場合に表示するとプロンプトで
363: // もう一回表示されて二重になってしまうので注意。
364:
365: if (s_enable) { // ステップ実行が..
1.1 root 366: s_count--;
1.1.1.5 root 367: if (s_count == 0) { // 成立
368: s_enable = false;
369: return true;
370: } else if (is_break) { // 非成立だがブレークが成立
1.1 root 371: s_enable = false;
372: return true;
1.1.1.5 root 373: } else if (t_enable) { // どちらも非成立で trace on
374: cmd_minus();
1.1 root 375: }
376:
1.1.1.5 root 377: } else if (so_enable) { // ステップアウトが..
378: if (md->IsStepOut()) { // 成立
1.1 root 379: so_enable = false;
380: return true;
1.1.1.5 root 381: } else if (is_break) { // 非成立だがブレークが成立
382: so_enable = false;
383: return true;
384: } else if (t_enable) { // どちらも非成立で trace on
385: cmd_minus();
1.1 root 386: }
387:
1.1.1.5 root 388: } else if (n_enable) { // 次命令まで実行が..
389: if (n_breakaddr != 0xffffffff) {
390: // ステップインをスキップ中
391: if (n_breakaddr == md->GetPC()) {
392: n_count--;
393: }
394: } else {
395: // 1命令実行
396: n_count--;
397: }
398: if (n_count == 0) { // 成立
399: n_enable = false;
400: return true;
401: } else if (is_break) { // 非成立だがブレークが成立
402: n_enable = false;
1.1 root 403: return true;
404: }
1.1.1.5 root 405: if (t_enable) { // どちらも非成立で trace on
406: cmd_minus();
407: }
408:
409: // スキップ中でなければ、ステップインが起きるか都度調べる。
410: // すでにスキップ中なら到達するまでは何もしない。
411: if (n_breakaddr == 0xffffffff) {
412: SetNBreakpoint();
413: }
1.1 root 414: }
415:
1.1.1.5 root 416: // ステップ実行系がなければブレークポイントの成否だけ
417: return is_break;
1.1 root 418: }
419:
1.1.1.4 root 420: // ブレークポイントがどれかでも成立するかを調べる。
421: // 1つ以上成立してブレークするなら true を返す。
422: // 1つも成立しておらずブレークしないなら false を返す。
1.1.1.5 root 423: // 成立すれば内容をここで表示する。
1.1.1.4 root 424: bool
425: Debugger::CheckAllBreakpoints()
426: {
427: bool is_break = false;
428:
429: // 命令ごとにクリアする
430: bi_inst = 0;
431: bi_inst_bytes = 0;
432:
433: // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが)
434: // 残りの他の条件も成立すればカウントを進める必要があるため、
435: // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。
1.1.1.5 root 436: for (int i = 0, end = bpoint.size(); i < end; i++) {
437: auto& bp = bpoint[i];
438:
1.1.1.4 root 439: switch (bp.type) {
440: case BreakpointType::Address:
441: if (bp.addr != md->GetPC()) {
442: continue;
443: }
444: break;
445:
446: case BreakpointType::Memory:
447: if (!md->CheckLEA(bp.addr)) {
448: continue;
449: }
450: break;
451:
452: case BreakpointType::Exception:
453: if (bv_vector >= 0) {
454: if (bp.vec1 <= bv_vector && bv_vector <= bp.vec2) {
455: break;
456: }
457: }
458: continue;
459:
460: case BreakpointType::Instruction:
461: if (CheckBreakpointInst(bp) == false) {
462: continue;
463: }
464: break;
465:
466: default:
467: continue;
468: }
469:
470: // 条件は成立したのでカウントとスキップ
471: bp.matched++;
472:
473: // skip == -1 は常にブレークしない (カウントするだけ)
474: if (bp.skip < 0) {
475: continue;
476: }
477:
478: // skip が 1 以上なら、remain を減算
479: if (bp.skip > 0 && --bp.skipremain > 0) {
480: continue;
481: }
482:
483: // ブレーク成立
484: is_break = true;
1.1.1.5 root 485:
486: std::string desc;
487: switch (bp.type) {
488: case BreakpointType::Address:
489: desc = string_format("addr $%08x", bp.addr);
490: break;
491: case BreakpointType::Memory:
492: desc = string_format("mem $%08x", bp.addr);
493: break;
494: case BreakpointType::Exception:
495: {
496: desc = string_format("excp $%02x", bv_vector);
497: const char *name = md->GetExceptionName(bv_vector);
498: if (name != NULL && name[0] != '\0') {
499: desc += string_format(" \"%s\"", name);
500: }
501: break;
502: }
503: case BreakpointType::Instruction:
504: desc = string_format("inst %0*x",
505: bi_inst_bytes * 2,
506: bi_inst >> ((4 - bi_inst_bytes) * 8));
507: break;
508: default:
509: assert(false);
510: break;
511: }
512: cons->Print("breakpoint #%d (%s) reached\n", i, desc.c_str());
1.1.1.4 root 513: }
514:
515: // 例外通知は通過ごとに常に下ろしておく
516: bv_vector = -1;
517:
518: // ブレークポイントが1つでも成立したかどうかを返す
519: return is_break;
520: }
521:
522: // 命令ブレークポイントが成立するか調べる。
523: bool
524: Debugger::CheckBreakpointInst(breakpoint_t& bp)
525: {
526: // uint32 bi_inst が現在の PC 位置の命令データ (左詰め)
527: // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。
528: // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。
529:
1.1.1.6 ! root 530: saddr_t laddr(md->GetPC(), md->IsSuper());
! 531: DebuggerMemoryStream mem(md, laddr);
1.1.1.5 root 532:
1.1.1.4 root 533: // 必要なバイト数に達するまで読み足す
1.1.1.5 root 534: bi_inst = 0;
1.1.1.4 root 535: while (bi_inst_bytes < bi_need_bytes) {
1.1.1.6 ! root 536: uint64 data = mem.FetchInst();
1.1.1.5 root 537: if ((int64)data < 0) {
538: return false;
1.1.1.4 root 539: }
540:
1.1.1.5 root 541: bi_inst <<= md->inst_bytes * 8;
542: bi_inst |= data;
543: bi_inst_bytes += md->inst_bytes;
1.1.1.4 root 544: }
1.1.1.5 root 545:
1.1.1.4 root 546: // 左詰めにする
547: bi_inst <<= (4 - bi_inst_bytes) * 8;
548:
549: // 必要なバイト数取得できたので比較
550: if ((bi_inst & bp.mask) == bp.inst) {
551: return true;
552: }
553: return false;
554: }
555:
556: // 例外通知 (MPU からの連絡用)
557: void
1.1.1.5 root 558: debugger_notify_exception(int vector)
1.1.1.4 root 559: {
560: gDebugger->NotifyException(vector);
561: }
562:
563: // 例外通知 (本体)
564: void
565: Debugger::NotifyException(int vector)
566: {
567: bv_vector = vector;
568: }
569:
1.1 root 570: // コマンド一覧。
1.1.1.3 root 571: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
572: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
1.1.1.4 root 573: { "bi", &Debugger::cmd_bi, Debugger::CommandAction::Stay },
574: { "bm", &Debugger::cmd_bm, Debugger::CommandAction::Stay },
575: { "bv", &Debugger::cmd_bv, Debugger::CommandAction::Stay },
1.1.1.3 root 576: { "bx", &Debugger::cmd_bx, Debugger::CommandAction::Stay },
577: { "b", &Debugger::cmd_b, Debugger::CommandAction::Stay },
578: { "brhist", &Debugger::cmd_brhist, Debugger::CommandAction::Stay },
579: { "c", &Debugger::cmd_c, Debugger::CommandAction::Leave },
580: { "d", &Debugger::cmd_d, Debugger::CommandAction::Stay },
581: { "dt", &Debugger::cmd_dt, Debugger::CommandAction::Stay },
582: { "D", &Debugger::cmd_D, Debugger::CommandAction::Stay },
1.1.1.5 root 583: { "disp", &Debugger::cmd_disp, Debugger::CommandAction::Stay },
1.1.1.4 root 584: { "exhist", &Debugger::cmd_exhist, Debugger::CommandAction::Stay },
1.1.1.5 root 585: { "hb", &Debugger::cmd_hb, Debugger::CommandAction::Stay },
586: { "hr", &Debugger::cmd_hr, Debugger::CommandAction::Stay },
1.1.1.3 root 587: { "h", &Debugger::cmd_h, Debugger::CommandAction::Stay },
588: { "help", &Debugger::cmd_h, Debugger::CommandAction::Stay },
589: { "L", &Debugger::cmd_L, Debugger::CommandAction::Stay },
590: { "m", &Debugger::cmd_m, Debugger::CommandAction::Stay },
591: { "mt", &Debugger::cmd_mt, Debugger::CommandAction::Stay },
592: { "M", &Debugger::cmd_M, Debugger::CommandAction::Stay },
593: { "n", &Debugger::cmd_n, Debugger::CommandAction::Leave },
1.1.1.5 root 594: { "nt", &Debugger::cmd_nt, Debugger::CommandAction::Leave },
1.1.1.3 root 595: { "q", &Debugger::cmd_q, Debugger::CommandAction::Quit },
596: { "quit", &Debugger::cmd_q, Debugger::CommandAction::Quit },
1.1.1.5 root 597: { "reset", &Debugger::cmd_reset, Debugger::CommandAction::Leave },
1.1.1.3 root 598: { "s", &Debugger::cmd_s, Debugger::CommandAction::Leave },
1.1.1.5 root 599: { "st", &Debugger::cmd_st, Debugger::CommandAction::Leave },
1.1.1.3 root 600: { "so", &Debugger::cmd_so, Debugger::CommandAction::Leave },
1.1.1.5 root 601: { "sot", &Debugger::cmd_sot, Debugger::CommandAction::Leave },
1.1.1.3 root 602: { "show", &Debugger::cmd_show, Debugger::CommandAction::Stay },
603: { "t", &Debugger::cmd_t, Debugger::CommandAction::Leave },
604: { "-", &Debugger::cmd_minus, Debugger::CommandAction::Stay },
1.1 root 605: };
606:
607: // ヘルプ
1.1.1.5 root 608: // h : 一覧を表示
609: // h <cmd> : 単独コマンドの詳細を表示
1.1 root 610: void
611: Debugger::cmd_h()
612: {
1.1.1.5 root 613: if (args.size() < 2) {
614: // 引数なしなら一覧表示。
1.1.1.6 ! root 615: ShowHelpList(HelpListMain);
1.1.1.5 root 616: return;
1.1.1.3 root 617: }
1.1.1.5 root 618:
619: // 引数があれば個別の詳細
620: bool try_again = false;
621: std::string cmd = args[1];
622: do {
1.1.1.6 ! root 623: // レジスタ系のヘルプを MD から取得して、ローカル変数で足す
! 624: auto details = HelpDetails;
! 625: for (const auto& dict : md->GetHelpReg()) {
! 626: details.push_back(dict);
! 627: }
1.1.1.5 root 628:
1.1.1.6 ! root 629: // そこから検索
! 630: for (const auto& dict : details) {
1.1.1.5 root 631: if (cmd == dict.first) {
632: const auto& desc = dict.second;
633: if (desc[0] == '=') {
634: // "=<cmd>" 形式なら <cmd> を探しなおす。
635: // 別名で同じヘルプを指すシンボリックリンクみたいなもの。
636: cmd = desc.substr(1);
637: try_again = true;
638: break;
639: }
640: // そうでなければこれを表示して終了
641: std::string disp = HelpConvert(dict.second);
642: cons->Print("%s", disp.c_str());
643: return;
644: }
645: }
646: } while (try_again);
647: cons->Print("invalid command name: %s\n", args[1].c_str());
1.1.1.3 root 648: }
649:
1.1.1.5 root 650: // hb : ブレークポイント系コマンドの一覧を表示
651: // こいつだけ結構占めるので別階層。
652: void
653: Debugger::cmd_hb()
654: {
1.1.1.6 ! root 655: ShowHelpList(HelpListBreakpoints);
1.1.1.5 root 656: }
657:
658: // hr : レジスタ表示系コマンドの一覧を表示
659: // CPU ごとに違うので。
660: void
661: Debugger::cmd_hr()
662: {
1.1.1.6 ! root 663: ShowHelpList(md->GetHelpListReg());
1.1.1.5 root 664: }
1.1.1.3 root 665:
1.1.1.6 ! root 666: // ヘルプ一覧を表示。
1.1.1.3 root 667: void
1.1.1.6 ! root 668: Debugger::ShowHelpList(const HelpMessages& msgs)
1.1.1.3 root 669: {
1.1.1.5 root 670: cons->Print("Type \"help <cmd>\" for indivisual details.\n");
1.1.1.3 root 671: for (const auto& pair : msgs) {
1.1.1.5 root 672: cons->Print(" %-16s %s\n", pair.first.c_str(), pair.second.c_str());
1.1 root 673: }
674: }
675:
1.1.1.5 root 676: // 個別ヘルプメッセージを出力用に置換。
677: // 1. 行頭の連続する改行 (通常は1つのはず) を取り除く。
678: // 2. 末尾の連続するタブ (通常は1つのはず) を取り除く。
679: // 3. (各行頭の) タブを空白4つに置換する。
680: std::string
681: Debugger::HelpConvert(const std::string& src)
682: {
683: int start = 0;
684: int len = src.size();
685:
686: // 末尾の連続するタブをカウント
687: while (src[len - 1] == '\t') {
688: len--;
689: }
690: // 先頭の連続する改行をカウント
691: while (src[start] == '\n') {
692: start++;
693: len--;
694: }
695: std::string stripped = src.substr(start, len);
696:
697: // 面倒なので行頭に関わらず全タブを置換
698: const char *c_stripped = stripped.c_str();
699: std::string dst;
700: for (const char *s = c_stripped; *s; s++) {
701: if (*s == '\t') {
702: dst.append(" ");
703: } else {
704: dst.append(1, *s);
705: }
706: }
707: return dst;
708: }
709:
710: /*static*/ const HelpMessages
1.1.1.6 ! root 711: Debugger::HelpListMain = {
1.1.1.5 root 712: { "b*", "Set/show Breakpoints (Type \"hb\" for details)" },
713: { "brhist", "Show branch history" },
714: { "c", "Continue" },
715: { "d/dt/D", "Disassemble" },
716: { "disp", "Set register group to show" },
717: { "exhist", "Show exception history" },
718: { "help", "Show this message" },
719: { "L", "Set log level" },
720: { "m/mt/M", "Memory dump" },
721: { "n/nt", "Step until next instruction (Skip subroutines)" },
722: { "quit", "Quit" },
723: { "r*", "Show registers (Type \"hr\" for details)" },
724: { "reset", "Reset the VM" },
725: { "s/st", "Step an instruction (Step in)" },
726: { "so", "Step out" },
727: { "show", "Show monitor" },
728: };
729:
730: /*static*/ const HelpMessages
1.1.1.6 ! root 731: Debugger::HelpListBreakpoints = {
1.1.1.5 root 732: { "b", "Show all breakpoints" },
733: { "b arg..","Set/Delete breakpoint" },
734: { "bm", "Set memory breakpoint" },
735: { "bi", "Set instruction breakpoint" },
736: { "bv", "Set exception breakpoint" },
737: { "bx", "Delete all breakpoints" },
738: };
739:
740: // 各コマンドの詳細。"コマンド名" => "説明文" の形式。
741: // 説明文は C+11 の生文字リテラル機能を使って R"**( )**" で囲む。
742: // 1つのエントリは
743: // { "cmdname", R"**(
744: // <tab>cmdname [argument...]
745: //
746: // <tab>Description...
747: // <tab>)**" },
748: // のようになる。説明文本文はインデント1つ(TAB 幅 4) で字下げした状態で
749: // 80桁以内に収めること。出力の際にタブを空白4つに置き換えることでソース上
750: // での見た目と実際の出力とを揃えてある。
751: // またソースコード上の見栄えの問題として、文字列の開始記号、終了記号を本文
752: // とは別の行に書いているため、オブジェクトとしての文字列には先頭に改行、
753: // 末尾にタブが余計に入っているが、これは表示の際にコードで取り除く。
754: //
755: // 説明文の書き方は、まずコマンド書式を1行ずつ書く。
756: // コマンド書式と本文との間は1行あける。
757: /*static*/ const HelpMessages
758: Debugger::HelpDetails = {
759: //-----
760: { "b", R"**(
761: Command: b
762: Command: b <address> [<skipcount>]
763: Command: b #n
764:
765: The first form (with no arguments) shows all breakpoints.
766: The second form sets a new breakpoint on <address>.
767: XXX skipcount
768: If <skipcount> is -1, it will never match. It's useful to count the
769: number of times you have passed this address.
770: The third form deletes a breakpoint specified by number.
771: )**" },
772:
773: //-----
774: { "bm", R"**(
775: Command: bm <address> [<skipcount>]
776:
777: Sets a memory breakpoint on <address>.
778: XXX skipcount
779: If <skipcount> is -1, it will never match. It's useful to count the
780: number of times you have passed this address.
781: )**" },
782:
783: //-----
784: { "bi", R"**(
785: Command: bi <inst>[:<mask>] [<skipcount>]
786:
787: Sets an instruction breakpoint. <inst> must be 16 bits or 32 bits on
788: m68k and must be 32 bits on m88k. <mask> can specify the mask. Its
789: length must be the same as <inst>. If the <mask> is ommited, all bits
790: of <inst> is used to compare.
791: For example,
792: "bi 4e75" on m68k will stop at 'rts' instruction.
793: "bi 70004e4f:fff0ffff" on m68k will stop at any of 'IOCS #0'..'IOCS #15'.
794:
795: XXX skipcount
796: If <skipcount> is -1, it will never match. It's useful to count the
797: number of times you have passed this address.
798: )**" },
799:
800: //-----
801: { "bv", R"**(
802: Command: bv <vector>[-<vector2>] [<skipcount>]
803:
804: Sets an exception breakpoint. <vector> must be specified in hex.
805: <vector> ranges from 0 to ff (255 in decimal) on m68k, and 0 to 1ff (511
806: in decimal) on m88k. If <vector2> is specified, it matches the range
807: from <vector> to <vector2> (including themselves).
808: For example, "bv 1-1ff" on m88k means all exceptions but reset.
809: XXX skipcount
810: If <skipcount> is -1, it will never match. It's useful to count the
811: number of times you have passed this address.
812: )**" },
813:
814: //-----
815: { "bx", R"**(
816: Command: bx
817:
818: Deletes all breakpoints.
819: )**" },
820:
821: //-----
822: { "brhist", R"**(
823: Command: brhist [<maxlines>]
824:
825: Show branch history. <maxlines> specifies the maximum number of lines
826: to display. If <maxlines> is negative value, sort in reverse order.
827: )**" },
828:
829: //-----
830: { "c", R"**(
831: Command: c [<address>]
832:
833: Continue (until <address> if specified).
834: )**" },
835:
836: //-----
837: { "d", R"**(
838: Command: D <address>] [<lines>]
1.1.1.6 ! root 839: Command: d [[<space>:]<address>] [<lines>]
! 840: Command: dt [[<space>:]<address>] [<lines>]
1.1.1.5 root 841:
842: Shows disassemble.
843: The first form ("D") interprets <address> as a physical address.
844: The second and third form ("d", "dt") interprets <address> as a logical
845: address if the address translation is enabled. Normally, <address> is
1.1.1.6 ! root 846: interpreted in the current privilege and current address space. You can
! 847: change it by <space> modifier.
! 848: On m68k, <space> can be specified either by function code number directly
! 849: ('1', '2', '5', and '6) or by one or more following modifiers:
! 850: 's' .. Supervisor mode 'u' .. User mode
! 851: 'd' .. Data space 'p' or 'i' .. Program space
! 852: On m88k, <space> can be specified by using combination of privilege
! 853: modifier ('s' or 'u', same as above) and following CMMU identifiers:
! 854: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
! 855: Or CMMU can also be identified by CMMU number (like '6' or '7').
! 856:
1.1.1.5 root 857: "d" only looks up in ATC (on m68k) or in BATC/PATC (on m88k).
858: "dt" will simulate to search the page table in addition to that.
859: )**" },
860: { "dt", "=d" },
861: { "D", "=d" },
862:
863: //-----
864: { "disp", R"**(
865: Command: disp <register...>
866:
867: Sets the registers list to be shown in the trace.
868: <registers...> is comma-separated list and each of which is a command
869: name that shows registers. See "hr".
870: The default is "r" and thus it only shows general registers in the trace.
871: For example, if you set "disp r,rf", it will show general registers and
872: FPU registers in every trace.
873: )**" },
874:
875: //-----
876: { "exhist", R"**(
877: Command: exhist [<maxlines>]
878:
879: Show exception history. <maxlines> specifies the maximum number of lines
880: to display. If <maxlines> is negative value, sort in reverse order.
881: )**" },
882:
883: //-----
884: { "help", R"**(
885: Command: help [<command>]
886: Command: hb
887: Command: hr
888:
889: The "help" command without arguments shows the list of commands.
890: The "help" command with an argument shows <command>'s detailed help.
891: "h" is a synonym of "help".
892: The "hb" command shows the list of breakpoint-related commands.
893: The "hr" command shows the list of register-related commands.
894: )**" },
895: { "h", "=help" },
896: { "hb", "=help" },
897: { "hr", "=help" },
898:
899: //-----
900: { "L", R"**(
901: Command: L <name> <level>
902:
903: Set loglevel. XXX To be written...
904: )**" },
905:
906: //-----
907: { "m", R"**(
908: Command: M <address>] [<lines>]
1.1.1.6 ! root 909: Command: m [[<space>:]<address>] [<lines>]
! 910: Command: mt [[<space>:]<address>] [<lines>]
1.1.1.5 root 911:
912: Shows memory dump.
913: The first form ("M") interprets <address> as a physical address.
914: The second and third form ("m", "mt") interprets <address> as a logical
915: address if the address translation is enabled. Normally, <address> is
1.1.1.6 ! root 916: interpreted in the current privilege and current address space. You can
! 917: change it by <space> modifier.
! 918: On m68k, <space> can be specified either by function code number directly
! 919: ('1', '2', '5', and '6) or by one or more following modifiers:
! 920: 's' .. Supervisor mode 'u' .. User mode
! 921: 'd' .. Data space 'p' or 'i' .. Program space
! 922: On m88k, <space> can be specified by using combination of privilege
! 923: modifier ('s' or 'u', same as above) and following CMMU identifiers:
! 924: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
! 925: Or CMMU can also be identified by CMMU number (like '6' or '7').
! 926:
1.1.1.5 root 927: "m" only looks up in ATC (on m68k) or in BATC/PATC (on m88k).
928: "mt" will simulate to search the page table in addition to that.
929: )**" },
930: { "mt", "=m" },
931: { "M", "=m" },
932:
933: //-----
934: { "n", R"**(
935: Command: n [<count>]
936: Command: nt [<count>]
937:
938: Step one (or <count>) instructions. Unlike "s" command, "n" skips
939: subroutine.
940: If "t" is suffixed, it shows a trace for each instruction (including
941: while skipping).
942: )**" },
943: { "nt", "=n" },
944:
945: //-----
946: { "quit", R"**(
947: Command: quit (or q)
948:
949: Quit the debugger console. This continues the execution, as same as "c".
950: )**" },
951: { "q", "=quit" },
952:
953: //-----
954: { "reset", R"**(
955: Command: reset
956:
957: Reset the VM. Currently this does the hardware reset.
958: )**" },
959:
960: //-----
961: { "s", R"**(
962: Command: s [<count>]
963: Command: st [<count>]
964:
965: Step one (or <count>) instructions. Unlike "n" command, "s" steps in
966: subroutine.
967: If "t" is suffixed, it shows a trace for each instruction.
968:
969: "t" command is a synonym of "st" for backward compatibility.
970: )**" },
971: { "st", "=s" },
972:
973: //-----
974: { "so", R"**(
975: Command: so
976: Command: sot
977:
978: Step out this sub routine.
979: If "t" is suffixed, it shows a trace for each instruction.
980: )**" },
981:
982: //-----
983: { "show", R"**(
984: Command: show <monitor>
985:
986: Shows the specified monitor.
987: )**" },
988:
989: //-----
990: { "t", "=s" },
991: };
992:
1.1.1.3 root 993: // cmdbuf を args... に分解する。
1.1 root 994: void
1.1.1.3 root 995: Debugger::ParseCmdbuf()
1.1 root 996: {
1.1.1.3 root 997: int pos;
998: int start;
999:
1000: pos = 0;
1001: args.clear();
1.1 root 1002:
1003: // 引数を空白で分解
1.1.1.3 root 1004: for (;;) {
1005: // 先頭の空白はスキップ
1006: for (; cmdbuf[pos] != '\0'; pos++) {
1007: if (!isspace((unsigned int)cmdbuf[pos]))
1008: break;
1009: }
1010: if (cmdbuf[pos] == '\0')
1.1 root 1011: break;
1012:
1.1.1.3 root 1013: // 終わりを探す
1014: start = pos;
1015: for (; cmdbuf[pos] != '\0'; pos++) {
1016: if (isspace((unsigned int)cmdbuf[pos]))
1.1 root 1017: break;
1018: }
1.1.1.3 root 1019: args.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2 root 1020: }
1021: // デバッグ表示
1022: if (0) {
1.1.1.3 root 1023: for (int i = 0 ; i < args.size(); i++) {
1024: printf("args[%d]=|%s|\n", i, args[i].c_str());
1.1.1.2 root 1025: }
1026: }
1.1 root 1027: }
1028:
1029: //
1030: // デバッガコマンド
1031: //
1032:
1033: // ブレークポイント
1.1.1.4 root 1034: // b ... 一覧表示
1035: // b #n ... #n を削除
1036: // b <addr> [<skip>] ... 設定
1.1 root 1037: void
1038: Debugger::cmd_b()
1039: {
1040: // 引数なしなら一覧表示
1.1.1.3 root 1041: if (args.size() < 2) {
1.1 root 1042: return cmd_b_list();
1043: }
1044:
1045: // 引数取得
1.1.1.3 root 1046: if (args[1][0] == '#') {
1.1 root 1047: // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.3 root 1048: int i = atoi(&args[1][1]);
1049: if (i < 0 || i >= bpoint.size()) {
1.1.1.4 root 1050: cons->Print("invaild breakpoint number: #%d\n", i);
1.1 root 1051: return;
1052: }
1.1.1.3 root 1053: auto& bp = bpoint[i];
1.1.1.4 root 1054: if (bp.type == BreakpointType::Unused) {
1055: cons->Print("invalid breakpoint number: #%d\n", i);
1056: return;
1.1 root 1057: }
1.1.1.4 root 1058: cons->Print("breakpoint #%d (%08x) removed\n",
1059: i, bp.addr);
1060: bp.type = BreakpointType::Unused;
1061: // 今登録されている命令ブレークの必要命令長を再計算
1062: RecalcInstMask();
1.1 root 1063: return;
1064: }
1065:
1.1.1.4 root 1066: return cmd_b_set(BreakpointType::Address);
1067: }
1068:
1069: // メモリブレークポイントの設定
1070: // bm <addr> [<skip>]
1071: void
1072: Debugger::cmd_bm()
1073: {
1074: // XXX m68k では未サポート
1.1.1.6 ! root 1075: if (md->arch == Arch::M680x0) {
1.1.1.4 root 1076: cons->Print("bm not supported yet on m68k\n");
1077: return;
1078: }
1079: cmd_b_set(BreakpointType::Memory);
1080: }
1081:
1082: // type が違うだけの各種ブレークポイント設定の共通部分。
1083: void
1084: Debugger::cmd_b_set(BreakpointType type)
1085: {
1086: breakpoint_t bp;
1087:
1088: if (args.size() < 2) {
1089: cons->Print("usage: %s <addr> [<skipcount>]\n", args[0].c_str());
1090: return;
1091: }
1092:
1093: // アドレス
1094: if (!ParseAddr(args[1].c_str(), &bp.addr)) {
1095: return;
1096: }
1097: // あればスキップカウント
1098: if (args.size() > 2) {
1099: bp.skip = atoi(args[2].c_str());
1100: }
1101:
1102: // 空いてるところにセット
1103: // (よく似たエントリがあっても干渉しない)
1104: bp.type = type;
1105: int bi = AddBreakpoint(bp);
1106: if (bi == -1) {
1107: cons->Print("no free breakpoints\n");
1.1 root 1108: } else {
1.1.1.4 root 1109: cons->Print("breakpoint #%d added\n", bi);
1.1.1.3 root 1110: }
1.1.1.4 root 1111: }
1112:
1113: // 命令ブレークポイントの設定
1114: // bi <insn>[:<mask>] [<skip>]
1115: void
1116: Debugger::cmd_bi()
1117: {
1118: breakpoint_t bp;
1119: std::string inststr;
1120: std::string maskstr;
1121: int instlen;
1122: int masklen;
1123:
1124: if (args.size() < 2) {
1125: cons->Print("usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.3 root 1126: return;
1.1 root 1127: }
1128:
1.1.1.4 root 1129: // 引数をまず分離
1130: auto pos = args[1].find(':');
1131: if (pos == std::string::npos) {
1132: // マスク指定なし
1133: inststr = args[1];
1134: instlen = inststr.size();
1135: masklen = -1;
1136: } else {
1137: // マスク指定あり
1138: inststr = args[1].substr(0, pos);
1139: instlen = inststr.size();
1140: maskstr = args[1].substr(pos + 1);
1141: masklen = maskstr.size();
1142: }
1143:
1144: // 命令部チェック
1145: if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
1146: cons->Print("%s: invalid instruction value\n", args[1].c_str());
1147: return;
1148: }
1.1.1.5 root 1149: if (instlen % (md->inst_bytes * 2) != 0) {
1.1.1.4 root 1150: cons->Print("%s: invalid instruction length\n", args[1].c_str());
1151: return;
1152: }
1153:
1154: // マスク部チェック
1155: bp.mask = 0xffffffff;
1156: if (masklen != -1) {
1157: if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
1158: cons->Print("%s: invalid mask value\n", args[1].c_str());
1159: return;
1160: }
1161: if (masklen != instlen) {
1162: cons->Print("%s: inst:mask must be the same length\n",
1163: args[1].c_str());
1.1 root 1164: return;
1165: }
1166: }
1.1.1.4 root 1167: // 8バイト未満なら左詰め。
1.1.1.5 root 1168: if (md->inst_bytes < 4 && instlen < 8) {
1.1.1.4 root 1169: bp.inst <<= 32 - instlen * 4;
1170: bp.mask <<= 32 - instlen * 4;
1171: }
1172:
1173: // あればスキップカウント
1174: bp.skip = 0;
1175: if (args.size() > 2) {
1176: bp.skip = atoi(args[2].c_str());
1177: }
1178:
1179: // 空いてるところにセット
1180: bp.type = BreakpointType::Instruction;
1181: int bi = AddBreakpoint(bp);
1182: if (bi == -1) {
1183: cons->Print("no free breakpoints\n");
1184: } else {
1185: cons->Print("breakpoint #%d added\n", bi);
1186: }
1187:
1188: // 今登録されている命令ブレークの必要命令長を再計算
1189: RecalcInstMask();
1190: }
1191:
1192: // 例外ブレークポイントの設定
1193: // bv <vector_number>[-<end_number>] [<skip>]
1194: void
1195: Debugger::cmd_bv()
1196: {
1197: breakpoint_t bp;
1198:
1199: if (args.size() < 2) {
1200: cons->Print("usage: be <vec#>[-<end#>] [<skipcount>]\n");
1201: return;
1202: }
1203:
1204: auto pos = args[1].find('-');
1205: if (pos == std::string::npos) {
1206: // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
1207: if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) {
1208: cons->Print("%s: invalid vector number\n", args[1].c_str());
1209: return;
1210: }
1211: bp.vec2 = bp.vec1;
1212: } else {
1213: // ベクタ番号(範囲指定 [vec1, vec2])
1214: std::string str1 = args[1].substr(0, pos);
1215: std::string str2 = args[1].substr(pos + 1);
1216:
1217: if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) {
1218: cons->Print("%s: invalid first vector number\n", args[1].c_str());
1219: return;
1220: }
1221: if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) {
1222: cons->Print("%s: invalid last vector number\n", args[1].c_str());
1223: return;
1224: }
1225: }
1226:
1227: // 範囲チェック
1228: if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) {
1229: cons->Print("$%x: invalid vector number\n", bp.vec1);
1230: return;
1231: }
1232: if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) {
1233: cons->Print("$%x: invalid last vector number\n", bp.vec2);
1234: return;
1235: }
1236:
1237: // 大小が逆なら入れ替える?
1238: if (bp.vec1 > bp.vec2) {
1239: int tmp;
1240: tmp = bp.vec1;
1241: bp.vec1 = bp.vec2;
1242: bp.vec2 = tmp;
1243: }
1244:
1245: // あればスキップカウント
1246: bp.skip = 0;
1247: if (args.size() > 2) {
1248: bp.skip = atoi(args[2].c_str());
1249: }
1.1 root 1250:
1251: // 空いてるところにセット
1.1.1.4 root 1252: bp.type = BreakpointType::Exception;
1253: int bi = AddBreakpoint(bp);
1.1 root 1254: if (bi == -1) {
1.1.1.4 root 1255: cons->Print("no free breakpoints\n");
1.1 root 1256: } else {
1.1.1.4 root 1257: cons->Print("breakpoint #%d added\n", bi);
1.1 root 1258: }
1.1.1.5 root 1259:
1260: // すでに来ている例外をクリア。
1261: // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に
1262: // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは
1263: // 命令間(命令前)に呼ばれるやつ、なのでこうなる。
1264: // 1. 例外が起きると bv_vector がセットされる
1265: // 2. 例外ブレークポイントを設定していないとこれがクリアされない
1266: // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で
1267: // 1.のベクタが反応してしまう。
1268: // 命令ごととかにクリアしてもいいかも知れないが、ここでブレークポイントを
1269: // 設定したのだから、それ以前の事象には反応すべきでない、という意味では
1270: // ここでもいいか?
1271: bv_vector = -1;
1.1 root 1272: }
1273:
1274: // ブレークポイント一覧表示
1275: void
1276: Debugger::cmd_b_list()
1277: {
1.1.1.4 root 1278: ShowMonitor(*gBreakpointMonitor);
1279: }
1280:
1281: // ブレークポイント一覧 (モニタ)
1282: void
1283: Debugger::MonitorBreakpoint(TextScreen& monitor)
1284: {
1285: // 0 1 2 3 4 5 6
1286: // 0123456789012345678901234567890123456789012345678901234567890
1287: // No Type Parameter Matched Skip
1288: // #0 addr $01234567 123456789 123456789/123456789
1289: // #1 inst 00000000/00000000
1290: // #2 excp $00-$00
1291:
1292: monitor.Clear();
1293: monitor.Print(0, 0, "No Type Parameter");
1294: monitor.Print(26, 0, "Matched");
1295: monitor.Print(37, 0, "Skip");
1.1 root 1296:
1.1.1.3 root 1297: for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 root 1298: const auto& bp = bpoint[i];
1299: int y = i + 1;
1300:
1301: monitor.Print(0, y, "#%d", i);
1302:
1303: // 種別ごとの表示
1304: switch (bp.type) {
1305: case BreakpointType::Unused:
1306: continue;
1307:
1308: case BreakpointType::Address:
1309: monitor.Print(3, y, "addr $%08x", bp.addr);
1310: break;
1311: case BreakpointType::Memory:
1312: monitor.Print(3, y, "mem $%08x", bp.addr);
1313: break;
1314:
1315: case BreakpointType::Exception:
1316: monitor.Print(3, y, "excp $%02x", bp.vec1);
1317: if (bp.vec2 != bp.vec1) {
1318: monitor.Print(11, y, "-$%02x", bp.vec2);
1319: }
1320: break;
1321:
1322: case BreakpointType::Instruction:
1323: monitor.Print(3, y, "inst");
1.1.1.5 root 1324: if (md->inst_bytes == 4) {
1.1.1.4 root 1325: if (bp.mask == 0xffffffff) {
1326: monitor.Print(8, y, "%08x", bp.inst);
1327: } else {
1328: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1329: }
1330: } else {
1331: if (bp.mask == 0xffffffff) {
1332: monitor.Print(8, y, "%08x", bp.inst);
1333: } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) {
1334: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1335: } else if (bp.mask == 0xffff0000) {
1336: monitor.Print(8, y, "%04x", bp.inst >> 16);
1337: } else {
1338: monitor.Print(8, y, "%04x:%04x",
1339: bp.inst >> 16, bp.mask >> 16);
1340: }
1341: }
1342: break;
1343:
1344: default:
1345: monitor.Print(3, y, "type=%d", (int)bp.type);
1346: continue;
1347: }
1348:
1349: // マッチ回数
1350: monitor.Print(26, y, "%d", bp.matched);
1351:
1352: // スキップ
1353: if (bp.skip < 0) {
1354: monitor.Print(37, y, "forever");
1355: } else if (bp.skip > 0) {
1356: monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1 root 1357: }
1358: }
1359: }
1360:
1361: // ブレークポイント全削除
1362: void
1363: Debugger::cmd_bx()
1364: {
1.1.1.3 root 1365: for (auto& bp : bpoint) {
1.1.1.4 root 1366: bp.type = BreakpointType::Unused;
1.1 root 1367: }
1368: cons->Print(" All breakpoints disabled\n");
1.1.1.4 root 1369:
1370: // 今登録されている命令ブレークの必要命令長を再計算
1371: RecalcInstMask();
1.1 root 1372: }
1373:
1374: // ブレークポイントを設定。
1.1.1.4 root 1375: // new_bp のうち matched, skipremain はこちらで初期化する。
1376: // それ以外を埋めてから呼ぶこと。
1377: // 設定できればその番号、できなければ -1 を返す。
1.1 root 1378: int
1.1.1.4 root 1379: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1 root 1380: {
1.1.1.3 root 1381: for (int i = 0; i < bpoint.size(); i++) {
1382: auto& bp = bpoint[i];
1.1.1.4 root 1383: if (bp.type == BreakpointType::Unused) {
1384: bp = new_bp;
1385: bp.matched = 0;
1386: if (bp.skip > 0) {
1387: bp.skipremain = bp.skip;
1388: } else {
1389: bp.skipremain = 0;
1390: }
1391:
1.1 root 1392: return i;
1393: }
1394: }
1395: return -1;
1396: }
1397:
1.1.1.4 root 1398: // すべての命令ブレークのマスクのうち最長のものを再計算する。
1399: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
1400: void
1401: Debugger::RecalcInstMask()
1402: {
1403: uint32 mask;
1404: bi_need_bytes = 0;
1405:
1406: // 登録されている命令ブレークの最長マスクを求める
1407: mask = 0;
1408: for (const auto& bp : bpoint) {
1409: if (bp.type == BreakpointType::Instruction) {
1410: mask |= bp.mask;
1411: }
1412: }
1413:
1414: // mask の Number of Trailing Zero を求める。
1415: // (x & -x) で x の最も下の立ってるビットだけを立てる、
1416: // (x & -x) -1 でそれより下の全ビットを立てる、
1417: // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
1418: int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
1419: // 上位側から数えたマスクに必要なビット数
1420: int mlen = 32 - ntz;
1421: // 命令語単位に切り上げる
1.1.1.5 root 1422: mlen = roundup(mlen, md->inst_bytes * 8);
1.1.1.4 root 1423: // バイト数に変換
1424: mlen /= 8;
1425:
1426: if (mlen > bi_need_bytes) {
1427: bi_need_bytes = mlen;
1428: }
1429: }
1430:
1431: // ブランチ履歴、例外履歴表示
1432: // brhist [<maxlines>]
1433: // exhist [<maxlines>]
1434: // <maxlines> は表示する最大エントリ数。
1435: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
1436: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
1437: // する。
1.1 root 1438: void
1439: Debugger::cmd_brhist()
1440: {
1.1.1.4 root 1441: cmd_hist_common(md->GetBrHist(), 0);
1442: }
1443: void
1444: Debugger::cmd_exhist()
1445: {
1446: cmd_hist_common(md->GetExHist(), BranchHistory::ExHist);
1447: }
1.1.1.3 root 1448:
1.1.1.4 root 1449: // ブランチ履歴、例外履歴表示の共通部分。
1450: void
1451: Debugger::cmd_hist_common(BranchHistory& hist, uint64 flag)
1452: {
1453: // 表示最大行数(と向き)
1454: // 向きは bottom_to_top = true が新しいほうを下とする方向。
1455: int maxlines = 20;
1456: bool bottom_to_top = true;
1.1.1.3 root 1457: if (args.size() > 1) {
1.1.1.4 root 1458: maxlines = atoi(args[1].c_str());
1459: if (maxlines < 0) {
1460: bottom_to_top = false;
1461: maxlines = -maxlines;
1.1.1.3 root 1462: }
1.1.1.4 root 1463:
1464: if (maxlines < 1)
1465: maxlines = 1;
1466: if (maxlines > 256)
1467: maxlines = 256;
1468: }
1469:
1470: // コンソールでは空エントリの行は表示したくないので、
1471: // 先にエントリ数を調べる。
1472: int used = hist.GetUsed();
1473:
1474: // 表示行数 + 1行はヘッダ分
1475: int lines = std::min(used, maxlines) + 1;
1476:
1477: // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
1478: auto size = hist.GetMonitorSize();
1479: TextScreen tscr;
1480: tscr.Init(size.width, lines);
1481:
1482: // コンソールでは下が新しいの順のほうがいい
1483: if (bottom_to_top) {
1484: flag |= BranchHistory::BottomToTop;
1.1.1.3 root 1485: }
1.1.1.4 root 1486: tscr.userdata = flag;
1487:
1488: // 表示
1489: hist.MonitorUpdate(tscr);
1490: ShowTextScreen(tscr);
1.1 root 1491: }
1492:
1493: // 実行再開(continue): c [<addr>]
1494: // <addr> 指定があれば <addr> まで実行。
1495: void
1496: Debugger::cmd_c()
1497: {
1.1.1.3 root 1498: if (args.size() > 1) {
1.1 root 1499: // 引数があれば
1.1.1.3 root 1500: if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1 root 1501: return;
1502: }
1503: // 偶数番地に丸める
1504: bc_addr &= 0xfffffffe;
1505:
1506: bc_enable = true;
1507: }
1508: }
1509:
1.1.1.5 root 1510: // 引数などからアドレスを取得するごった煮共通ルーチン。
1511: //
1.1.1.6 ! root 1512: // 引数がなければ sa->addr には書き込まずに true で帰る (ので、呼び出し側が
! 1513: // 事前に適切なデフォルト値をセットしておくと、それがそのまま使われる)。
1.1.1.3 root 1514: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1.1.6 ! root 1515: // アドレスには空間修飾子を前置可能、省略の場合はいずれも現在値を使う。
! 1516: // エラーならメッセージを表示して false を返す。
1.1.1.5 root 1517: // sa は in/out パラメータ。
1.1 root 1518: bool
1.1.1.6 ! root 1519: Debugger::GetAddr(saddr_t *sa, bool default_data)
1.1 root 1520: {
1.1.1.5 root 1521: if (args.size() < 2) {
1.1.1.6 ! root 1522: // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。
1.1.1.5 root 1523: return true;
1524: }
1525:
1.1.1.6 ! root 1526: // 引数ありならパースする。アドレスが明示的に指定されたので、
! 1527: // ここから先の省略箇所は前回値ではなくデフォルト値で補完する。
! 1528:
1.1.1.5 root 1529: std::string& str = args[1];
1530: uint32 addr;
1531:
1.1.1.6 ! root 1532: // ':' があればその前はアドレス空間修飾子
! 1533: int mod_super = -1; // 'u'/'s'
! 1534: int mod_prog = -1; // 'd'/'p' or 'd'/'i'
! 1535: int mod_num = -1; // '0'..'7'
! 1536: auto addrpos = str.find(':');
! 1537: if (addrpos == std::string::npos) {
! 1538: // なければ先頭からアドレス
! 1539: addrpos = 0;
! 1540: } else {
! 1541: // ':' が途中にあれば、その前が修飾子。
! 1542: // ':' が先頭なら修飾子が空文字列という扱いでいいか。
! 1543: std::string mod = str.substr(0, addrpos);
! 1544: addrpos++;
! 1545: for (auto ch : mod) {
! 1546: if (ch == 'u') {
! 1547: if (mod_super == 1)
! 1548: goto error;
! 1549: mod_super = 0;
! 1550: } else if (ch == 's') {
! 1551: if (mod_super == 0)
! 1552: goto error;
! 1553: mod_super = 1;
! 1554: } else if (ch == 'd') {
! 1555: if (mod_prog == 1)
! 1556: goto error;
! 1557: mod_prog = 0;
! 1558: } else if (ch == 'p' || ch == 'i') {
! 1559: // m68k では 'P'rogram space、m88k では 'I'nstruction CMMU…
! 1560: if (mod_prog == 0)
! 1561: goto error;
! 1562: mod_prog = 1;
! 1563: } else if ('0' <= ch && ch <= '7') {
! 1564: int num = ch - '0';
! 1565: if (md->arch == Arch::M680x0) {
! 1566: // m68k では FC 指定は Super/User 指定を含んでおり、
! 1567: // 値指定は他の修飾子とは同時に指定できない。
! 1568: if (mod_super >= 0 || mod_prog >= 0 ||
! 1569: (mod_num >= 0 && mod_num != num)) {
! 1570: goto error;
! 1571: }
! 1572: // 有効な値は 1,2,5,6 のみ
! 1573: if (num == 0 || num == 3 || num == 4 || num == 7) {
! 1574: cons->Print("%c: invalid address space\n", ch);
! 1575: }
! 1576: mod_num = num;
! 1577: mod_super = mod_num >> 2; // FC2
! 1578: mod_prog = (mod_num & 3) - 1; // FC1,FC0
! 1579: } else if (md->arch == Arch::M88xx0) {
! 1580: // m88k では CMMU 指定と、Super/User 指定は独立。
! 1581: if (mod_prog >= 0 ||
! 1582: (mod_num >= 0 && mod_num != num)) {
! 1583: goto error;
! 1584: }
! 1585: mod_num = num;
! 1586: mod_prog = (mod_num & 1); // CMMU7 が Inst
! 1587: }
! 1588: } else {
! 1589: cons->Print("%c: unknown address space modifier\n", ch);
! 1590: return false;
! 1591: }
! 1592: continue;
! 1593:
! 1594: error:
! 1595: cons->Print("%s: address space modifier '%c' conflicts\n",
! 1596: mod.c_str(), ch);
! 1597: return false;
! 1598: }
! 1599:
! 1600: // 数値指定を機種ごとに読み替える
! 1601: if (mod_num >= 0) {
! 1602: if (md->arch == Arch::M680x0) {
! 1603: // m68k なら値指定で全部確定する
! 1604: // "1:" -> User/Data
! 1605: // "2:" -> User/Program
! 1606: // "5:" -> Super/Data
! 1607: // "6:" -> Super/Program
! 1608: mod_super = (mod_num & 4) ? true : false;
! 1609: mod_prog = (mod_num & 3) - 1;
! 1610: } else if (md->arch == Arch::M88xx0) {
! 1611: // m88k では mod_num を CMMU ID とする(?)。
! 1612: // XXX まだ CPU は1つしかないので雑に処理。
! 1613: // "6" -> Data (CPU#0)
! 1614: // "7" -> Program (CPU#0)
! 1615: if (mod_num < 6) {
! 1616: cons->Print("%d: CMMU#%d not installed\n",
! 1617: mod_num, mod_num);
! 1618: return false;
! 1619: }
! 1620: mod_prog = mod_num - 6;
! 1621: }
! 1622: }
1.1 root 1623: }
1.1.1.5 root 1624:
1.1.1.6 ! root 1625: // アドレス空間修飾子のうち省略箇所はデフォルト状態で補完
! 1626: // - 's'/'u' いずれもなければ現在値。
! 1627: // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。
! 1628: if (mod_super < 0) {
! 1629: mod_super = md->IsSuper();
! 1630: }
! 1631: if (mod_prog < 0) {
! 1632: mod_prog = !default_data;
! 1633: }
! 1634: sa->SetSuper(mod_super);
! 1635: sa->SetData(!mod_prog);
! 1636:
1.1.1.5 root 1637: // アドレス
1638: if (ParseAddr(str.c_str() + addrpos, &addr) == false) {
1639: return false;
1640: }
1641: // 偶数番地に丸める
1.1.1.6 ! root 1642: sa->SetAddr(addr & 0xfffffffe);
1.1.1.5 root 1643:
1.1 root 1644: return true;
1645: }
1646:
1.1.1.5 root 1647: // 逆アセンブル: d [[SU:]<addr> [<cnt>]] (論理、テーブルサーチなし)
1.1 root 1648: void
1649: Debugger::cmd_d()
1650: {
1.1.1.6 ! root 1651: cmd_d_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1652: }
1653:
1.1.1.5 root 1654: // 逆アセンブル: dt [[SU:]<addr>] [<cnt>]] (論理、テーブルサーチあり)
1.1 root 1655: void
1656: Debugger::cmd_dt()
1657: {
1.1.1.6 ! root 1658: cmd_d_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1659: }
1660:
1661: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
1662: void
1663: Debugger::cmd_D()
1664: {
1.1.1.6 ! root 1665: cmd_d_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1666: }
1667:
1668: // 逆アセンブル共通
1669: void
1.1.1.6 ! root 1670: Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1671: {
1.1.1.2 root 1672: saddr_t saddr;
1.1 root 1673: int cnt;
1674:
1675: cnt = 10;
1676:
1677: // 開始アドレス
1.1.1.6 ! root 1678: saddr = d_last_addr;
! 1679: if (GetAddr(&saddr, false) == false) {
1.1 root 1680: return;
1681: }
1682: // 2つ目の引数があれば行数
1.1.1.3 root 1683: if (args.size() > 2) {
1684: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1685: }
1686:
1.1.1.6 ! root 1687: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1688:
1.1 root 1689: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1690: std::string addrbuf;
1691: std::string mnemonic;
1692: std::string mnembuf;
1693: std::vector<uint8> local_ir;
1694:
1.1.1.6 ! root 1695: // アドレス
! 1696: if (mem.FormatAddr(addrbuf) == false) {
! 1697: // アドレス変換でバスエラーならここで終了
! 1698: cons->Print("%s\n", addrbuf.c_str());
! 1699: break;
1.1 root 1700: }
1.1.1.6 ! root 1701:
! 1702: // 逆アセンブル
! 1703: md->Disassemble(mem, mnemonic, local_ir);
! 1704: // オフライン用に整形
! 1705: mnembuf = md->FormatDisasm(mnemonic, local_ir);
! 1706:
1.1.1.2 root 1707: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1708:
1709: // 変換できなければここで終了
1710: if (local_ir.size() == 0)
1711: break;
1.1 root 1712: }
1.1.1.2 root 1713:
1.1.1.6 ! root 1714: // アドレスと Super/User 状態を次回継続用に保存
! 1715: d_last_addr = mem.laddr;
1.1 root 1716: }
1717:
1.1.1.5 root 1718: // 表示レジスタ選択
1719: // disp 現在の内容を表示
1720: // disp <regs>... 設定
1721: void
1722: Debugger::cmd_disp()
1723: {
1724: if (args.size() < 2) {
1725: // 表示
1726: bool first = true;
1727: for (const auto& r : disp_regs) {
1728: cons->Print("%s%s", (first ? "" : ","), r.c_str());
1729: first = false;
1730: }
1731: cons->Print("\n");
1732: return;
1733: }
1734:
1735: // 設定 (引数を分解する)
1736: char buf[256];
1737: char *p;
1738: char *last;
1739: strlcpy(buf, args[1].c_str(), sizeof(buf));
1740: disp_regs.clear();
1741: for (p = strtok_r(buf, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
1742: disp_regs.push_back(std::string(p));
1743: }
1744:
1745: // XXX チェック
1746: }
1747:
1.1 root 1748: // ログレベル設定: L <name> <level>
1749: void
1750: Debugger::cmd_L()
1751: {
1752: const char *name;
1753: int level;
1754:
1.1.1.3 root 1755: if (args.size() < 3) {
1.1 root 1756: cons->Print("L <name> <level>\n");
1757: return;
1758: }
1759:
1.1.1.3 root 1760: name = args[1].c_str();
1761: level = atoi(args[2].c_str());
1.1 root 1762:
1763: // 短縮形とかエイリアスとか
1764: // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい
1765: if (strcmp(name, "sch") == 0)
1766: name = "scheduler";
1767: if (strcmp(name, "scc") == 0)
1768: name = "sio";
1769:
1.1.1.3 root 1770: // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1 root 1771: // all はどうするか
1772: std::string sname = name;
1773: bool found = false;
1774: for (auto& obj : gObjects) {
1775: if (obj->logname == sname) {
1776: obj->loglevel = level;
1777: cons->Print("%s=%d\n", name, obj->loglevel);
1778: found = true;
1779: }
1780: }
1781: // 見付からない場合はエラー
1782: if (!found) {
1783: cons->Print("%s: invalid logname\n", name);
1784: }
1785: }
1786:
1787: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
1788: void
1789: Debugger::cmd_m()
1790: {
1.1.1.6 ! root 1791: cmd_m_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1792: }
1793:
1794: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
1795: void
1796: Debugger::cmd_mt()
1797: {
1.1.1.6 ! root 1798: cmd_m_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1799: }
1800:
1801: // メモリダンプ: M [<addr> [<cnt>]] (物理)
1802: void
1803: Debugger::cmd_M()
1804: {
1.1.1.6 ! root 1805: cmd_m_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1806: }
1807:
1808: // メモリダンプ共通
1809: void
1.1.1.6 ! root 1810: Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1811: {
1.1.1.2 root 1812: saddr_t saddr;
1.1 root 1813: int cnt;
1814:
1815: cnt = 10;
1816:
1817: // 開始アドレス
1.1.1.6 ! root 1818: saddr = m_last_addr;
! 1819: if (GetAddr(&saddr, true) == false) {
1.1 root 1820: return;
1821: }
1822: // 2つ目の引数があれば行数
1.1.1.3 root 1823: if (args.size() > 2) {
1824: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1825: }
1826:
1.1.1.6 ! root 1827: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1828:
1.1 root 1829: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1830: std::string addrbuf;
1831: std::string dumpbuf;
1832: std::string charbuf;
1833:
1.1.1.6 ! root 1834: // アドレス
! 1835: if (mem.FormatAddr(addrbuf) == false) {
! 1836: // アドレス変換でバスエラーならここで終了
1.1.1.2 root 1837: cons->Print("%s\n", addrbuf.c_str());
1838: break;
1839: }
1840:
1841: for (int j = 0; j < 8; j++) {
1842: for (int k = 0; k < 2; k++) {
1.1.1.6 ! root 1843: uint64 b = mem.Fetch8();
1.1.1.2 root 1844:
1845: if ((int64)b < 0) {
1846: dumpbuf += "--";
1847: charbuf += " ";
1848: } else {
1849: uint32 c = (uint32)b;
1850: dumpbuf += string_format("%02x", c);
1851: charbuf += string_format("%c",
1852: (0x20 <= c && c <= 0x7e) ? c : '.');
1853: }
1854: }
1855: dumpbuf += " ";
1856: }
1.1.1.6 ! root 1857:
1.1.1.2 root 1858: cons->Print("%-18s %-40s %s\n",
1859: addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str());
1.1 root 1860: }
1.1.1.6 ! root 1861:
! 1862: // アドレスと Super/User 状態を次回継続用に保存
! 1863: m_last_addr = mem.laddr;
1.1 root 1864: }
1865:
1866: // プロンプトに来た最初に表示するいつものやつを再表示する
1867: void
1868: Debugger::cmd_minus()
1869: {
1.1.1.5 root 1870: // 指定のレジスタ群を表示
1871: // disp_regs はレジスタ群のリスト { "r", "rf" } のような感じ。
1872: // 一方 ShowRegisters() の第2引数は1つのコマンドラインを空白で区切った
1873: // 文字列リスト { arg[0], arg[1], .. }。型が同じで意味が違うので注意。
1874: for (const auto& reg : disp_regs) {
1875: std::vector<std::string> tmpargs;
1876: tmpargs.push_back(reg);
1877: md->ShowRegister(cons, tmpargs);
1878: }
1.1 root 1879:
1.1.1.2 root 1880: // 現在の PC の位置を逆アセンブル。ここで ir を更新。
1881: std::string addrbuf;
1882: std::string mnemonic;
1883: std::string mnembuf;
1884: ir.clear();
1.1.1.6 ! root 1885:
! 1886: // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし
! 1887: saddr_t laddr(pc, md->IsSuper());
! 1888: DebuggerMemoryStream mem(md, laddr);
! 1889: // アドレス
! 1890: if (mem.FormatAddr(addrbuf) == false) {
! 1891: cons->Print("%s\n", addrbuf.c_str());
! 1892: return;
1.1.1.2 root 1893: }
1.1.1.6 ! root 1894: // 逆アセンブル
! 1895: md->Disassemble(mem, mnemonic, ir);
! 1896: // ライブ表示用に整形
! 1897: mnembuf = md->FormatDisasmLive(mnemonic, ir);
! 1898:
1.1.1.2 root 1899: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1.1 root 1900: }
1901:
1.1.1.5 root 1902: // サブルーチンとループを飛ばしながら count 命令ステップ実行
1903: // n [<count>?:1] (途中レジスタを表示しない)
1.1 root 1904: void
1905: Debugger::cmd_n()
1906: {
1.1.1.5 root 1907: cmd_n_common(false);
1908: }
1909:
1910: // サブルーチンとループを飛ばしながら count 命令ステップ実行
1911: // nt [<count>?:1] (1命令ずつレジスタを表示する)
1912: void
1913: Debugger::cmd_nt()
1914: {
1915: cmd_n_common(true);
1916: }
1917:
1918: // n と nt の共通部分
1919: void
1920: Debugger::cmd_n_common(bool trace)
1921: {
1922: t_enable = trace;
1923:
1.1.1.3 root 1924: if (args.size() > 1) {
1.1 root 1925: int count;
1.1.1.3 root 1926: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 1927: if (count < 1) {
1928: cons->Print(" invalid step count: %d\n", count);
1929: return;
1930: }
1931:
1932: n_count = count;
1933: } else {
1934: n_count = 1;
1935: }
1936: n_enable = true;
1937: SetNBreakpoint();
1938: }
1939:
1.1.1.5 root 1940: // n コマンド用のブレークポイントを設定する。
1941: // この命令語によって仕掛けるブレークポイントが変わるので、都度都度
1942: // 呼び出すこと。
1943: void
1.1 root 1944: Debugger::SetNBreakpoint()
1945: {
1.1.1.6 ! root 1946: saddr_t laddr(md->GetPC(), md->IsSuper());
! 1947: DebuggerMemoryStream mem(md, laddr);
1.1.1.5 root 1948:
1.1.1.6 ! root 1949: // 命令がサブルーチンやトラップなどステップインできる命令か。
! 1950: if (md->IsOpStepIn(mem)) {
1.1.1.5 root 1951: // この次の命令にブレークをかける
1952: if (md->inst_bytes_fixed != 0) {
1953: // 固定長命令
1.1.1.6 ! root 1954: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 1955: } else {
1956: // 可変長なら逆アセンブルしてみるしか
1957: std::string mnemonic;
1958: std::vector<uint8> v;
1959: // XXX 失敗したらどうすべ
1.1.1.6 ! root 1960: mem.ResetAddr(md->GetPC());
! 1961: md->Disassemble(mem, mnemonic, v);
! 1962: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 1963: }
1.1 root 1964: } else {
1.1.1.5 root 1965: // そうでなければ1命令進める。
1966: // 0xffffffff は常にアドレスとして不正なのでこれをフラグに使う。
1967: n_breakaddr = 0xffffffff;
1.1 root 1968: }
1969: }
1970:
1.1.1.2 root 1971: // デバッガ終了コマンド
1972: void
1973: Debugger::cmd_q()
1974: {
1975: cons->Print("quit debugger console.\n");
1.1 root 1976: }
1977:
1.1.1.5 root 1978: // VM リセット
1979: void
1980: Debugger::cmd_reset()
1981: {
1982: gScheduler->RequestResetHard();
1983: }
1984:
1.1 root 1985: // モニター表示
1986: void
1987: Debugger::cmd_show()
1988: {
1.1.1.3 root 1989: if (args.size() != 2) {
1.1 root 1990: cons->Print("usage: show <monitor>\n");
1991: for (auto& d : gObjects) {
1992: if (d->logname != "?") {
1993: cons->Print(" %s\n", d->logname.c_str());
1994: }
1995: }
1996: return;
1997: }
1998:
1.1.1.3 root 1999: std::string name = args[1];
1.1 root 2000: for (auto& d : gObjects) {
2001: if (d->logname == name) {
1.1.1.3 root 2002: ShowMonitor(*d);
1.1 root 2003: return;
2004: }
2005: }
2006: // 一致しない
2007: cons->Print("show: no such objects\n");
2008: }
2009:
1.1.1.3 root 2010: // モニターを更新して表示。
2011: void
1.1.1.4 root 2012: Debugger::ShowMonitor(IMonitor& monitor)
1.1.1.3 root 2013: {
2014: // モニタを更新
1.1.1.4 root 2015: TextScreen ts;
2016:
2017: auto size = monitor.GetMonitorSize();
2018: ts.Init(size.width, size.height);
2019:
2020: monitor.MonitorUpdate(ts);
2021: ShowTextScreen(ts);
1.1.1.3 root 2022: }
2023:
2024: // テキストスクリーンを表示。
1.1 root 2025: void
1.1.1.3 root 2026: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 2027: {
2028: char sbuf[1024]; // 適当
2029:
2030: // monitor 内部バッファから ShiftJIS バッファを作成。
2031: // その際属性もエスケープシーケンスで再現する。
2032: int col = monitor.GetCol();
2033: int row = monitor.GetRow();
2034: const uint16 *src = monitor.GetBuf();
2035:
2036: for (int y = 0; y < row; y++) {
2037: int sy = y * col;
2038: int sx;
2039: uint attr;
2040: char *d;
2041:
2042: memset(sbuf, 0, sizeof(sbuf));
2043: d = sbuf;
2044: attr = TA::Normal;
2045: for (sx = 0; sx < col; sx++) {
2046: uint a = src[sy + sx] & 0xff00;
2047: uint ch = src[sy + sx] & 0x00ff;
2048:
2049: // 属性の変わり目
2050: if (attr != a) {
2051: switch (a) {
2052: case TA::Normal:
2053: case TA::Off:
2054: *d++ = 0x1b;
2055: *d++ = '[';
2056: *d++ = 'm';
2057: break;
2058: case TA::On:
2059: *d++ = 0x1b;
2060: *d++ = '[';
2061: *d++ = '7';
2062: *d++ = 'm';
2063: break;
2064: case TA::Disable:
2065: *d++ = 0x1b;
2066: *d++ = '[';
2067: *d++ = '2';
2068: *d++ = 'm';
2069: break;
2070: case TA::Em:
2071: *d++ = 0x1b;
2072: *d++ = '[';
2073: *d++ = '1';
2074: *d++ = 'm';
2075: break;
2076: }
2077: attr = a;
2078: }
2079: *d++ = ch;
2080: }
1.1.1.3 root 2081: // 行の終わりで一旦属性を戻しておく
2082: if (attr != TA::Normal) {
2083: *d++ = 0x1b;
2084: *d++ = '[';
2085: *d++ = 'm';
2086: }
1.1 root 2087: *d = '\0';
2088:
2089: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
2090:
2091: cons->Print("%s\n", sbuf);
2092: }
2093: }
2094:
1.1.1.5 root 2095: // ステップ実行
2096: // s [<count>?:1] (途中レジスタを表示しない)
1.1 root 2097: void
2098: Debugger::cmd_s()
2099: {
1.1.1.5 root 2100: cmd_s_common(false);
2101: }
2102:
2103: // ステップ実行
2104: // st [<count>?:1] (命令ごとにレジスタを表示する)
2105: void
2106: Debugger::cmd_st()
2107: {
2108: cmd_s_common(true);
2109: }
2110:
2111: // ステップ実行共通部分
2112: void
2113: Debugger::cmd_s_common(bool trace)
2114: {
2115: t_enable = trace;
2116:
1.1.1.3 root 2117: if (args.size() > 1) {
1.1 root 2118: int count;
1.1.1.3 root 2119: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 2120: if (count < 1) {
2121: cons->Print(" invalid step count: %d\n", count);
2122: return;
2123: }
2124:
2125: s_count = count;
2126: } else {
2127: s_count = 1;
2128: }
2129: s_enable = true;
2130: }
2131:
1.1.1.5 root 2132: // ステップアウト (途中レジスタを表示しない)
1.1 root 2133: void
2134: Debugger::cmd_so()
2135: {
1.1.1.5 root 2136: cmd_so_common(false);
2137: }
2138:
2139: // ステップアウト (命令ごとにレジスタを表示する)
2140: void
2141: Debugger::cmd_sot()
2142: {
2143: cmd_so_common(true);
2144: }
2145:
2146: // ステップアウトの共通部分
2147: void
2148: Debugger::cmd_so_common(bool trace)
2149: {
2150: t_enable = trace;
2151:
1.1 root 2152: so_enable = true;
1.1.1.2 root 2153: md->SetStepOut();
1.1 root 2154: }
2155:
1.1.1.5 root 2156: // t は st の省略形。互換のため。
1.1 root 2157: void
2158: Debugger::cmd_t()
2159: {
1.1.1.5 root 2160: cmd_st();
1.1 root 2161: }
2162:
1.1.1.4 root 2163: // 引数で示されるプレフィックスなしの16進数値を返す。
2164: // 正しく変換できれば値を valp に格納して true を返す。
2165: // そうでなければ false を返す。
2166: bool
2167: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
2168: {
2169: uint32 val;
2170: char *end;
2171:
2172: errno = 0;
2173: val = strtoul(arg, &end, 16);
2174: if (arg[0] == '\0' || end[0] != '\0') {
2175: return false;
2176: }
2177: if (errno == ERANGE) {
2178: return false;
2179: }
2180:
2181: *valp = val;
2182: return true;
2183: }
2184:
1.1 root 2185: // 引数で示されるアドレスを返す。
2186: // '%' で始まればレジスタ。
2187: // そうでなければ16進数値。
2188: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
2189: // そうでなければエラーメッセージを表示して偽を返す。
2190: bool
1.1.1.4 root 2191: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1 root 2192: {
1.1.1.4 root 2193: uint32 addr;
1.1 root 2194: char *end;
2195:
2196: if (arg[0] == '%') {
1.1.1.2 root 2197: // '%' から始まればレジスタ
2198: uint64 r;
2199: r = md->GetRegAddr(arg + 1);
2200: if ((int64)r < 0) {
2201: #if 0 // not yet
1.1 root 2202: cons->Print("valid register name are: %s%s\n",
2203: "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
2204: ",%msp,%vbr,%srp,%crp");
1.1.1.2 root 2205: #endif
2206: cons->Print("invalid register name\n");
1.1 root 2207: return false;
2208: }
1.1.1.2 root 2209: addr = (uint32)r;
1.1 root 2210: } else {
2211: /* そうでなければ番地指定 */
2212: errno = 0;
2213: addr = strtoul(arg, &end, 16);
2214: if (arg[0] == '\0' || end[0] != '\0') {
2215: cons->Print("invalid address: '%s'\n", arg);
2216: return false;
2217: }
2218: if (errno == ERANGE) {
2219: cons->Print("out of range: %s\n", arg);
2220: return false;
2221: }
2222: }
2223:
2224: *addrp = addr;
2225: return true;
2226: }
2227:
1.1.1.4 root 2228:
2229: //
1.1.1.5 root 2230: // MD
2231: //
2232:
1.1.1.6 ! root 2233: // デストラクタ
! 2234: DebuggerMD::~DebuggerMD()
! 2235: {
1.1.1.5 root 2236: }
2237:
2238:
2239: //
1.1.1.4 root 2240: // ブレークポイントモニター
2241: //
2242:
2243: // コンストラクタ
2244: BreakpointMonitor::BreakpointMonitor()
2245: {
2246: monitor_size = nnSize(55, 9);
2247: }
2248:
2249: void
2250: BreakpointMonitor::MonitorUpdate(TextScreen& monitor)
2251: {
2252: gDebugger->MonitorBreakpoint(monitor);
2253: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.