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