|
|
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.10! root 1097: cmd_b_delete();
1.1 root 1098: return;
1099: }
1100:
1.1.1.4 root 1101: return cmd_b_set(BreakpointType::Address);
1102: }
1103:
1104: // メモリブレークポイントの設定
1105: // bm <addr> [<skip>]
1106: void
1107: Debugger::cmd_bm()
1108: {
1109: // XXX m68k では未サポート
1.1.1.6 root 1110: if (md->arch == Arch::M680x0) {
1.1.1.4 root 1111: cons->Print("bm not supported yet on m68k\n");
1112: return;
1113: }
1114: cmd_b_set(BreakpointType::Memory);
1115: }
1116:
1117: // type が違うだけの各種ブレークポイント設定の共通部分。
1118: void
1119: Debugger::cmd_b_set(BreakpointType type)
1120: {
1121: breakpoint_t bp;
1122:
1123: if (args.size() < 2) {
1124: cons->Print("usage: %s <addr> [<skipcount>]\n", args[0].c_str());
1125: return;
1126: }
1127:
1128: // アドレス
1129: if (!ParseAddr(args[1].c_str(), &bp.addr)) {
1130: return;
1131: }
1132: // あればスキップカウント
1133: if (args.size() > 2) {
1134: bp.skip = atoi(args[2].c_str());
1135: }
1136:
1137: // 空いてるところにセット
1138: // (よく似たエントリがあっても干渉しない)
1139: bp.type = type;
1140: int bi = AddBreakpoint(bp);
1141: if (bi == -1) {
1142: cons->Print("no free breakpoints\n");
1.1 root 1143: } else {
1.1.1.4 root 1144: cons->Print("breakpoint #%d added\n", bi);
1.1.1.3 root 1145: }
1.1.1.4 root 1146: }
1147:
1.1.1.10! root 1148: // ブレークポイント個別削除
! 1149: // (引数の先頭が '#' なことを判定したところで呼ばれる)
! 1150: // b #<n>
! 1151: void
! 1152: Debugger::cmd_b_delete()
! 1153: {
! 1154: int n = atoi(&args[1][1]);
! 1155: if (n < 0 || n >= bpoint.size()) {
! 1156: cons->Print("invalid breakpoint number: #%d\n", n);
! 1157: return;
! 1158: }
! 1159:
! 1160: auto& bp = bpoint[n];
! 1161: if (bp.type == BreakpointType::Unused) {
! 1162: cons->Print("invalid breakpoint number: #%d\n", n);
! 1163: return;
! 1164: }
! 1165:
! 1166: cons->Print("breakpoint #%d removed\n", n);
! 1167: bp.type = BreakpointType::Unused;
! 1168: // 今登録されている命令ブレークの必要命令長を再計算
! 1169: RecalcInstMask();
! 1170: }
! 1171:
1.1.1.4 root 1172: // 命令ブレークポイントの設定
1173: // bi <insn>[:<mask>] [<skip>]
1174: void
1175: Debugger::cmd_bi()
1176: {
1177: breakpoint_t bp;
1178: std::string inststr;
1179: std::string maskstr;
1180: int instlen;
1181: int masklen;
1182:
1183: if (args.size() < 2) {
1184: cons->Print("usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.3 root 1185: return;
1.1 root 1186: }
1187:
1.1.1.4 root 1188: // 引数をまず分離
1189: auto pos = args[1].find(':');
1190: if (pos == std::string::npos) {
1191: // マスク指定なし
1192: inststr = args[1];
1193: instlen = inststr.size();
1194: masklen = -1;
1195: } else {
1196: // マスク指定あり
1197: inststr = args[1].substr(0, pos);
1198: instlen = inststr.size();
1199: maskstr = args[1].substr(pos + 1);
1200: masklen = maskstr.size();
1201: }
1202:
1203: // 命令部チェック
1204: if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
1205: cons->Print("%s: invalid instruction value\n", args[1].c_str());
1206: return;
1207: }
1.1.1.5 root 1208: if (instlen % (md->inst_bytes * 2) != 0) {
1.1.1.4 root 1209: cons->Print("%s: invalid instruction length\n", args[1].c_str());
1210: return;
1211: }
1212:
1213: // マスク部チェック
1214: bp.mask = 0xffffffff;
1215: if (masklen != -1) {
1216: if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
1217: cons->Print("%s: invalid mask value\n", args[1].c_str());
1218: return;
1219: }
1220: if (masklen != instlen) {
1221: cons->Print("%s: inst:mask must be the same length\n",
1222: args[1].c_str());
1.1 root 1223: return;
1224: }
1225: }
1.1.1.4 root 1226: // 8バイト未満なら左詰め。
1.1.1.5 root 1227: if (md->inst_bytes < 4 && instlen < 8) {
1.1.1.4 root 1228: bp.inst <<= 32 - instlen * 4;
1229: bp.mask <<= 32 - instlen * 4;
1230: }
1231:
1232: // あればスキップカウント
1233: bp.skip = 0;
1234: if (args.size() > 2) {
1235: bp.skip = atoi(args[2].c_str());
1236: }
1237:
1238: // 空いてるところにセット
1239: bp.type = BreakpointType::Instruction;
1240: int bi = AddBreakpoint(bp);
1241: if (bi == -1) {
1242: cons->Print("no free breakpoints\n");
1243: } else {
1244: cons->Print("breakpoint #%d added\n", bi);
1245: }
1246:
1247: // 今登録されている命令ブレークの必要命令長を再計算
1248: RecalcInstMask();
1249: }
1250:
1251: // 例外ブレークポイントの設定
1252: // bv <vector_number>[-<end_number>] [<skip>]
1253: void
1254: Debugger::cmd_bv()
1255: {
1256: breakpoint_t bp;
1257:
1258: if (args.size() < 2) {
1259: cons->Print("usage: be <vec#>[-<end#>] [<skipcount>]\n");
1260: return;
1261: }
1262:
1263: auto pos = args[1].find('-');
1264: if (pos == std::string::npos) {
1265: // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
1266: if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) {
1267: cons->Print("%s: invalid vector number\n", args[1].c_str());
1268: return;
1269: }
1270: bp.vec2 = bp.vec1;
1271: } else {
1272: // ベクタ番号(範囲指定 [vec1, vec2])
1273: std::string str1 = args[1].substr(0, pos);
1274: std::string str2 = args[1].substr(pos + 1);
1275:
1276: if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) {
1277: cons->Print("%s: invalid first vector number\n", args[1].c_str());
1278: return;
1279: }
1280: if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) {
1281: cons->Print("%s: invalid last vector number\n", args[1].c_str());
1282: return;
1283: }
1284: }
1285:
1286: // 範囲チェック
1287: if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) {
1288: cons->Print("$%x: invalid vector number\n", bp.vec1);
1289: return;
1290: }
1291: if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) {
1292: cons->Print("$%x: invalid last vector number\n", bp.vec2);
1293: return;
1294: }
1295:
1296: // 大小が逆なら入れ替える?
1297: if (bp.vec1 > bp.vec2) {
1298: int tmp;
1299: tmp = bp.vec1;
1300: bp.vec1 = bp.vec2;
1301: bp.vec2 = tmp;
1302: }
1303:
1304: // あればスキップカウント
1305: bp.skip = 0;
1306: if (args.size() > 2) {
1307: bp.skip = atoi(args[2].c_str());
1308: }
1.1 root 1309:
1310: // 空いてるところにセット
1.1.1.4 root 1311: bp.type = BreakpointType::Exception;
1312: int bi = AddBreakpoint(bp);
1.1 root 1313: if (bi == -1) {
1.1.1.4 root 1314: cons->Print("no free breakpoints\n");
1.1 root 1315: } else {
1.1.1.4 root 1316: cons->Print("breakpoint #%d added\n", bi);
1.1 root 1317: }
1.1.1.5 root 1318:
1319: // すでに来ている例外をクリア。
1320: // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に
1321: // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは
1322: // 命令間(命令前)に呼ばれるやつ、なのでこうなる。
1323: // 1. 例外が起きると bv_vector がセットされる
1324: // 2. 例外ブレークポイントを設定していないとこれがクリアされない
1325: // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で
1326: // 1.のベクタが反応してしまう。
1327: // 命令ごととかにクリアしてもいいかも知れないが、ここでブレークポイントを
1328: // 設定したのだから、それ以前の事象には反応すべきでない、という意味では
1329: // ここでもいいか?
1330: bv_vector = -1;
1.1 root 1331: }
1332:
1333: // ブレークポイント一覧表示
1334: void
1335: Debugger::cmd_b_list()
1336: {
1.1.1.9 root 1337: ShowMonitor(bpoint_monitor);
1.1.1.4 root 1338: }
1339:
1340: // ブレークポイント一覧 (モニタ)
1341: void
1.1.1.9 root 1342: Debugger::MonitorUpdateBpoint(Monitor *, TextScreen& monitor)
1.1.1.4 root 1343: {
1344: // 0 1 2 3 4 5 6
1345: // 0123456789012345678901234567890123456789012345678901234567890
1346: // No Type Parameter Matched Skip
1347: // #0 addr $01234567 123456789 123456789/123456789
1348: // #1 inst 00000000/00000000
1349: // #2 excp $00-$00
1350:
1351: monitor.Clear();
1352: monitor.Print(0, 0, "No Type Parameter");
1353: monitor.Print(26, 0, "Matched");
1354: monitor.Print(37, 0, "Skip");
1.1 root 1355:
1.1.1.3 root 1356: for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 root 1357: const auto& bp = bpoint[i];
1358: int y = i + 1;
1359:
1360: monitor.Print(0, y, "#%d", i);
1361:
1362: // 種別ごとの表示
1363: switch (bp.type) {
1364: case BreakpointType::Unused:
1365: continue;
1366:
1367: case BreakpointType::Address:
1368: monitor.Print(3, y, "addr $%08x", bp.addr);
1369: break;
1370: case BreakpointType::Memory:
1371: monitor.Print(3, y, "mem $%08x", bp.addr);
1372: break;
1373:
1374: case BreakpointType::Exception:
1375: monitor.Print(3, y, "excp $%02x", bp.vec1);
1376: if (bp.vec2 != bp.vec1) {
1377: monitor.Print(11, y, "-$%02x", bp.vec2);
1378: }
1379: break;
1380:
1381: case BreakpointType::Instruction:
1382: monitor.Print(3, y, "inst");
1.1.1.5 root 1383: if (md->inst_bytes == 4) {
1.1.1.4 root 1384: if (bp.mask == 0xffffffff) {
1385: monitor.Print(8, y, "%08x", bp.inst);
1386: } else {
1387: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1388: }
1389: } else {
1390: if (bp.mask == 0xffffffff) {
1391: monitor.Print(8, y, "%08x", bp.inst);
1392: } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) {
1393: monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
1394: } else if (bp.mask == 0xffff0000) {
1395: monitor.Print(8, y, "%04x", bp.inst >> 16);
1396: } else {
1397: monitor.Print(8, y, "%04x:%04x",
1398: bp.inst >> 16, bp.mask >> 16);
1399: }
1400: }
1401: break;
1402:
1403: default:
1404: monitor.Print(3, y, "type=%d", (int)bp.type);
1405: continue;
1406: }
1407:
1408: // マッチ回数
1409: monitor.Print(26, y, "%d", bp.matched);
1410:
1411: // スキップ
1412: if (bp.skip < 0) {
1413: monitor.Print(37, y, "forever");
1414: } else if (bp.skip > 0) {
1415: monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1 root 1416: }
1417: }
1418: }
1419:
1420: // ブレークポイント全削除
1421: void
1422: Debugger::cmd_bx()
1423: {
1.1.1.3 root 1424: for (auto& bp : bpoint) {
1.1.1.4 root 1425: bp.type = BreakpointType::Unused;
1.1 root 1426: }
1427: cons->Print(" All breakpoints disabled\n");
1.1.1.4 root 1428:
1429: // 今登録されている命令ブレークの必要命令長を再計算
1430: RecalcInstMask();
1.1 root 1431: }
1432:
1433: // ブレークポイントを設定。
1.1.1.4 root 1434: // new_bp のうち matched, skipremain はこちらで初期化する。
1435: // それ以外を埋めてから呼ぶこと。
1436: // 設定できればその番号、できなければ -1 を返す。
1.1 root 1437: int
1.1.1.4 root 1438: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1 root 1439: {
1.1.1.3 root 1440: for (int i = 0; i < bpoint.size(); i++) {
1441: auto& bp = bpoint[i];
1.1.1.4 root 1442: if (bp.type == BreakpointType::Unused) {
1443: bp = new_bp;
1444: bp.matched = 0;
1445: if (bp.skip > 0) {
1446: bp.skipremain = bp.skip;
1447: } else {
1448: bp.skipremain = 0;
1449: }
1450:
1.1 root 1451: return i;
1452: }
1453: }
1454: return -1;
1455: }
1456:
1.1.1.4 root 1457: // すべての命令ブレークのマスクのうち最長のものを再計算する。
1458: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
1459: void
1460: Debugger::RecalcInstMask()
1461: {
1462: uint32 mask;
1463: bi_need_bytes = 0;
1464:
1465: // 登録されている命令ブレークの最長マスクを求める
1466: mask = 0;
1467: for (const auto& bp : bpoint) {
1468: if (bp.type == BreakpointType::Instruction) {
1469: mask |= bp.mask;
1470: }
1471: }
1472:
1473: // mask の Number of Trailing Zero を求める。
1474: // (x & -x) で x の最も下の立ってるビットだけを立てる、
1475: // (x & -x) -1 でそれより下の全ビットを立てる、
1476: // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
1477: int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
1478: // 上位側から数えたマスクに必要なビット数
1479: int mlen = 32 - ntz;
1480: // 命令語単位に切り上げる
1.1.1.5 root 1481: mlen = roundup(mlen, md->inst_bytes * 8);
1.1.1.4 root 1482: // バイト数に変換
1483: mlen /= 8;
1484:
1485: if (mlen > bi_need_bytes) {
1486: bi_need_bytes = mlen;
1487: }
1488: }
1489:
1490: // ブランチ履歴、例外履歴表示
1491: // brhist [<maxlines>]
1492: // exhist [<maxlines>]
1493: // <maxlines> は表示する最大エントリ数。
1494: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
1495: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
1496: // する。
1.1 root 1497: void
1498: Debugger::cmd_brhist()
1499: {
1.1.1.9 root 1500: cmd_hist_common(md->GetBrHist());
1.1.1.4 root 1501: }
1502: void
1503: Debugger::cmd_exhist()
1504: {
1.1.1.9 root 1505: cmd_hist_common(md->GetExHist());
1.1.1.4 root 1506: }
1.1.1.3 root 1507:
1.1.1.4 root 1508: // ブランチ履歴、例外履歴表示の共通部分。
1509: void
1.1.1.9 root 1510: Debugger::cmd_hist_common(BranchHistory& hist)
1.1.1.4 root 1511: {
1512: // 表示最大行数(と向き)
1513: // 向きは bottom_to_top = true が新しいほうを下とする方向。
1514: int maxlines = 20;
1515: bool bottom_to_top = true;
1.1.1.3 root 1516: if (args.size() > 1) {
1.1.1.4 root 1517: maxlines = atoi(args[1].c_str());
1518: if (maxlines < 0) {
1519: bottom_to_top = false;
1520: maxlines = -maxlines;
1.1.1.3 root 1521: }
1.1.1.4 root 1522:
1523: if (maxlines < 1)
1524: maxlines = 1;
1525: if (maxlines > 256)
1526: maxlines = 256;
1527: }
1528:
1529: // コンソールでは空エントリの行は表示したくないので、
1530: // 先にエントリ数を調べる。
1531: int used = hist.GetUsed();
1532:
1533: // 表示行数 + 1行はヘッダ分
1534: int lines = std::min(used, maxlines) + 1;
1535:
1536: // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
1.1.1.9 root 1537: auto& histmon = hist.monitor;
1538: auto size = histmon.GetSize();
1539: TextScreen screen;
1540: screen.Init(size.width, lines);
1.1.1.4 root 1541:
1542: // コンソールでは下が新しいの順のほうがいい
1543: if (bottom_to_top) {
1.1.1.9 root 1544: screen.userdata |= BranchHistory::BottomToTop;
1.1.1.3 root 1545: }
1.1.1.4 root 1546:
1547: // 表示
1.1.1.9 root 1548: MONITOR_UPDATE(histmon, screen);
1549: ShowTextScreen(screen);
1.1 root 1550: }
1551:
1552: // 実行再開(continue): c [<addr>]
1553: // <addr> 指定があれば <addr> まで実行。
1554: void
1555: Debugger::cmd_c()
1556: {
1.1.1.3 root 1557: if (args.size() > 1) {
1.1 root 1558: // 引数があれば
1.1.1.3 root 1559: if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1 root 1560: return;
1561: }
1562: // 偶数番地に丸める
1563: bc_addr &= 0xfffffffe;
1564:
1565: bc_enable = true;
1566: }
1567: }
1568:
1.1.1.5 root 1569: // 引数などからアドレスを取得するごった煮共通ルーチン。
1570: //
1.1.1.6 root 1571: // 引数がなければ sa->addr には書き込まずに true で帰る (ので、呼び出し側が
1572: // 事前に適切なデフォルト値をセットしておくと、それがそのまま使われる)。
1.1.1.3 root 1573: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1.1.6 root 1574: // アドレスには空間修飾子を前置可能、省略の場合はいずれも現在値を使う。
1575: // エラーならメッセージを表示して false を返す。
1.1.1.5 root 1576: // sa は in/out パラメータ。
1.1 root 1577: bool
1.1.1.6 root 1578: Debugger::GetAddr(saddr_t *sa, bool default_data)
1.1 root 1579: {
1.1.1.5 root 1580: if (args.size() < 2) {
1.1.1.6 root 1581: // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。
1.1.1.5 root 1582: return true;
1583: }
1584:
1.1.1.6 root 1585: // 引数ありならパースする。アドレスが明示的に指定されたので、
1586: // ここから先の省略箇所は前回値ではなくデフォルト値で補完する。
1587:
1.1.1.5 root 1588: std::string& str = args[1];
1589: uint32 addr;
1590:
1.1.1.6 root 1591: // ':' があればその前はアドレス空間修飾子
1592: int mod_super = -1; // 'u'/'s'
1593: int mod_prog = -1; // 'd'/'p' or 'd'/'i'
1594: int mod_num = -1; // '0'..'7'
1595: auto addrpos = str.find(':');
1596: if (addrpos == std::string::npos) {
1597: // なければ先頭からアドレス
1598: addrpos = 0;
1599: } else {
1600: // ':' が途中にあれば、その前が修飾子。
1601: // ':' が先頭なら修飾子が空文字列という扱いでいいか。
1602: std::string mod = str.substr(0, addrpos);
1603: addrpos++;
1604: for (auto ch : mod) {
1605: if (ch == 'u') {
1606: if (mod_super == 1)
1607: goto error;
1608: mod_super = 0;
1609: } else if (ch == 's') {
1610: if (mod_super == 0)
1611: goto error;
1612: mod_super = 1;
1613: } else if (ch == 'd') {
1614: if (mod_prog == 1)
1615: goto error;
1616: mod_prog = 0;
1617: } else if (ch == 'p' || ch == 'i') {
1618: // m68k では 'P'rogram space、m88k では 'I'nstruction CMMU…
1619: if (mod_prog == 0)
1620: goto error;
1621: mod_prog = 1;
1622: } else if ('0' <= ch && ch <= '7') {
1623: int num = ch - '0';
1624: if (md->arch == Arch::M680x0) {
1625: // m68k では FC 指定は Super/User 指定を含んでおり、
1626: // 値指定は他の修飾子とは同時に指定できない。
1627: if (mod_super >= 0 || mod_prog >= 0 ||
1628: (mod_num >= 0 && mod_num != num)) {
1629: goto error;
1630: }
1631: // 有効な値は 1,2,5,6 のみ
1632: if (num == 0 || num == 3 || num == 4 || num == 7) {
1633: cons->Print("%c: invalid address space\n", ch);
1634: }
1635: mod_num = num;
1636: mod_super = mod_num >> 2; // FC2
1637: mod_prog = (mod_num & 3) - 1; // FC1,FC0
1638: } else if (md->arch == Arch::M88xx0) {
1639: // m88k では CMMU 指定と、Super/User 指定は独立。
1640: if (mod_prog >= 0 ||
1641: (mod_num >= 0 && mod_num != num)) {
1642: goto error;
1643: }
1644: mod_num = num;
1645: mod_prog = (mod_num & 1); // CMMU7 が Inst
1646: }
1647: } else {
1648: cons->Print("%c: unknown address space modifier\n", ch);
1649: return false;
1650: }
1651: continue;
1652:
1653: error:
1654: cons->Print("%s: address space modifier '%c' conflicts\n",
1655: mod.c_str(), ch);
1656: return false;
1657: }
1658:
1659: // 数値指定を機種ごとに読み替える
1660: if (mod_num >= 0) {
1661: if (md->arch == Arch::M680x0) {
1662: // m68k なら値指定で全部確定する
1663: // "1:" -> User/Data
1664: // "2:" -> User/Program
1665: // "5:" -> Super/Data
1666: // "6:" -> Super/Program
1667: mod_super = (mod_num & 4) ? true : false;
1668: mod_prog = (mod_num & 3) - 1;
1669: } else if (md->arch == Arch::M88xx0) {
1670: // m88k では mod_num を CMMU ID とする(?)。
1671: // XXX まだ CPU は1つしかないので雑に処理。
1672: // "6" -> Data (CPU#0)
1673: // "7" -> Program (CPU#0)
1674: if (mod_num < 6) {
1675: cons->Print("%d: CMMU#%d not installed\n",
1676: mod_num, mod_num);
1677: return false;
1678: }
1679: mod_prog = mod_num - 6;
1680: }
1681: }
1.1 root 1682: }
1.1.1.5 root 1683:
1.1.1.6 root 1684: // アドレス空間修飾子のうち省略箇所はデフォルト状態で補完
1685: // - 's'/'u' いずれもなければ現在値。
1686: // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。
1687: if (mod_super < 0) {
1688: mod_super = md->IsSuper();
1689: }
1690: if (mod_prog < 0) {
1691: mod_prog = !default_data;
1692: }
1693: sa->SetSuper(mod_super);
1694: sa->SetData(!mod_prog);
1695:
1.1.1.5 root 1696: // アドレス
1697: if (ParseAddr(str.c_str() + addrpos, &addr) == false) {
1698: return false;
1699: }
1.1.1.7 root 1700: // 命令境界に丸める
1701: sa->SetAddr(addr & ~(md->inst_bytes - 1));
1.1.1.5 root 1702:
1.1 root 1703: return true;
1704: }
1705:
1.1.1.5 root 1706: // 逆アセンブル: d [[SU:]<addr> [<cnt>]] (論理、テーブルサーチなし)
1.1 root 1707: void
1708: Debugger::cmd_d()
1709: {
1.1.1.6 root 1710: cmd_d_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1711: }
1712:
1.1.1.5 root 1713: // 逆アセンブル: dt [[SU:]<addr>] [<cnt>]] (論理、テーブルサーチあり)
1.1 root 1714: void
1715: Debugger::cmd_dt()
1716: {
1.1.1.6 root 1717: cmd_d_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1718: }
1719:
1720: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
1721: void
1722: Debugger::cmd_D()
1723: {
1.1.1.6 root 1724: cmd_d_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1725: }
1726:
1727: // 逆アセンブル共通
1728: void
1.1.1.6 root 1729: Debugger::cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1730: {
1.1.1.2 root 1731: saddr_t saddr;
1.1 root 1732: int cnt;
1733:
1734: cnt = 10;
1735:
1736: // 開始アドレス
1.1.1.6 root 1737: saddr = d_last_addr;
1738: if (GetAddr(&saddr, false) == false) {
1.1 root 1739: return;
1740: }
1741: // 2つ目の引数があれば行数
1.1.1.3 root 1742: if (args.size() > 2) {
1743: cnt = strtol(args[2].c_str(), NULL, 10);
1.1 root 1744: }
1745:
1.1.1.6 root 1746: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1747:
1.1 root 1748: for (int i = 0; i < cnt; i++) {
1.1.1.2 root 1749: std::string addrbuf;
1750: std::string mnemonic;
1751: std::string mnembuf;
1752: std::vector<uint8> local_ir;
1753:
1.1.1.6 root 1754: // アドレス
1755: if (mem.FormatAddr(addrbuf) == false) {
1756: // アドレス変換でバスエラーならここで終了
1757: cons->Print("%s\n", addrbuf.c_str());
1758: break;
1.1 root 1759: }
1.1.1.6 root 1760:
1761: // 逆アセンブル
1762: md->Disassemble(mem, mnemonic, local_ir);
1763: // オフライン用に整形
1764: mnembuf = md->FormatDisasm(mnemonic, local_ir);
1765:
1.1.1.2 root 1766: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1767:
1768: // 変換できなければここで終了
1769: if (local_ir.size() == 0)
1770: break;
1.1 root 1771: }
1.1.1.2 root 1772:
1.1.1.6 root 1773: // アドレスと Super/User 状態を次回継続用に保存
1774: d_last_addr = mem.laddr;
1.1 root 1775: }
1776:
1.1.1.5 root 1777: // 表示レジスタ選択
1778: // disp 現在の内容を表示
1779: // disp <regs>... 設定
1780: void
1781: Debugger::cmd_disp()
1782: {
1783: if (args.size() < 2) {
1784: // 表示
1785: bool first = true;
1786: for (const auto& r : disp_regs) {
1787: cons->Print("%s%s", (first ? "" : ","), r.c_str());
1788: first = false;
1789: }
1790: cons->Print("\n");
1791: return;
1792: }
1793:
1794: // 設定 (引数を分解する)
1795: char buf[256];
1796: char *p;
1797: char *last;
1798: strlcpy(buf, args[1].c_str(), sizeof(buf));
1799: disp_regs.clear();
1800: for (p = strtok_r(buf, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
1801: disp_regs.push_back(std::string(p));
1802: }
1803:
1804: // XXX チェック
1805: }
1806:
1.1.1.9 root 1807: // ログレベル設定: L <name>[=<level>][...]
1.1 root 1808: void
1809: Debugger::cmd_L()
1810: {
1.1.1.9 root 1811: if (args.size() < 2) {
1812: cons->Print("L <name1>[=<level1>][,<name2>[=<level2>]]...\n");
1.1 root 1813: return;
1814: }
1815:
1.1.1.9 root 1816: // 一旦全部つなげる。
1817: // help が混ざる場合の "L foo=1,help" と "L foo=1 help" を同じ動作に
1818: // するため。
1819: std::string str;
1820: for (int i = 1, ac = args.size(); i < ac; i++) {
1821: if (str.empty() == false) {
1822: str += ',';
1823: }
1824: str += args[i];
1825: }
1826:
1827: // で、再分解
1828: std::vector<std::string> items = string_split(str, ',');
1829:
1830: // "help" があれば一覧を表示
1831: for (const auto& item : items) {
1832: if (item == "help") {
1833: std::vector<std::string> list = MainApp::GetLogNames();
1834: for (const auto& name : list) {
1835: cons->Print(" %s\n", name.c_str());
1836: }
1837: return;
1838: }
1839: }
1.1 root 1840:
1.1.1.9 root 1841: // ログレベルを設定
1842: std::string errmsg;
1843: if (MainApp::SetLogopt(items, &errmsg) == false) {
1844: cons->Print("%s\n", errmsg.c_str());
1845: return;
1.1 root 1846: }
1847: }
1848:
1849: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
1850: void
1851: Debugger::cmd_m()
1852: {
1.1.1.6 root 1853: cmd_m_common(MemoryMode::Logical, MMULookupMode::False);
1.1 root 1854: }
1855:
1856: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
1857: void
1858: Debugger::cmd_mt()
1859: {
1.1.1.6 root 1860: cmd_m_common(MemoryMode::Logical, MMULookupMode::True);
1.1 root 1861: }
1862:
1863: // メモリダンプ: M [<addr> [<cnt>]] (物理)
1864: void
1865: Debugger::cmd_M()
1866: {
1.1.1.6 root 1867: cmd_m_common(MemoryMode::Physical, MMULookupMode::None);
1.1 root 1868: }
1869:
1870: // メモリダンプ共通
1871: void
1.1.1.6 root 1872: Debugger::cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode)
1.1 root 1873: {
1.1.1.2 root 1874: saddr_t saddr;
1.1.1.9 root 1875: int row;
1.1 root 1876:
1.1.1.9 root 1877: row = 8;
1.1 root 1878:
1879: // 開始アドレス
1.1.1.6 root 1880: saddr = m_last_addr;
1881: if (GetAddr(&saddr, true) == false) {
1.1 root 1882: return;
1883: }
1884: // 2つ目の引数があれば行数
1.1.1.3 root 1885: if (args.size() > 2) {
1.1.1.9 root 1886: row = strtol(args[2].c_str(), NULL, 10);
1887: }
1888:
1889: // userdata を作成
1890: uint64 userdata = saddr.GetAddr();
1891: if (saddr.IsSuper() == false) {
1892: userdata |= (1ULL << 32);
1893: }
1894: if (saddr.IsData()) {
1895: userdata |= (1ULL << 33);
1.1 root 1896: }
1.1.1.9 root 1897: if (access_mode == MemoryMode::Logical) {
1898: userdata |= (1ULL << 34);
1899: }
1900: if (lookup_mode == MMULookupMode::True) {
1901: userdata |= (1ULL << 35);
1902: }
1903: // CLI では毎回次を表示していってほしい
1904: userdata |= (1ULL << 36);
1905:
1906: TextScreen screen;
1907: screen.Init(m_monitor.GetSize().width, row);
1908: screen.userdata = userdata;
1909:
1910: MONITOR_UPDATE(m_monitor, screen);
1911: ShowTextScreen(screen);
1912:
1913: // アドレスを次回継続用に保存 (Super/Data は維持)
1914: m_last_addr = (uint32)screen.userdata;
1915: }
1916:
1917: // メモリダンプモニタ
1918: //
1919: // userdata の下位32bit はアドレス。上位は各種フラグ。
1920: //
1921: // ..| b36 | b35 | b34 | b33 | b32 | b31 .. b0
1922: // userdata | A | T | L | D | U | address
1923: // | | | | +---- 1:User 0:Supervisor space
1924: // | | | +---------- 1:Data, 0:Instruction space
1925: // | | +---------------- 1:Logical, 0:Physical
1926: // | +---------------------- 1:Table Lookup, 0:ATC Only
1927: // +---------------------------- 1:Auto increment mode
1928: void
1929: Debugger::MonitorUpdateMemdump(Monitor *, TextScreen& screen)
1930: {
1931: uint32 laddr = (uint32)screen.userdata;
1932: bool user = (screen.userdata >> 32) & 1;
1933: bool data = (screen.userdata >> 33) & 1;
1934: MemoryMode access_mode = ((screen.userdata >> 34) & 1)
1935: ? MemoryMode::Logical : MemoryMode::Physical;
1936: MMULookupMode lookup_mode = ((screen.userdata >> 35) & 1)
1937: ? MMULookupMode::True : MMULookupMode::None;
1938: bool autoinc = ((screen.userdata >> 36) & 1);
1.1 root 1939:
1.1.1.9 root 1940: saddr_t saddr(laddr, !user, data);
1.1.1.6 root 1941: DebuggerMemoryStream mem(md, saddr, access_mode, lookup_mode);
1.1.1.2 root 1942:
1.1.1.9 root 1943: screen.Clear();
1944:
1945: int row = screen.GetRow();
1946: for (int y = 0; y < row; y++) {
1.1.1.2 root 1947: std::string addrbuf;
1948: std::string dumpbuf;
1949: std::string charbuf;
1950:
1.1.1.6 root 1951: // アドレス
1.1.1.9 root 1952: bool ok = mem.FormatAddr(addrbuf);
1953: screen.Print(0, y, "%s", addrbuf.c_str());
1954: if (ok == false) {
1.1.1.6 root 1955: // アドレス変換でバスエラーならここで終了
1.1.1.2 root 1956: break;
1957: }
1958:
1959: for (int j = 0; j < 8; j++) {
1960: for (int k = 0; k < 2; k++) {
1.1.1.6 root 1961: uint64 b = mem.Fetch8();
1.1.1.2 root 1962:
1963: if ((int64)b < 0) {
1964: dumpbuf += "--";
1.1.1.9 root 1965: charbuf += ' ';
1.1.1.2 root 1966: } else {
1967: uint32 c = (uint32)b;
1968: dumpbuf += string_format("%02x", c);
1969: charbuf += string_format("%c",
1970: (0x20 <= c && c <= 0x7e) ? c : '.');
1971: }
1972: }
1.1.1.9 root 1973: dumpbuf += ' ';
1.1.1.2 root 1974: }
1.1.1.6 root 1975:
1.1.1.9 root 1976: screen.Print(19, y, "%s", dumpbuf.c_str());
1977: screen.Print(60, y, "%s", charbuf.c_str());
1.1 root 1978: }
1.1.1.6 root 1979:
1.1.1.9 root 1980: // 次回継続用に書き戻す
1981: if (autoinc) {
1982: union64 udata;
1983: udata.q = screen.userdata;
1984: udata.l = mem.laddr.GetAddr();
1985: screen.userdata = udata.q;
1986: }
1.1 root 1987: }
1988:
1989: // プロンプトに来た最初に表示するいつものやつを再表示する
1990: void
1991: Debugger::cmd_minus()
1992: {
1.1.1.5 root 1993: // 指定のレジスタ群を表示
1994: // disp_regs はレジスタ群のリスト { "r", "rf" } のような感じ。
1995: // 一方 ShowRegisters() の第2引数は1つのコマンドラインを空白で区切った
1996: // 文字列リスト { arg[0], arg[1], .. }。型が同じで意味が違うので注意。
1997: for (const auto& reg : disp_regs) {
1998: std::vector<std::string> tmpargs;
1999: tmpargs.push_back(reg);
2000: md->ShowRegister(cons, tmpargs);
2001: }
1.1 root 2002:
1.1.1.2 root 2003: // 現在の PC の位置を逆アセンブル。ここで ir を更新。
2004: std::string addrbuf;
2005: std::string mnemonic;
2006: std::string mnembuf;
2007: ir.clear();
1.1.1.6 root 2008:
2009: // 論理アクセス時、ATC ミスが分かったほうが便利だと思うので lookup なし
2010: saddr_t laddr(pc, md->IsSuper());
2011: DebuggerMemoryStream mem(md, laddr);
2012: // アドレス
2013: if (mem.FormatAddr(addrbuf) == false) {
2014: cons->Print("%s\n", addrbuf.c_str());
2015: return;
1.1.1.2 root 2016: }
1.1.1.6 root 2017: // 逆アセンブル
2018: md->Disassemble(mem, mnemonic, ir);
2019: // ライブ表示用に整形
2020: mnembuf = md->FormatDisasmLive(mnemonic, ir);
2021:
1.1.1.2 root 2022: cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
1.1 root 2023: }
2024:
1.1.1.5 root 2025: // サブルーチンとループを飛ばしながら count 命令ステップ実行
2026: // n [<count>?:1] (途中レジスタを表示しない)
1.1 root 2027: void
2028: Debugger::cmd_n()
2029: {
1.1.1.5 root 2030: cmd_n_common(false);
2031: }
2032:
2033: // サブルーチンとループを飛ばしながら count 命令ステップ実行
2034: // nt [<count>?:1] (1命令ずつレジスタを表示する)
2035: void
2036: Debugger::cmd_nt()
2037: {
2038: cmd_n_common(true);
2039: }
2040:
2041: // n と nt の共通部分
2042: void
2043: Debugger::cmd_n_common(bool trace)
2044: {
2045: t_enable = trace;
2046:
1.1.1.3 root 2047: if (args.size() > 1) {
1.1 root 2048: int count;
1.1.1.3 root 2049: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 2050: if (count < 1) {
2051: cons->Print(" invalid step count: %d\n", count);
2052: return;
2053: }
2054:
2055: n_count = count;
2056: } else {
2057: n_count = 1;
2058: }
2059: n_enable = true;
2060: SetNBreakpoint();
2061: }
2062:
1.1.1.5 root 2063: // n コマンド用のブレークポイントを設定する。
2064: // この命令語によって仕掛けるブレークポイントが変わるので、都度都度
2065: // 呼び出すこと。
2066: void
1.1 root 2067: Debugger::SetNBreakpoint()
2068: {
1.1.1.6 root 2069: saddr_t laddr(md->GetPC(), md->IsSuper());
2070: DebuggerMemoryStream mem(md, laddr);
1.1.1.5 root 2071:
1.1.1.6 root 2072: // 命令がサブルーチンやトラップなどステップインできる命令か。
2073: if (md->IsOpStepIn(mem)) {
1.1.1.5 root 2074: // この次の命令にブレークをかける
2075: if (md->inst_bytes_fixed != 0) {
2076: // 固定長命令
1.1.1.6 root 2077: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 2078: } else {
2079: // 可変長なら逆アセンブルしてみるしか
2080: std::string mnemonic;
2081: std::vector<uint8> v;
2082: // XXX 失敗したらどうすべ
1.1.1.6 root 2083: mem.ResetAddr(md->GetPC());
2084: md->Disassemble(mem, mnemonic, v);
2085: n_breakaddr = (uint32)mem.laddr;
1.1.1.5 root 2086: }
1.1 root 2087: } else {
1.1.1.5 root 2088: // そうでなければ1命令進める。
2089: // 0xffffffff は常にアドレスとして不正なのでこれをフラグに使う。
2090: n_breakaddr = 0xffffffff;
1.1 root 2091: }
2092: }
2093:
1.1.1.2 root 2094: // デバッガ終了コマンド
2095: void
2096: Debugger::cmd_q()
2097: {
2098: cons->Print("quit debugger console.\n");
1.1 root 2099: }
2100:
1.1.1.5 root 2101: // VM リセット
2102: void
2103: Debugger::cmd_reset()
2104: {
2105: gScheduler->RequestResetHard();
2106: }
2107:
1.1 root 2108: // モニター表示
2109: void
2110: Debugger::cmd_show()
2111: {
1.1.1.3 root 2112: if (args.size() != 2) {
1.1.1.9 root 2113: std::vector<int> list;
2114:
2115: // 登録されているモニターだけを列挙
2116: // (登録されていてもサブウィンドウの ID を持っているものは除外)
2117: for (const auto mon : gMonitorManager.GetList()) {
2118: int id = mon->GetId();
2119: if (id <= ID_MONITOR_END) {
2120: list.push_back(id);
1.1 root 2121: }
2122: }
1.1.1.9 root 2123:
2124: // 一覧を表示
2125: std::string msg = MonitorManager::MakeListString(list);
2126: cons->Print("usage: show <monitor>\n");
2127: cons->Print("%s", msg.c_str());
1.1 root 2128: return;
2129: }
2130:
1.1.1.9 root 2131: std::vector<Monitor *> candidates;
2132: std::vector<std::string> cand_names;
1.1.1.3 root 2133: std::string name = args[1];
1.1.1.9 root 2134:
2135: for (auto mon : gMonitorManager.GetList()) {
2136: int id = mon->GetId();
2137: const auto& aliases = gMonitorManager.GetAliases(id);
2138:
2139: bool matched = false;
2140: for (const auto& alias : aliases) {
2141: // 部分一致したら名前はすべて覚えておく
2142: if (starts_with_ignorecase(alias, name)) {
2143: cand_names.push_back(alias);
2144: matched = true;
2145: }
2146: }
2147:
2148: // 同じモニタで別名が複数回部分一致しても、
2149: // モニタのほうは1回として数える
2150: if (matched) {
2151: candidates.push_back(mon);
2152: }
2153: }
2154:
2155: if (candidates.empty()) {
2156: // 一致しない
2157: cons->Print("unknown monitor name: \"%s\"\n", name.c_str());
2158: } else if (candidates.size() == 1) {
2159: // 確定した
2160: Monitor& mon = *(candidates[0]);
2161: ShowMonitor(mon);
2162: } else {
2163: // 候補が複数あった
2164: std::string candstr;
2165: for (const auto& cname : cand_names) {
2166: candstr += ' ';
2167: candstr += cname;
1.1 root 2168: }
1.1.1.9 root 2169: cons->Print("ambiguous monitor name \"%s\": candidates are%s\n",
2170: name.c_str(), candstr.c_str());
1.1 root 2171: }
2172: }
2173:
1.1.1.3 root 2174: // モニターを更新して表示。
2175: void
1.1.1.9 root 2176: Debugger::ShowMonitor(Monitor& monitor)
1.1.1.3 root 2177: {
2178: // モニタを更新
1.1.1.4 root 2179: TextScreen ts;
2180:
1.1.1.9 root 2181: auto size = monitor.GetSize();
1.1.1.4 root 2182: ts.Init(size.width, size.height);
2183:
1.1.1.9 root 2184: MONITOR_UPDATE(monitor, ts);
1.1.1.4 root 2185: ShowTextScreen(ts);
1.1.1.3 root 2186: }
2187:
2188: // テキストスクリーンを表示。
1.1 root 2189: void
1.1.1.3 root 2190: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 2191: {
2192: char sbuf[1024]; // 適当
2193:
2194: // monitor 内部バッファから ShiftJIS バッファを作成。
2195: // その際属性もエスケープシーケンスで再現する。
2196: int col = monitor.GetCol();
2197: int row = monitor.GetRow();
1.1.1.8 root 2198: const std::vector<uint16>& src = monitor.GetBuf();
1.1 root 2199:
2200: for (int y = 0; y < row; y++) {
2201: int sy = y * col;
2202: int sx;
2203: uint attr;
2204: char *d;
2205:
2206: memset(sbuf, 0, sizeof(sbuf));
2207: d = sbuf;
2208: attr = TA::Normal;
2209: for (sx = 0; sx < col; sx++) {
2210: uint a = src[sy + sx] & 0xff00;
2211: uint ch = src[sy + sx] & 0x00ff;
2212:
2213: // 属性の変わり目
2214: if (attr != a) {
2215: switch (a) {
2216: case TA::Normal:
2217: case TA::Off:
2218: *d++ = 0x1b;
2219: *d++ = '[';
2220: *d++ = 'm';
2221: break;
2222: case TA::On:
2223: *d++ = 0x1b;
2224: *d++ = '[';
2225: *d++ = '7';
2226: *d++ = 'm';
2227: break;
2228: case TA::Disable:
2229: *d++ = 0x1b;
2230: *d++ = '[';
2231: *d++ = '2';
2232: *d++ = 'm';
2233: break;
2234: case TA::Em:
2235: *d++ = 0x1b;
2236: *d++ = '[';
2237: *d++ = '1';
2238: *d++ = 'm';
2239: break;
2240: }
2241: attr = a;
2242: }
2243: *d++ = ch;
2244: }
1.1.1.3 root 2245: // 行の終わりで一旦属性を戻しておく
2246: if (attr != TA::Normal) {
2247: *d++ = 0x1b;
2248: *d++ = '[';
2249: *d++ = 'm';
2250: }
1.1 root 2251: *d = '\0';
2252:
2253: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
2254:
2255: cons->Print("%s\n", sbuf);
2256: }
2257: }
2258:
1.1.1.5 root 2259: // ステップ実行
2260: // s [<count>?:1] (途中レジスタを表示しない)
1.1 root 2261: void
2262: Debugger::cmd_s()
2263: {
1.1.1.5 root 2264: cmd_s_common(false);
2265: }
2266:
2267: // ステップ実行
2268: // st [<count>?:1] (命令ごとにレジスタを表示する)
2269: void
2270: Debugger::cmd_st()
2271: {
2272: cmd_s_common(true);
2273: }
2274:
2275: // ステップ実行共通部分
2276: void
2277: Debugger::cmd_s_common(bool trace)
2278: {
2279: t_enable = trace;
2280:
1.1.1.3 root 2281: if (args.size() > 1) {
1.1 root 2282: int count;
1.1.1.3 root 2283: count = strtol(args[1].c_str(), NULL, 10);
1.1 root 2284: if (count < 1) {
2285: cons->Print(" invalid step count: %d\n", count);
2286: return;
2287: }
2288:
2289: s_count = count;
2290: } else {
2291: s_count = 1;
2292: }
2293: s_enable = true;
2294: }
2295:
1.1.1.5 root 2296: // ステップアウト (途中レジスタを表示しない)
1.1 root 2297: void
2298: Debugger::cmd_so()
2299: {
1.1.1.5 root 2300: cmd_so_common(false);
2301: }
2302:
2303: // ステップアウト (命令ごとにレジスタを表示する)
2304: void
2305: Debugger::cmd_sot()
2306: {
2307: cmd_so_common(true);
2308: }
2309:
2310: // ステップアウトの共通部分
2311: void
2312: Debugger::cmd_so_common(bool trace)
2313: {
2314: t_enable = trace;
2315:
1.1 root 2316: so_enable = true;
1.1.1.2 root 2317: md->SetStepOut();
1.1 root 2318: }
2319:
1.1.1.5 root 2320: // t は st の省略形。互換のため。
1.1 root 2321: void
2322: Debugger::cmd_t()
2323: {
1.1.1.5 root 2324: cmd_st();
1.1 root 2325: }
2326:
1.1.1.4 root 2327: // 引数で示されるプレフィックスなしの16進数値を返す。
2328: // 正しく変換できれば値を valp に格納して true を返す。
2329: // そうでなければ false を返す。
2330: bool
2331: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
2332: {
2333: uint32 val;
2334: char *end;
2335:
2336: errno = 0;
2337: val = strtoul(arg, &end, 16);
2338: if (arg[0] == '\0' || end[0] != '\0') {
2339: return false;
2340: }
2341: if (errno == ERANGE) {
2342: return false;
2343: }
2344:
2345: *valp = val;
2346: return true;
2347: }
2348:
1.1 root 2349: // 引数で示されるアドレスを返す。
2350: // '%' で始まればレジスタ。
2351: // そうでなければ16進数値。
2352: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
2353: // そうでなければエラーメッセージを表示して偽を返す。
2354: bool
1.1.1.4 root 2355: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1 root 2356: {
1.1.1.4 root 2357: uint32 addr;
1.1 root 2358: char *end;
2359:
2360: if (arg[0] == '%') {
1.1.1.2 root 2361: // '%' から始まればレジスタ
2362: uint64 r;
2363: r = md->GetRegAddr(arg + 1);
2364: if ((int64)r < 0) {
2365: #if 0 // not yet
1.1 root 2366: cons->Print("valid register name are: %s%s\n",
2367: "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
2368: ",%msp,%vbr,%srp,%crp");
1.1.1.2 root 2369: #endif
2370: cons->Print("invalid register name\n");
1.1 root 2371: return false;
2372: }
1.1.1.2 root 2373: addr = (uint32)r;
1.1 root 2374: } else {
2375: /* そうでなければ番地指定 */
2376: errno = 0;
2377: addr = strtoul(arg, &end, 16);
2378: if (arg[0] == '\0' || end[0] != '\0') {
2379: cons->Print("invalid address: '%s'\n", arg);
2380: return false;
2381: }
2382: if (errno == ERANGE) {
2383: cons->Print("out of range: %s\n", arg);
2384: return false;
2385: }
2386: }
2387:
2388: *addrp = addr;
2389: return true;
2390: }
2391:
1.1.1.4 root 2392:
2393: //
1.1.1.5 root 2394: // MD
2395: //
2396:
1.1.1.6 root 2397: // デストラクタ
2398: DebuggerMD::~DebuggerMD()
2399: {
1.1.1.5 root 2400: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.