|
|
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.11 root 7: //
8: // デバッガ
9: //
10:
11: //
12: // VM スレッド デバッガスレッド HostCOM
13: // condvar
14: // | | |<--- 入力
15: // | |<----------------------|
16: // | | RxCallback
17: // | |
18: // | |---------------------->|
19: // | | HostCOMDevice::Tx() |---> 出力
20:
21: // |<---------------------------|
22: // | is_prompt = true; | デバッガスレッドからプロンプトを出したい
1.1.1.13 root 23: // | Message(MPU_TRACE_ALL); | 場合は MPU (VM) をトレースモードにする。
1.1.1.11 root 24: // | | その際 is_prompt を立てておくことで止まる。
25: // | |
26: // |--------------------------->|
27: // | condvar REQUEST_PROMPT | VM スレッドからプロンプトを出したい場合
28: // | | (上述の例も含む) は条件変数で通知。
29: // | |
30: // |<---------------------------|
31: // | condvar prompt_released | プロンプトを出している間 VM スレッドは
32: // | | 条件変数で待機しているので、これを起こす
33: // | | ことで実行再開。
34:
35: #include "debugger.h"
1.1.1.13 root 36: #include "debugger_hd64180.h"
1.1.1.2 root 37: #include "debugger_m680x0.h"
38: #include "debugger_m88xx0.h"
1.1.1.20! root 39: #include "event.h"
1.1.1.11 root 40: #include "hostcom.h"
1.1.1.2 root 41: #include "mainapp.h"
1.1.1.13 root 42: #include "memdump.h"
1.1.1.9 root 43: #include "mystring.h"
1.1.1.11 root 44: #include "power.h"
1.1.1.7 root 45: #include "scheduler.h"
1.1.1.13 root 46: #include "syncer.h"
1.1.1.11 root 47: #include "uimessage.h"
48: #include "vectortable.h"
1.1.1.3 root 49: #include <algorithm>
1.1.1.12 root 50: #include <cmath>
1.1.1.13 root 51: #include <map>
1.1.1.11 root 52: #if defined(HAVE_BSD_STDIO_H)
53: #include <bsd/stdio.h>
54: #endif
1.1 root 55:
1.1.1.11 root 56: static int readfunc(void *, char *, int);
57: static int writefunc(void *, const char *, int);
1.1 root 58:
59: // コンストラクタ
60: Debugger::Debugger()
1.1.1.13 root 61: : inherited(OBJ_DEBUGGER)
1.1 root 62: {
1.1.1.11 root 63: // ベクタテーブル
64: pVectorTable.reset(new VectorTable(gMainApp.GetVMType()));
1.1.1.9 root 65:
1.1.1.13 root 66: // ブレークポイントモニタ
1.1.1.17 root 67: bpoint_monitor = gMonitorManager->Regist(ID_MONITOR_BREAKPOINT, this);
68: bpoint_monitor->func = ToMonitorCallback(&Debugger::MonitorUpdateBpoint);
1.1.1.20! root 69: bpoint_monitor->SetSize(59, 9);
1.1.1.9 root 70:
1.1.1.13 root 71: // メモリダンプモニタ
1.1.1.9 root 72: for (int i = 0, end = memdump_monitor.size(); i < end; i++) {
1.1.1.16 root 73: uint objid = OBJ_MPU_MEMDUMP(i);
1.1.1.13 root 74: int monid = ID_MONITOR_MEMDUMP(i);
75: memdump_monitor[i].reset(new MemdumpMonitor(objid, monid));
76: }
77:
78: if (gMainApp.Has(VMCap::LUNA)) {
79: // XP 空間のメモリダンプモニタ
80: for (int i = 0, end = xpmemdump_monitor.size(); i < end; i++) {
1.1.1.16 root 81: uint objid = OBJ_XP_MEMDUMP(i);
1.1.1.13 root 82: int monid = ID_MONITOR_XPMEMDUMP(i);
83: xpmemdump_monitor[i].reset(new MemdumpMonitor(objid, monid));
84: }
85: }
1.1 root 86: }
87:
1.1.1.11 root 88: // デストラクタ
89: Debugger::~Debugger()
90: {
91: // fclose(fflush) にあたり hostcom へのアクセスが発生するので、
92: // hostcom より先に片付けておかなければならない。
93: Close();
94:
95: if ((bool)hostcom) {
1.1.1.19 root 96: hostcom->ResetRxCallback();
1.1.1.14 root 97: hostcom->SetAcceptCallback(NULL);
1.1.1.11 root 98: }
99:
100: TerminateThread();
101: }
102:
103: bool
104: Debugger::Create()
105: {
106: // ホストドライバを作成
1.1.1.18 root 107: try {
1.1.1.19 root 108: hostcom.reset(new HostCOMDevice(this, -1, "Debugger"));
1.1.1.18 root 109: } catch (...) { }
1.1.1.14 root 110: if ((bool)hostcom == false) {
1.1.1.18 root 111: warnx("Failed to initialize HostCOMDevice at %s", __method__);
1.1.1.11 root 112: return false;
113: }
114:
1.1.1.19 root 115: hostcom->SetRxCallback(ToDeviceCallback(&Debugger::RxCallback), 0);
1.1.1.11 root 116: hostcom->SetAcceptCallback(ToDeviceCallback(&Debugger::AcceptCallback));
117:
118: return true;
119: }
120:
1.1.1.13 root 121: // ログレベル設定
122: void
123: Debugger::SetLogLevel(int loglevel_)
124: {
125: inherited::SetLogLevel(loglevel_);
126:
127: // ホストドライバを従属させる
128: if ((bool)hostcom) {
129: hostcom->SetLogLevel(loglevel_);
130: }
131: }
132:
1.1.1.11 root 133: // 初期化
134: bool
1.1 root 135: Debugger::Init()
136: {
1.1.1.13 root 137: if (inherited::Init() == false) {
138: return false;
139: }
140:
141: syncer = GetSyncer();
142:
1.1.1.18 root 143: try {
144: if (gMainApp.Has(VMCap::M88K)) {
145: md_mpu.reset(new DebuggerMD_m88xx0(this));
146: } else {
147: md_mpu.reset(new DebuggerMD_m680x0(this));
148: }
149: } catch (...) { }
150: if ((bool)md_mpu == false) {
151: warnx("Failed to initialize md_mpu at %s", __method__);
152: return false;
1.1.1.13 root 153: }
1.1.1.18 root 154:
1.1.1.13 root 155: if (gMainApp.Has(VMCap::LUNA)) {
1.1.1.18 root 156: try {
157: md_xp.reset(new DebuggerMD_hd64180(this));
158: } catch (...) { }
159: if ((bool)md_xp == false) {
160: warnx("Failed to initialize md_xp at %s", __method__);
161: return false;
162: }
1.1.1.13 root 163: }
1.1.1.18 root 164:
1.1.1.13 root 165: // とりあえずメインプロセッサに固定
166: curmd = md_mpu.get();
167:
168: // md_* が用意できたので MemdumpMonitor にセットする。
169: // これらは Monitor なので Init() より前に用意してなければならないが、
170: // 一方で md は諸々が落ち着いた後のここでないと用意できない。うーん。
171: // ここはバス別のモニタなので curmd ではなく md_{mpu,xp} を参照する。
172: for (int i = 0, end = memdump_monitor.size(); i < end; i++) {
173: auto *mem = memdump_monitor[i].get();
174: mem->InitMD(md_mpu.get());
175: }
176: if (gMainApp.Has(VMCap::LUNA)) {
177: for (int i = 0, end = xpmemdump_monitor.size(); i < end; i++) {
178: auto *mem = xpmemdump_monitor[i].get();
179: mem->InitMD(md_xp.get());
180: }
1.1.1.11 root 181: }
182:
1.1 root 183: // -d なら CPU 起動時点で停止してプロンプトを待つ
1.1.1.2 root 184: if (gMainApp.debug_on_start) {
1.1.1.11 root 185: is_pause = true;
186: // この時点ではまだ MPU にメッセージを送ることはできない
1.1 root 187: }
188:
1.1.1.13 root 189: // -b [<cpu>,]<addr>[,<skip>] ならブレークポイント設定
1.1.1.20! root 190: for (const auto& str : gMainApp.debug_breakaddr) {
! 191: if (ParseOptBreakAddr(str) == false) {
1.1.1.13 root 192: return false;
1.1.1.4 root 193: }
1.1.1.20! root 194: }
1.1.1.13 root 195:
1.1.1.20! root 196: // --bi [<cpu>,]<inst>[:<mask>][,<skip>] で命令ブレークポイント設定。
! 197: for (auto& str : gMainApp.debug_breakinst) {
! 198: if (ParseOptBreakInst(str) == false) {
1.1.1.11 root 199: return false;
1.1.1.4 root 200: }
1.1.1.20! root 201: }
! 202:
! 203: // --bv [<cpu>,]<vec1>[-<vec2>][,<skip>] でベクタブレークポイント設定。
! 204: for (auto& str : gMainApp.debug_breakvec) {
! 205: if (ParseOptBreakVector(str) == false) {
! 206: return false;
1.1.1.13 root 207: }
1.1 root 208: }
209:
1.1.1.15 root 210: // --bi-exg なら命令ブレークポイントを設定
211: // (CPU のチェックはしていない)
212: if (gMainApp.debug_breakinst_exg) {
213: breakpoint_t bp;
214: bp.type = BreakpointType::Instruction;
215: bp.inst = 0xcf4f0000;
216: bp.mask = 0xffff0000;
1.1.1.20! root 217: bp.md = curmd;
! 218: AddBreakpoint(bp);
1.1.1.15 root 219: // 今登録されている命令ブレークの必要命令長を再計算
220: RecalcInstMask();
221: }
222:
1.1.1.11 root 223: // 入出力を FILE で扱う
224: cons = funopen(this, readfunc, writefunc, NULL, NULL);
225: if (cons == NULL) {
226: warnx("funopen2 failed");
227: return false;
228: }
229:
230: return true;
1.1 root 231: }
232:
233: // デバッガスレッド
234: void
235: Debugger::ThreadRun()
236: {
1.1.1.16 root 237: SetThreadAffinityHint(AffinityClass::Light);
238:
1.1.1.5 root 239: // disp_regs の初期値設定。
240: // 再接続でも継続してていいような気がするのでループ外で初期化。
241: disp_regs.clear();
242: disp_regs.push_back("r");
243:
1.1.1.11 root 244: // MPU のトレース状態の初期化は vm/mpu* 側のリセット例外で行っている。
245:
1.1 root 246: for (;;) {
1.1.1.11 root 247: // 何か起きるまで待つ
248: uint32 req;
249: {
250: std::unique_lock<std::mutex> lock(mtx);
251: cv_request.wait(lock, [&] { return request != 0; });
252: req = request;
253: request = 0;
254: }
255:
256: if ((req & REQUEST_EXIT)) {
1.1.1.3 root 257: break;
258: }
259:
1.1.1.11 root 260: if ((req & REQUEST_RXCHAR)) {
261: // コンソールからの文字入力
262: int c;
263: while ((c = hostcom->Rx()) >= 0) {
264: Input(c);
265: }
266: }
1.1 root 267:
1.1.1.11 root 268: if ((req & REQUEST_ACCEPT)) {
269: // TCP 待ち受けに着信があればすぐにプロンプトを出したい
270: Input('\n');
271: }
1.1.1.3 root 272:
1.1.1.11 root 273: if ((req & REQUEST_PROMPT)) {
274: // VM 停止(したのでプロンプトモードへ)
275: EnterPrompt();
276: }
277: }
278:
1.1.1.13 root 279: LeavePrompt(false);
1.1.1.11 root 280: Close();
281: }
282:
283: // コンソールをクローズする
284: void
285: Debugger::Close()
286: {
287: if (cons) {
288: fclose(cons);
289: cons = NULL;
290: }
291: }
292:
293: #if 0
1.1.1.3 root 294: bool first = true;
295: for (;;) {
296: // 接続後の1回目だけ実行するもの。
297: if (first) {
298: first = false;
299:
300: // greeting はプロンプトが取れる前にもう表示したい。
301: // 何らかの事故でプロンプトが取れなくても、ここまでは接続
302: // できてることが分かるように。
1.1.1.11 root 303: fprintf(cons, "This is debugger console\n");
1.1.1.3 root 304:
305: // 接続ごとに初期化する値
306: n_enable = false;
307: s_enable = false;
1.1.1.5 root 308: t_enable = true;
1.1.1.3 root 309: }
1.1.1.11 root 310: }
311: }
1.1.1.3 root 312:
1.1.1.11 root 313: #endif
1.1.1.8 root 314:
1.1.1.11 root 315: // スレッドに終了指示
316: void
317: Debugger::Terminate()
318: {
1.1.1.20! root 319: std::lock_guard<std::mutex> lock(mtx);
1.1.1.11 root 320: request |= REQUEST_EXIT;
321: cv_request.notify_one();
322: }
1.1.1.3 root 323:
1.1.1.11 root 324: // funopen の read コールバック
325: static int
326: readfunc(void *cookie, char *buf, int bufsize)
327: {
1.1.1.16 root 328: auto debugger = reinterpret_cast<Debugger *>(cookie);
329: return debugger->ReadFunc(buf, bufsize);
1.1.1.11 root 330: }
1.1.1.3 root 331:
1.1.1.11 root 332: // funopen の write コールバック
333: static int
334: writefunc(void *cookie, const char *buf, int len)
335: {
1.1.1.16 root 336: auto debugger = reinterpret_cast<Debugger *>(cookie);
337: return debugger->WriteFunc(buf, len);
1.1.1.11 root 338: }
1.1.1.3 root 339:
1.1.1.11 root 340: // funopen の read コールバックの本体
341: int
342: Debugger::ReadFunc(char *buf, int bufsize)
343: {
344: char *d = buf;
345: char *end = buf + bufsize;
1.1.1.8 root 346:
1.1.1.11 root 347: for (; d < end; ) {
348: if ((bool)hostcom == false) {
349: errno = EIO;
350: return -1;
351: }
1.1.1.3 root 352:
1.1.1.11 root 353: int c = hostcom->Rx();
354: if (c < 0) {
355: break;
1.1.1.3 root 356: }
1.1.1.11 root 357: *d++ = c;
358: }
359: return (d - buf);
360: }
1.1.1.3 root 361:
1.1.1.11 root 362: // funopen の write コールバックの本体
363: int
364: Debugger::WriteFunc(const char *buf, int len)
365: {
366: const char *s = buf;
367: const char *end = buf + len;
1.1 root 368:
1.1.1.11 root 369: for (; s < end; ) {
370: if ((bool)hostcom == false) {
371: errno = EIO;
372: return -1;
373: }
374:
375: // ホストスレッドのキューが満杯なら空くまで待つ。うーん…。
376: // XXX 無期限で大丈夫だろうか
377: int c = *s++;
378: if (c == '\n') {
379: // ここで LF を CRLF にする?
380: // (TELNET に出力するのに必要)
381: while (hostcom->Tx('\r') == false) {
382: usleep(10);
383: }
384: }
385: while (hostcom->Tx(c) == false) {
386: usleep(10);
1.1.1.3 root 387: }
1.1 root 388: }
1.1.1.11 root 389: return (s - buf);
390: }
1.1 root 391:
1.1.1.11 root 392: // ホストからの1文字受信通知 (HostCOM スレッドから呼ばれる)
393: void
1.1.1.19 root 394: Debugger::RxCallback(uint32 arg)
1.1.1.11 root 395: {
396: // デバッガスレッドに通知
1.1.1.20! root 397: std::lock_guard<std::mutex> lock(mtx);
1.1.1.11 root 398: request |= REQUEST_RXCHAR;
399: cv_request.notify_one();
1.1 root 400: }
401:
1.1.1.11 root 402: // ホストからの着信通知 (HostCOM スレッドから呼ばれる)
403: void
1.1.1.19 root 404: Debugger::AcceptCallback(uint32 dummy)
1.1.1.3 root 405: {
1.1.1.11 root 406: // デバッガスレッドに通知
1.1.1.20! root 407: std::lock_guard<std::mutex> lock(mtx);
1.1.1.11 root 408: request |= REQUEST_ACCEPT;
409: cv_request.notify_one();
410: }
411:
412: // ホストからの1文字入力
413: void
414: Debugger::Input(int c)
415: {
416: // '^@' は捨てる
417: // (意図的にも入力できるが nc が TELNET オプション扱えなくて送ってくる)
418: if (c == '\0') {
419: return;
420: }
421:
422: // HostCOM からは改行で CR が来るようなので LF にしておく
423: if (c == '\r') {
424: c = '\n';
425: }
426:
427: if (is_prompt == false) {
428: // プロンプトでない時は、Enter か ^C でプロンプトを出す
429: if (c == '\n' || c == '\x03') {
430: // MPU に一時停止を要求
431: is_pause = true;
1.1.1.13 root 432: scheduler->SendMessage(MessageID::MPU_TRACE_ALL, true);
1.1.1.11 root 433: }
1.1.1.3 root 434: } else {
1.1.1.11 root 435: // プロンプト中なら行入力
1.1.1.13 root 436: if (c == '\b') {
437: if (cmdbuf.empty() == false) {
438: // 簡易バックスペースを出力。
439: // XXX 実際どうするんだこれ
440: fputc('\b', cons);
441: fputc(' ', cons);
442: fputc('\b', cons);
443: fflush(cons);
1.1.1.3 root 444:
1.1.1.13 root 445: cmdbuf.pop_back();
446: }
447: } else if (c == '\n') {
1.1.1.11 root 448: // Enter ならここでコマンド実行
1.1.1.13 root 449: fputc(c, cons);
450: fflush(cons);
1.1.1.11 root 451:
452: auto act = Command();
453:
454: // 次回との差分のため、今のレジスタセットをバックアップ
1.1.1.13 root 455: curmd->BackupRegs();
1.1.1.11 root 456:
457: switch (act) {
1.1.1.12 root 458: case CmdAct::Stay:
1.1.1.11 root 459: // プロンプトに留まるならここで、次行のプロンプト?
460: PrintPrompt();
461: break;
1.1.1.12 root 462: case CmdAct::Leave:
1.1.1.11 root 463: // プロンプトを抜ける
1.1.1.13 root 464: LeavePrompt(IsTrace());
1.1.1.11 root 465: break;
1.1.1.12 root 466: case CmdAct::Quit:
1.1.1.11 root 467: // アプリケーション自体を終了
1.1.1.13 root 468: LeavePrompt(false);
1.1.1.20! root 469: gMainApp.GetUIMessage()->Post(UIMessage::APPEXIT);
1.1.1.11 root 470: }
1.1.1.14 root 471: } else if (c < ' ' || c == 0x07f) {
1.1.1.13 root 472: // 他のコントロールコードはとりあえず無視
473: } else {
474: // 通常文字なら、エコーバックして追加
475: fputc(c, cons);
476: fflush(cons);
477:
478: cmdbuf.push_back(c);
1.1.1.3 root 479: }
480: }
1.1.1.11 root 481: }
1.1.1.3 root 482:
1.1.1.11 root 483: // コマンドモード(プロンプト)に入る
484: void
485: Debugger::EnterPrompt()
486: {
487: is_prompt = true;
488:
489: // ブレークポイント到達メッセージがあればここで表示
490: if (bpointmsg.empty() == false) {
491: fprintf(cons, "%s", bpointmsg.c_str());
492: bpointmsg.clear();
493: }
494:
1.1.1.13 root 495: // d/m をいきなり引数なしで実行した時のため、現在地にしておく。
496: ResetStickyAddr();
497:
1.1.1.11 root 498: // プロンプトのたびに表示するやつ
499: cmd_minus();
500:
501: PrintPrompt();
1.1.1.3 root 502: }
503:
1.1.1.13 root 504: // コマンドモード(プロンプト)から出る。
505: // trace はプロンプトから抜ける際のトレース状態の指示。
506: // スレッド終了時は false を指定すること。
1.1.1.11 root 507: void
1.1.1.13 root 508: Debugger::LeavePrompt(bool trace)
1.1 root 509: {
1.1.1.11 root 510: // MPU のトレース状態を変更
1.1.1.13 root 511: scheduler->SendMessage(MessageID::MPU_TRACE_ALL, trace);
1.1.1.3 root 512:
1.1.1.11 root 513: is_prompt = false;
1.1.1.2 root 514:
1.1.1.11 root 515: // 最後に VM スレッドで待機している Exec() を起こす
516: {
1.1.1.20! root 517: std::lock_guard<std::mutex> lock(mtx);
1.1.1.11 root 518: prompt_released = true;
519: cv_prompt.notify_one();
1.1.1.2 root 520: }
1.1.1.3 root 521: }
1.1 root 522:
1.1.1.11 root 523: // プロンプトを表示
524: void
525: Debugger::PrintPrompt()
526: {
1.1.1.13 root 527: fprintf(cons, "%s> ", curmd->GetName().c_str());
1.1.1.11 root 528: fflush(cons);
529: }
530:
531: // コマンド実行
1.1.1.12 root 532: Debugger::CmdAct
1.1.1.11 root 533: Debugger::Command()
1.1.1.3 root 534: {
1.1.1.11 root 535: string_rtrim(cmdbuf);
1.1.1.7 root 536:
1.1.1.11 root 537: if (cmdbuf.empty()) {
538: // 空行なら、直前のコマンドをもう一度。
539: // 前行がなければ何もせずもう一度プロンプトを表示するかね。
540: if (last_cmdbuf.empty()) {
1.1.1.12 root 541: return CmdAct::Stay;
1.1.1.3 root 542: }
1.1.1.11 root 543: cmdbuf = last_cmdbuf;
544: } else {
545: // 入力された行を次回のために保存
546: last_cmdbuf = cmdbuf;
547: }
1.1 root 548:
1.1.1.20! root 549: // 行を cmdargs に分解
1.1.1.11 root 550: ParseCmdbuf();
551: cmdbuf.clear();
1.1 root 552:
1.1.1.11 root 553: // 前回の値が有効なのはコマンドが連続した時だけ
554:
555: // コマンドをテーブルから探す
556: auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
1.1.1.20! root 557: [=](auto x) { return strcmp(cmdargs[0].c_str(), x.name) == 0; });
1.1.1.11 root 558: if (it != cmdtable.end()) {
559: auto cmd = *it;
560:
561: // 見付かれば実行
1.1.1.12 root 562: return (this->*(cmd.func))();
1.1.1.11 root 563: }
564:
565: // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
1.1.1.20! root 566: if (cmdargs[0][0] == 'r') {
1.1.1.11 root 567: // ShowRegister() は処理したら true を返す。
568: // "r" 系コマンドはすべて Stay。
1.1.1.20! root 569: if (curmd->ShowRegister(cons, cmdargs)) {
1.1.1.12 root 570: return CmdAct::Stay;
1.1.1.3 root 571: }
1.1.1.11 root 572: }
1.1.1.3 root 573:
1.1.1.11 root 574: // 知らないコマンドも Stay 相当。
1.1.1.20! root 575: fprintf(cons, "%s: Unknown command.\n", cmdargs[0].c_str());
1.1.1.11 root 576: // この行は次回空エンターで再発行しないでいい。
577: last_cmdbuf.clear();
1.1.1.12 root 578: return CmdAct::Stay;
1.1.1.11 root 579: }
1.1.1.3 root 580:
1.1.1.11 root 581: // ブレークポイントとかを調べる。1命令ごとに呼び出される。
582: // (VM スレッドから呼ばれる)
583: void
1.1.1.13 root 584: Debugger::Exec(DebuggerMD *md)
1.1.1.11 root 585: {
1.1.1.13 root 586: if (Check(md)) {
587: // この CPU でブレークしたので、ターゲット CPU をこっちに変更。
588: ChangeMD(md);
589:
1.1.1.11 root 590: // 実時間を停止
1.1.1.13 root 591: syncer->StopRealTime();
1.1.1.3 root 592:
1.1.1.11 root 593: // 待機
594: {
595: std::unique_lock<std::mutex> lock(mtx);
1.1.1.3 root 596:
1.1.1.11 root 597: // notify 前にクリア
598: prompt_released = false;
1.1.1.3 root 599:
1.1.1.11 root 600: // MPU が一時停止したことをデバッガスレッドへ通知する
601: request |= REQUEST_PROMPT;
602: cv_request.notify_one();
1.1 root 603:
1.1.1.11 root 604: // デバッガスレッドがプロンプト解放するまで待機
605: cv_prompt.wait(lock, [&] { return prompt_released; });
1.1.1.3 root 606: }
1.1.1.2 root 607:
1.1.1.11 root 608: // 実時間を再開
1.1.1.13 root 609: syncer->StartRealTime();
1.1.1.11 root 610: } else {
611: // t (トレース表示) が有効な場合は終了条件にマッチしなかったここで
612: // レジスタを表示。終了条件にマッチした時は EnterPrompt() で表示する。
1.1.1.13 root 613: if (t_enable == md) {
614: assert(md == curmd);
615: pc = curmd->GetPC();
1.1.1.11 root 616: cmd_minus();
617: // 次回との差分のため今のレジスタセットをバックアップ
1.1.1.13 root 618: curmd->BackupRegs();
1.1.1.11 root 619: }
1.1.1.3 root 620: }
1.1 root 621: }
622:
1.1.1.11 root 623: // MPU をトレース状態にすべきなら true を返す。
1.1 root 624: bool
1.1.1.11 root 625: Debugger::IsTrace() const
1.1 root 626: {
1.1.1.11 root 627: // 有効なブレークポイントがあれば MPU をトレースにする
628: for (const auto& bp : bpoint) {
629: if (bp.type != BreakpointType::Unused) {
630: return true;
631: }
632: }
633:
634: // この辺のどれかがあれば MPU をトレースにする
1.1.1.13 root 635: return is_pause || (step_type != StepType::None);
1.1 root 636: }
637:
1.1.1.11 root 638: // デバッガ実行中なら命令開始前に VM スレッドから呼ばれる。
1.1 root 639: // プロンプトに降りるなら true を返す。
640: bool
1.1.1.13 root 641: Debugger::Check(DebuggerMD *md)
1.1 root 642: {
1.1.1.5 root 643: bool is_break = false;
644:
645: // ブレークポイントは他のチェックとは併用になるので先に調べる。
1.1.1.13 root 646: if (CheckAllBreakpoints(md)) {
1.1.1.5 root 647: is_break = true;
1.1 root 648:
1.1.1.13 root 649: // この次に調べる各種終了条件が来ないうちに先にブレークポイントに
650: // 到達した場合でも、ブレークポイントによりプロンプトに降りる
651: // わけなので、実行中のステップ実行をキャンセルする。
652: // この場合もブレークポイント側が true なので true を返せばよい。
653: step_type = StepType::None;
654: step_md = NULL;
655: }
656:
657: // ステップ実行系は、ターゲット CPU 側でだけ判定を行い、
658: // いずれの場合も終了条件にマッチしたらブレークポイントの成否に関わらず
659: // true を返せばいい。
660: // なお、トレース表示に関してはここではなく Exec() 側でやってある。
661:
662: if (step_md == md) {
663: bool hit = false;
664: switch (step_type) {
665: case StepType::None:
666: break;
1.1.1.5 root 667:
1.1.1.13 root 668: case StepType::Count: // 命令数指定
669: step_count--;
670: putlog(1, "Check s: --step_count=%d", step_count);
671: if (step_count == 0) {
672: hit = true;
673: }
674: break;
1.1.1.5 root 675:
1.1.1.13 root 676: case StepType::CountSkipSub: // 命令数指定 (サブルーチンをスキップ)
677: if ((int64)step_addr >= 0) {
678: // アドレスが指定されていれば、ステップインをスキップ中
679: if (step_addr == step_md->GetPC()) {
680: step_count--;
681: step_addr = (uint64)-1;
682: }
683: } else {
684: step_count--;
685: }
686: if (loglevel >= 1) {
687: if ((int64)step_addr < 0) {
688: putlogn("Check n: --step_count=%d", step_count);
689: } else {
690: putlogn("Check n: step_addr=$%08x", (uint32)step_addr);
691: }
692: }
693: if (step_count == 0 || is_break/*?*/) {
694: hit = true;
695: } else {
696: // スキップ中でなければ、ステップインが起きるか都度調べる。
697: // すでにスキップ中なら到達するまでは何もしない。
698: if ((int64)step_addr < 0) {
699: SetNBreakpoint();
700: }
701: }
702: break;
1.1.1.5 root 703:
1.1.1.13 root 704: case StepType::StepOut: // ステップアウト
705: putlog(1, "Check so");
706: if (step_md->IsStepOut()) {
707: hit = true;
708: }
709: break;
1.1 root 710:
1.1.1.13 root 711: case StepType::Addr: // アドレス指定
712: putlog(1, "Check c: step_addr=$%08x", (uint32)step_addr);
713: if (step_addr == step_md->GetPC()) {
714: hit = true;
715: }
716: break;
1.1 root 717:
1.1.1.13 root 718: case StepType::Time: // 時間指定
719: putlog(1, "Check ct");
720: if (scheduler->GetVirtTime() >= step_time) {
721: hit = true;
722:
723: // 時間到達は他のと比べて分かりづらいので、
724: // ブレークポイントメッセージに便乗して表示(?)
725: bpointmsg += string_format("%s has passed.\n",
726: TimeToStr(ct_timespan).c_str());
1.1.1.5 root 727: }
1.1.1.13 root 728: break;
729:
730: default:
731: PANIC("StepType %d not supported\n", (int)step_type);
1.1.1.5 root 732: }
1.1.1.13 root 733:
734: if (hit) {
1.1.1.11 root 735: is_break = true;
1.1.1.13 root 736: step_type = StepType::None;
737: step_md = NULL;
738: t_enable = NULL;
1.1.1.5 root 739: }
1.1.1.11 root 740: }
1.1.1.5 root 741:
1.1.1.11 root 742: // デバッガから MPU の一時停止が要求されているか
743: if (is_pause) {
744: is_pause = false;
745: is_break = true;
1.1 root 746: }
747:
1.1.1.5 root 748: return is_break;
1.1 root 749: }
750:
1.1.1.13 root 751: // md で指定された CPU のブレークポイントがどれかでも成立するかを調べる。
752: // 1つ以上成立してブレークするなら、true を返す。
1.1.1.4 root 753: // 1つも成立しておらずブレークしないなら false を返す。
1.1.1.11 root 754: // このルーチンから cons への出力は使わないこと。(プロンプトにいない時でも
755: // 呼ばれるので)
1.1.1.4 root 756: bool
1.1.1.13 root 757: Debugger::CheckAllBreakpoints(DebuggerMD *md)
1.1.1.4 root 758: {
759: bool is_break = false;
1.1.1.13 root 760: int vector;
1.1.1.4 root 761:
762: // 命令ごとにクリアする
763: bi_inst = 0;
764: bi_inst_bytes = 0;
765:
1.1.1.13 root 766: // 例外はここでローカルにコピーしてから、クリアする。
767: // 厳密には atomic exchange すべきのような。
768: vector = md->bv_vector;
769: md->bv_vector = -1;
770:
1.1.1.4 root 771: // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが)
772: // 残りの他の条件も成立すればカウントを進める必要があるため、
773: // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。
1.1.1.5 root 774: for (int i = 0, end = bpoint.size(); i < end; i++) {
775: auto& bp = bpoint[i];
776:
1.1.1.13 root 777: if (bp.md != md) {
778: continue;
779: }
780:
1.1.1.4 root 781: switch (bp.type) {
782: case BreakpointType::Address:
1.1.1.13 root 783: // 通常動作中でアドレスが一致すればマッチ
784: if (bp.addr == bp.md->GetPC() &&
785: bp.md->GetCPUState() == CPUState::Normal)
786: {
787: break;
1.1.1.4 root 788: }
1.1.1.13 root 789: continue;
1.1.1.4 root 790:
791: case BreakpointType::Memory:
1.1.1.13 root 792: if (bp.md->CheckLEA(bp.addr)) {
793: break;
1.1.1.4 root 794: }
1.1.1.13 root 795: continue;
1.1.1.4 root 796:
797: case BreakpointType::Exception:
1.1.1.13 root 798: if (vector >= 0) {
799: if (bp.vec1 <= vector && vector <= bp.vec2) {
1.1.1.4 root 800: break;
801: }
802: }
803: continue;
804:
805: case BreakpointType::Instruction:
1.1.1.13 root 806: if (CheckBreakpointInst(bp)) {
807: break;
1.1.1.4 root 808: }
1.1.1.13 root 809: continue;
1.1.1.4 root 810:
811: default:
812: continue;
813: }
814:
815: // 条件は成立したのでカウントとスキップ
816: bp.matched++;
817:
818: // skip == -1 は常にブレークしない (カウントするだけ)
819: if (bp.skip < 0) {
820: continue;
821: }
822:
823: // skip が 1 以上なら、remain を減算
824: if (bp.skip > 0 && --bp.skipremain > 0) {
825: continue;
826: }
827:
828: // ブレーク成立
829: is_break = true;
1.1.1.5 root 830:
831: std::string desc;
1.1.1.13 root 832: desc = string_format("cpu=%s ", bp.md->GetName().c_str());
1.1.1.5 root 833: switch (bp.type) {
834: case BreakpointType::Address:
1.1.1.13 root 835: desc += string_format("addr=$%08x", bp.addr);
1.1.1.5 root 836: break;
837: case BreakpointType::Memory:
1.1.1.13 root 838: desc += string_format("mem=$%08x", bp.addr);
1.1.1.5 root 839: break;
840: case BreakpointType::Exception:
841: {
1.1.1.13 root 842: desc += string_format("excp=$%02x", vector);
843: const char *name;
844: if (bp.md->arch == CPUArch::HD64180) {
845: name = MPU64180Device::InterruptName[vector];
846: } else {
847: auto vectortable = pVectorTable.get();
848: name = vectortable->GetExceptionName(vector);
849: }
1.1.1.11 root 850: if (name) {
1.1.1.5 root 851: desc += string_format(" \"%s\"", name);
852: }
853: break;
854: }
855: case BreakpointType::Instruction:
1.1.1.13 root 856: desc += string_format("inst=%0*x",
1.1.1.5 root 857: bi_inst_bytes * 2,
858: bi_inst >> ((4 - bi_inst_bytes) * 8));
859: break;
860: default:
861: assert(false);
862: break;
863: }
1.1.1.7 root 864:
865: // 到達メッセージを作成。
866: // この時点ではまだコンソールを取得していない可能性があるので
867: // (-D なしで起動した場合とか)、表示せず用意するだけ。
868: // コンソールが取得できたところで表示する。
1.1.1.20! root 869: bpointmsg += string_format("Breakpoint #%d (%s) reached.\n",
1.1.1.7 root 870: i, desc.c_str());
1.1.1.4 root 871: }
872:
873: // ブレークポイントが1つでも成立したかどうかを返す
874: return is_break;
875: }
876:
877: // 命令ブレークポイントが成立するか調べる。
878: bool
879: Debugger::CheckBreakpointInst(breakpoint_t& bp)
880: {
881: // uint32 bi_inst が現在の PC 位置の命令データ (左詰め)
882: // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。
883: // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。
884:
1.1.1.16 root 885: busaddr laddr = busaddr(bp.md->GetPC())
886: | BusAddr::Fetch
887: | busaddr::SU(bp.md->IsSuper());
1.1.1.15 root 888: DebuggerMemoryStream mem(bp.md, laddr);
1.1.1.5 root 889:
1.1.1.4 root 890: // 必要なバイト数に達するまで読み足す
1.1.1.5 root 891: bi_inst = 0;
1.1.1.4 root 892: while (bi_inst_bytes < bi_need_bytes) {
1.1.1.13 root 893: uint64 data = mem.Read(bp.md->inst_bytes);
1.1.1.5 root 894: if ((int64)data < 0) {
895: return false;
1.1.1.4 root 896: }
897:
1.1.1.13 root 898: bi_inst <<= bp.md->inst_bytes * 8;
1.1.1.5 root 899: bi_inst |= data;
1.1.1.13 root 900: bi_inst_bytes += bp.md->inst_bytes;
1.1.1.4 root 901: }
1.1.1.5 root 902:
1.1.1.4 root 903: // 左詰めにする
904: bi_inst <<= (4 - bi_inst_bytes) * 8;
905:
906: // 必要なバイト数取得できたので比較
907: if ((bi_inst & bp.mask) == bp.inst) {
908: return true;
909: }
910: return false;
911: }
912:
1.1.1.13 root 913: // 例外通知 (メイン CPU)
1.1.1.4 root 914: void
1.1.1.13 root 915: Debugger::NotifyExceptionMain(int vector)
1.1.1.4 root 916: {
1.1.1.13 root 917: auto md = md_mpu.get();
918: md->bv_vector = vector;
1.1.1.4 root 919: }
920:
1.1.1.13 root 921: // 例外通知 (XP)。
922: // ベクタとして割り込み優先順位 (Intmap*) を使う。
1.1.1.4 root 923: void
1.1.1.13 root 924: Debugger::NotifyExceptionXP(int vector)
1.1.1.4 root 925: {
1.1.1.13 root 926: auto md = md_xp.get();
927: md->bv_vector = vector;
1.1.1.4 root 928: }
929:
1.1 root 930: // コマンド一覧。
1.1.1.3 root 931: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
932: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
1.1.1.12 root 933: { "bi", &Debugger::cmd_bi, },
934: { "bm", &Debugger::cmd_bm, },
935: { "bv", &Debugger::cmd_bv, },
936: { "bx", &Debugger::cmd_bx, },
937: { "b", &Debugger::cmd_b, },
938: { "brhist", &Debugger::cmd_brhist, },
939: { "c", &Debugger::cmd_c, },
940: { "ct", &Debugger::cmd_ct, },
941: { "d", &Debugger::cmd_d, },
942: { "D", &Debugger::cmd_D, },
943: { "disp", &Debugger::cmd_disp, },
944: { "exhist", &Debugger::cmd_exhist, },
945: { "hb", &Debugger::cmd_hb, },
946: { "hr", &Debugger::cmd_hr, },
947: { "h", &Debugger::cmd_h, },
948: { "help", &Debugger::cmd_h, },
949: { "L", &Debugger::cmd_L, },
950: { "m", &Debugger::cmd_m, },
951: { "M", &Debugger::cmd_M, },
1.1.1.13 root 952: { "mpu", &Debugger::cmd_mpu, },
1.1.1.12 root 953: { "n", &Debugger::cmd_n, },
954: { "nt", &Debugger::cmd_nt, },
955: { "q", &Debugger::cmd_q, },
956: { "quit", &Debugger::cmd_q, },
957: { "reset", &Debugger::cmd_reset, },
958: { "s", &Debugger::cmd_s, },
959: { "st", &Debugger::cmd_st, },
960: { "so", &Debugger::cmd_so, },
961: { "sot", &Debugger::cmd_sot, },
962: { "show", &Debugger::cmd_show, },
963: { "t", &Debugger::cmd_t, },
1.1.1.13 root 964: { "xp", &Debugger::cmd_xp, },
965: { "z", &Debugger::cmd_z, },
1.1.1.12 root 966: { "-", &Debugger::cmd_minus, },
1.1 root 967: };
968:
969: // ヘルプ
1.1.1.5 root 970: // h : 一覧を表示
971: // h <cmd> : 単独コマンドの詳細を表示
1.1.1.12 root 972: Debugger::CmdAct
1.1 root 973: Debugger::cmd_h()
974: {
1.1.1.20! root 975: if (cmdargs.size() < 2) {
1.1.1.5 root 976: // 引数なしなら一覧表示。
1.1.1.6 root 977: ShowHelpList(HelpListMain);
1.1.1.12 root 978: return CmdAct::Stay;
1.1.1.3 root 979: }
1.1.1.5 root 980:
981: // 引数があれば個別の詳細
1.1.1.20! root 982: const std::string cmd1 = cmdargs[1];
1.1.1.13 root 983:
984: // curmd の MD 分も併せて検索するため、ローカルに map を作成する。
985: // value のほうは実体コピーは不要なのでポインタ。
986: std::map<const std::string, const std::string *> helpmap;
987: for (const auto& pair : HelpDetails) {
988: helpmap.insert(std::make_pair(pair.first, &pair.second));
989: }
990: for (const auto& pair : curmd->GetHelpReg()) {
991: helpmap.insert(std::make_pair(pair.first, &pair.second));
992: }
993:
994: // 検索
995: const auto it1 = helpmap.find(cmd1);
996: if (it1 == helpmap.end()) {
1.1.1.20! root 997: fprintf(cons, "Invalid command name: %s\n", cmd1.c_str());
1.1.1.13 root 998: return CmdAct::Stay;
999: }
1000: const auto& msg1 = *(it1->second);
1001:
1002: const std::string *msg;
1003: if (msg1[0] != '=') {
1004: // メッセージ本文が "=" から始まってなければこれを採用。
1005: msg = &msg1;
1006: } else {
1007: // メッセージ本文が "=<cmd>" の形式なら、<cmd> を探し直す。
1008: // 別名で同じヘルプを指すシンボリックリンクみたいなもの。
1009: std::string cmd2 = msg1.substr(1);
1010:
1011: // 再検索 (こっちは見付からないはずはない)
1012: const auto it2 = helpmap.find(cmd2);
1013: if (it2 == helpmap.end()) {
1.1.1.20! root 1014: fprintf(cons, "Warning: '%s' in '%s' not found.\n",
1.1.1.13 root 1015: msg1.c_str(), cmd1.c_str());
1016: return CmdAct::Stay;
1.1.1.5 root 1017: }
1.1.1.13 root 1018: const auto& msg2 = *(it2->second);
1019: msg = &msg2;
1020: }
1021:
1022: std::string disp = HelpConvert(*msg);
1023: fprintf(cons, "%s", disp.c_str());
1.1.1.12 root 1024: return CmdAct::Stay;
1.1.1.3 root 1025: }
1026:
1.1.1.5 root 1027: // hb : ブレークポイント系コマンドの一覧を表示
1028: // こいつだけ結構占めるので別階層。
1.1.1.12 root 1029: Debugger::CmdAct
1.1.1.5 root 1030: Debugger::cmd_hb()
1031: {
1.1.1.12 root 1032: return ShowHelpList(HelpListBreakpoints);
1.1.1.5 root 1033: }
1034:
1035: // hr : レジスタ表示系コマンドの一覧を表示
1036: // CPU ごとに違うので。
1.1.1.12 root 1037: Debugger::CmdAct
1.1.1.5 root 1038: Debugger::cmd_hr()
1039: {
1.1.1.13 root 1040: return ShowHelpList(curmd->GetHelpListReg());
1.1.1.5 root 1041: }
1.1.1.3 root 1042:
1.1.1.6 root 1043: // ヘルプ一覧を表示。
1.1.1.12 root 1044: Debugger::CmdAct
1.1.1.6 root 1045: Debugger::ShowHelpList(const HelpMessages& msgs)
1.1.1.3 root 1046: {
1.1.1.11 root 1047: fprintf(cons, "Type \"help <cmd>\" for indivisual details.\n");
1.1.1.3 root 1048: for (const auto& pair : msgs) {
1.1.1.11 root 1049: fprintf(cons, " %-16s %s\n", pair.first.c_str(), pair.second.c_str());
1.1 root 1050: }
1.1.1.12 root 1051: return CmdAct::Stay;
1.1 root 1052: }
1053:
1.1.1.5 root 1054: // 個別ヘルプメッセージを出力用に置換。
1055: // 1. 行頭の連続する改行 (通常は1つのはず) を取り除く。
1056: // 2. 末尾の連続するタブ (通常は1つのはず) を取り除く。
1057: // 3. (各行頭の) タブを空白4つに置換する。
1058: std::string
1059: Debugger::HelpConvert(const std::string& src)
1060: {
1061: int start = 0;
1062: int len = src.size();
1063:
1064: // 末尾の連続するタブをカウント
1065: while (src[len - 1] == '\t') {
1066: len--;
1067: }
1068: // 先頭の連続する改行をカウント
1069: while (src[start] == '\n') {
1070: start++;
1071: len--;
1072: }
1073: std::string stripped = src.substr(start, len);
1074:
1075: // 面倒なので行頭に関わらず全タブを置換
1076: const char *c_stripped = stripped.c_str();
1077: std::string dst;
1078: for (const char *s = c_stripped; *s; s++) {
1079: if (*s == '\t') {
1080: dst.append(" ");
1081: } else {
1082: dst.append(1, *s);
1083: }
1084: }
1085: return dst;
1086: }
1087:
1088: /*static*/ const HelpMessages
1.1.1.6 root 1089: Debugger::HelpListMain = {
1.1.1.5 root 1090: { "b*", "Set/show Breakpoints (Type \"hb\" for details)" },
1091: { "brhist", "Show branch history" },
1.1.1.12 root 1092: { "c/ct", "Continue" },
1.1.1.5 root 1093: { "d/dt/D", "Disassemble" },
1094: { "disp", "Set register group to show" },
1095: { "exhist", "Show exception history" },
1096: { "help", "Show this message" },
1097: { "L", "Set log level" },
1098: { "m/mt/M", "Memory dump" },
1099: { "n/nt", "Step until next instruction (Skip subroutines)" },
1100: { "quit", "Quit" },
1101: { "r*", "Show registers (Type \"hr\" for details)" },
1102: { "reset", "Reset the VM" },
1103: { "s/st", "Step an instruction (Step in)" },
1104: { "so", "Step out" },
1105: { "show", "Show monitor" },
1106: };
1107:
1108: /*static*/ const HelpMessages
1.1.1.6 root 1109: Debugger::HelpListBreakpoints = {
1.1.1.5 root 1110: { "b", "Show all breakpoints" },
1111: { "b arg..","Set/Delete breakpoint" },
1112: { "bm", "Set memory breakpoint" },
1113: { "bi", "Set instruction breakpoint" },
1114: { "bv", "Set exception breakpoint" },
1115: { "bx", "Delete all breakpoints" },
1116: };
1117:
1118: // 各コマンドの詳細。"コマンド名" => "説明文" の形式。
1119: // 説明文は C+11 の生文字リテラル機能を使って R"**( )**" で囲む。
1120: // 1つのエントリは
1121: // { "cmdname", R"**(
1122: // <tab>cmdname [argument...]
1123: //
1124: // <tab>Description...
1125: // <tab>)**" },
1126: // のようになる。説明文本文はインデント1つ(TAB 幅 4) で字下げした状態で
1127: // 80桁以内に収めること。出力の際にタブを空白4つに置き換えることでソース上
1128: // での見た目と実際の出力とを揃えてある。
1129: // またソースコード上の見栄えの問題として、文字列の開始記号、終了記号を本文
1130: // とは別の行に書いているため、オブジェクトとしての文字列には先頭に改行、
1131: // 末尾にタブが余計に入っているが、これは表示の際にコードで取り除く。
1132: //
1133: // 説明文の書き方は、まずコマンド書式を1行ずつ書く。
1134: // コマンド書式と本文との間は1行あける。
1135: /*static*/ const HelpMessages
1136: Debugger::HelpDetails = {
1137: //-----
1138: { "b", R"**(
1139: Command: b
1.1.1.13 root 1140: Command: b [<cpu>,]<address> [<skipcount>]
1.1.1.5 root 1141: Command: b #n
1142:
1143: The first form (with no arguments) shows all breakpoints.
1.1.1.13 root 1144: The second form sets a new breakpoint at <address> on <cpu>.
1145: If <cpu> is omitted, the current cpu is used.
1.1.1.5 root 1146: XXX skipcount
1147: If <skipcount> is -1, it will never match. It's useful to count the
1148: number of times you have passed this address.
1149: The third form deletes a breakpoint specified by number.
1150: )**" },
1151:
1152: //-----
1153: { "bm", R"**(
1.1.1.13 root 1154: Command: bm [<cpu>,]<address> [<skipcount>]
1.1.1.5 root 1155:
1.1.1.13 root 1156: Sets a memory breakpoint at <address> on <cpu>.
1157: If <cpu> is omitted, the current cpu is used.
1.1.1.5 root 1158: XXX skipcount
1159: If <skipcount> is -1, it will never match. It's useful to count the
1160: number of times you have passed this address.
1161: )**" },
1162:
1163: //-----
1164: { "bi", R"**(
1.1.1.13 root 1165: Command: bi [<cpu>,]<inst>[:<mask>] [<skipcount>]
1.1.1.5 root 1166:
1.1.1.13 root 1167: Sets an instruction breakpoint on <cpu>. <inst> must be 16 bits or 32
1168: bits on m68k and must be 32 bits on m88k. <mask> can specify the mask.
1169: Its length must be the same as <inst>. If the <mask> is omitted, all
1170: bits of <inst> is used to compare.
1.1.1.5 root 1171: For example,
1172: "bi 4e75" on m68k will stop at 'rts' instruction.
1173: "bi 70004e4f:fff0ffff" on m68k will stop at any of 'IOCS #0'..'IOCS #15'.
1174:
1.1.1.13 root 1175: If <cpu> is omitted, the current cpu is used.
1.1.1.5 root 1176: XXX skipcount
1177: If <skipcount> is -1, it will never match. It's useful to count the
1178: number of times you have passed this address.
1179: )**" },
1180:
1181: //-----
1182: { "bv", R"**(
1.1.1.13 root 1183: Command: bv [<cpu>,]<vector>[-<vector2>] [<skipcount>]
1.1.1.5 root 1184:
1.1.1.20! root 1185: Sets an exception breakpoint on <cpu>. <vector> accepts hexadecimal
! 1186: number if prefixed with "$" or "0x", or decimal number if no prefix.
! 1187: <vector> ranges from 0 to 255 on m68k, and 0 to 511 on m88k.
! 1188: If <vector2> is specified, it matches the range from <vector> to <vector2>
! 1189: (including themselves). For example, "bv 1-$1ff" on m88k means all
! 1190: exceptions but reset.
1.1.1.13 root 1191:
1192: If <cpu> is omitted, the current cpu is used.
1.1.1.5 root 1193: XXX skipcount
1194: If <skipcount> is -1, it will never match. It's useful to count the
1195: number of times you have passed this address.
1196: )**" },
1197:
1198: //-----
1199: { "bx", R"**(
1200: Command: bx
1201:
1202: Deletes all breakpoints.
1203: )**" },
1204:
1205: //-----
1206: { "brhist", R"**(
1207: Command: brhist [<maxlines>]
1208:
1209: Show branch history. <maxlines> specifies the maximum number of lines
1210: to display. If <maxlines> is negative value, sort in reverse order.
1211: )**" },
1212:
1213: //-----
1214: { "c", R"**(
1215: Command: c [<address>]
1216:
1217: Continue (until <address> if specified).
1218: )**" },
1219:
1220: //-----
1.1.1.12 root 1221: { "ct", R"**(
1222: Command: ct [<timespan>]
1223:
1224: Continue until specified <timespan> has elapsed. If the <timespan> is
1.1.1.13 root 1225: omitted, the last value is used again. The unit of <timespan> is
1.1.1.12 root 1226: seconds in default. You can use "msec", "usec", or "nsec" suffix.
1227: )**" },
1228:
1229: //-----
1.1.1.5 root 1230: { "d", R"**(
1231: Command: D <address>] [<lines>]
1.1.1.6 root 1232: Command: d [[<space>:]<address>] [<lines>]
1.1.1.5 root 1233:
1234: Shows disassemble.
1235: The first form ("D") interprets <address> as a physical address.
1.1.1.15 root 1236: The second form ("d") interprets <address> as a logical address
1237: if the address translation is enabled. Normally, <address> is
1.1.1.6 root 1238: interpreted in the current privilege and current address space. You can
1239: change it by <space> modifier.
1240: On m68k, <space> can be specified either by function code number directly
1241: ('1', '2', '5', and '6) or by one or more following modifiers:
1242: 's' .. Supervisor mode 'u' .. User mode
1243: 'd' .. Data space 'p' or 'i' .. Program space
1244: On m88k, <space> can be specified by using combination of privilege
1245: modifier ('s' or 'u', same as above) and following CMMU identifiers:
1246: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
1247: Or CMMU can also be identified by CMMU number (like '6' or '7').
1.1.1.5 root 1248: )**" },
1249: { "D", "=d" },
1250:
1251: //-----
1252: { "disp", R"**(
1253: Command: disp <register...>
1254:
1255: Sets the registers list to be shown in the trace.
1256: <registers...> is comma-separated list and each of which is a command
1257: name that shows registers. See "hr".
1258: The default is "r" and thus it only shows general registers in the trace.
1259: For example, if you set "disp r,rf", it will show general registers and
1260: FPU registers in every trace.
1261: )**" },
1262:
1263: //-----
1264: { "exhist", R"**(
1265: Command: exhist [<maxlines>]
1266:
1267: Show exception history. <maxlines> specifies the maximum number of lines
1268: to display. If <maxlines> is negative value, sort in reverse order.
1269: )**" },
1270:
1271: //-----
1272: { "help", R"**(
1273: Command: help [<command>]
1274: Command: hb
1275: Command: hr
1276:
1277: The "help" command without arguments shows the list of commands.
1278: The "help" command with an argument shows <command>'s detailed help.
1279: "h" is a synonym of "help".
1280: The "hb" command shows the list of breakpoint-related commands.
1281: The "hr" command shows the list of register-related commands.
1282: )**" },
1283: { "h", "=help" },
1284: { "hb", "=help" },
1285: { "hr", "=help" },
1286:
1287: //-----
1288: { "L", R"**(
1.1.1.9 root 1289: Command: L <name1>[=<level1>][,<name2>=[<level2>]]...
1.1.1.5 root 1290:
1291: Set loglevel. XXX To be written...
1292: )**" },
1293:
1294: //-----
1295: { "m", R"**(
1296: Command: M <address>] [<lines>]
1.1.1.6 root 1297: Command: m [[<space>:]<address>] [<lines>]
1.1.1.5 root 1298:
1299: Shows memory dump.
1300: The first form ("M") interprets <address> as a physical address.
1.1.1.15 root 1301: The second form ("m") interprets <address> as a logical address
1302: if the address translation is enabled. Normally, <address> is
1.1.1.6 root 1303: interpreted in the current privilege and current address space. You can
1304: change it by <space> modifier.
1305: On m68k, <space> can be specified either by function code number directly
1306: ('1', '2', '5', and '6) or by one or more following modifiers:
1307: 's' .. Supervisor mode 'u' .. User mode
1308: 'd' .. Data space 'p' or 'i' .. Program space
1309: On m88k, <space> can be specified by using combination of privilege
1310: modifier ('s' or 'u', same as above) and following CMMU identifiers:
1311: 'd' .. Data CMMU 'i' or 'p' .. Instruction CMMU
1312: Or CMMU can also be identified by CMMU number (like '6' or '7').
1.1.1.5 root 1313: )**" },
1314: { "M", "=m" },
1315:
1316: //-----
1.1.1.13 root 1317: { "mpu", R"**(
1318: Command: mpu
1319:
1320: Change the target CPU to "mpu"(M68030/M88100).
1321: )**" },
1322:
1323: //-----
1.1.1.5 root 1324: { "n", R"**(
1325: Command: n [<count>]
1326: Command: nt [<count>]
1327:
1328: Step one (or <count>) instructions. Unlike "s" command, "n" skips
1.1.1.13 root 1329: subroutine (and repeat instruction like as LDIR in HD64*180).
1.1.1.5 root 1330: If "t" is suffixed, it shows a trace for each instruction (including
1331: while skipping).
1332: )**" },
1333: { "nt", "=n" },
1334:
1335: //-----
1336: { "quit", R"**(
1337: Command: quit (or q)
1338:
1339: Quit the debugger console. This continues the execution, as same as "c".
1340: )**" },
1341: { "q", "=quit" },
1342:
1343: //-----
1344: { "reset", R"**(
1345: Command: reset
1346:
1347: Reset the VM. Currently this does the hardware reset.
1348: )**" },
1349:
1350: //-----
1351: { "s", R"**(
1352: Command: s [<count>]
1353: Command: st [<count>]
1354:
1355: Step one (or <count>) instructions. Unlike "n" command, "s" steps in
1356: subroutine.
1357: If "t" is suffixed, it shows a trace for each instruction.
1358:
1359: "t" command is a synonym of "st" for backward compatibility.
1360: )**" },
1361: { "st", "=s" },
1362:
1363: //-----
1364: { "so", R"**(
1365: Command: so
1366: Command: sot
1367:
1368: Step out this sub routine.
1369: If "t" is suffixed, it shows a trace for each instruction.
1370: )**" },
1371:
1372: //-----
1373: { "show", R"**(
1374: Command: show <monitor>
1375:
1376: Shows the specified monitor.
1377: )**" },
1378:
1379: //-----
1380: { "t", "=s" },
1.1.1.13 root 1381:
1382: //-----
1383: { "xp", R"**(
1384: Command: xp
1385:
1386: Change the target CPU to "xp"(HD647180).
1387: )**" },
1388:
1389: //-----
1390: { "z", R"**(
1391: Command: z
1392:
1393: Continue until the next address.
1394: )**" },
1.1.1.5 root 1395: };
1396:
1.1.1.20! root 1397: // cmdbuf を cmdargs... に分解する。
1.1 root 1398: void
1.1.1.3 root 1399: Debugger::ParseCmdbuf()
1.1 root 1400: {
1.1.1.3 root 1401: int pos;
1402: int start;
1403:
1404: pos = 0;
1.1.1.20! root 1405: cmdargs.clear();
1.1 root 1406:
1407: // 引数を空白で分解
1.1.1.3 root 1408: for (;;) {
1409: // 先頭の空白はスキップ
1410: for (; cmdbuf[pos] != '\0'; pos++) {
1411: if (!isspace((unsigned int)cmdbuf[pos]))
1412: break;
1413: }
1414: if (cmdbuf[pos] == '\0')
1.1 root 1415: break;
1416:
1.1.1.3 root 1417: // 終わりを探す
1418: start = pos;
1419: for (; cmdbuf[pos] != '\0'; pos++) {
1420: if (isspace((unsigned int)cmdbuf[pos]))
1.1 root 1421: break;
1422: }
1.1.1.20! root 1423: cmdargs.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2 root 1424: }
1425: // デバッグ表示
1426: if (0) {
1.1.1.20! root 1427: for (int i = 0 ; i < cmdargs.size(); i++) {
! 1428: printf("cmdargs[%d]=|%s|\n", i, cmdargs[i].c_str());
1.1.1.2 root 1429: }
1430: }
1.1 root 1431: }
1432:
1433: //
1434: // デバッガコマンド
1435: //
1436:
1437: // ブレークポイント
1.1.1.13 root 1438: // b ... 一覧表示
1439: // b #n ... #n を削除
1440: // b [<cpu>,]<addr> [<skip>] ... 設定
1.1.1.12 root 1441: Debugger::CmdAct
1.1 root 1442: Debugger::cmd_b()
1443: {
1444: // 引数なしなら一覧表示
1.1.1.20! root 1445: if (cmdargs.size() < 2) {
1.1 root 1446: return cmd_b_list();
1447: }
1448:
1449: // 引数取得
1.1.1.20! root 1450: if (cmdargs[1][0] == '#') {
1.1 root 1451: // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.10 root 1452: cmd_b_delete();
1.1.1.12 root 1453: return CmdAct::Stay;
1.1 root 1454: }
1455:
1.1.1.4 root 1456: return cmd_b_set(BreakpointType::Address);
1457: }
1458:
1459: // メモリブレークポイントの設定
1.1.1.13 root 1460: // bm [<cpu>,]<addr> [<skip>]
1.1.1.12 root 1461: Debugger::CmdAct
1.1.1.4 root 1462: Debugger::cmd_bm()
1463: {
1.1.1.13 root 1464: // XXX m88k のみ未サポート
1465: if (curmd->arch != CPUArch::M88xx0) {
1.1.1.20! root 1466: fprintf(cons, "bm: Not supported yet on m68k.\n");
1.1.1.12 root 1467: return CmdAct::Stay;
1.1.1.4 root 1468: }
1.1.1.12 root 1469: return cmd_b_set(BreakpointType::Memory);
1.1.1.4 root 1470: }
1471:
1472: // type が違うだけの各種ブレークポイント設定の共通部分。
1.1.1.20! root 1473: // すぐ下の ParseOptBreakAddr() と限りなく似ている。
1.1.1.12 root 1474: Debugger::CmdAct
1.1.1.4 root 1475: Debugger::cmd_b_set(BreakpointType type)
1476: {
1477: breakpoint_t bp;
1.1.1.13 root 1478: std::string cpustr;
1479: std::string addrstr;
1.1.1.4 root 1480:
1.1.1.20! root 1481: if (cmdargs.size() < 2) {
! 1482: fprintf(cons, "Usage: %s [<cpu>,]<addr> [<skipcount>]\n",
! 1483: cmdargs[0].c_str());
1.1.1.12 root 1484: return CmdAct::Stay;
1.1.1.4 root 1485: }
1486:
1.1.1.13 root 1487: // まず CPU を分離
1.1.1.20! root 1488: auto pos = cmdargs[1].find(',');
1.1.1.13 root 1489: if (pos == std::string::npos) {
1490: // CPU 指定なし
1.1.1.20! root 1491: addrstr = cmdargs[1];
1.1.1.13 root 1492: } else {
1493: // CPU 指定あり
1.1.1.20! root 1494: cpustr = cmdargs[1].substr(0, pos);
! 1495: addrstr = cmdargs[1].substr(pos + 1);
1.1.1.13 root 1496: }
1497:
1.1.1.20! root 1498: // CPU
! 1499: bp.md = ParseCPU(cpustr);
! 1500: if (bp.md == NULL) {
! 1501: fprintf(cons, "Invalid CPU Name.\n");
! 1502: return CmdAct::Stay;
! 1503: }
1.1.1.4 root 1504: // アドレス
1.1.1.13 root 1505: if (!ParseAddr(addrstr.c_str(), &bp.addr)) {
1.1.1.12 root 1506: return CmdAct::Stay;
1.1.1.4 root 1507: }
1508: // あればスキップカウント
1.1.1.20! root 1509: if (cmdargs.size() > 2) {
! 1510: bp.skip = atoi(cmdargs[2].c_str());
1.1.1.4 root 1511: }
1512:
1513: // 空いてるところにセット
1514: // (よく似たエントリがあっても干渉しない)
1515: bp.type = type;
1.1.1.20! root 1516: int bi = AddBreakpoint(bp);
1.1.1.4 root 1517: if (bi == -1) {
1.1.1.20! root 1518: fprintf(cons, "Breakpoints full.\n");
1.1 root 1519: } else {
1.1.1.20! root 1520: fprintf(cons, "Breakpoint #%d added.\n", bi);
1.1.1.3 root 1521: }
1.1.1.12 root 1522: return CmdAct::Stay;
1.1.1.4 root 1523: }
1524:
1.1.1.20! root 1525: // コマンドラインオプション -b(--b) の引数 str からのブレークポイント(アドレス)
! 1526: // を登録する。
! 1527: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
! 1528: // すぐ上の cmd_b_set() と限りなく似ている。
! 1529: bool
! 1530: Debugger::ParseOptBreakAddr(const std::string& str)
! 1531: {
! 1532: breakpoint_t bp;
! 1533: std::string cpustr;
! 1534: std::string addrstr;
! 1535: std::string skipstr;
! 1536:
! 1537: // "," で分離。
! 1538: auto args = string_split(str, ',');
! 1539: if (args.size() == 1) {
! 1540: // 1個ならアドレスのみ。
! 1541: addrstr = args[0];
! 1542: } else if (args.size() == 2) {
! 1543: // 2個なら cpu か skip のどちらかが省略されている…。
! 1544: // 先頭が CPU 文字列かどうかで判定する。
! 1545: if (IsCPUStr(args[0])) {
! 1546: cpustr = args[0];
! 1547: addrstr = args[1];
! 1548: } else {
! 1549: addrstr = args[0];
! 1550: skipstr = args[1];
! 1551: }
! 1552: } else if (args.size() == 3) {
! 1553: cpustr = args[0];
! 1554: addrstr = args[1];
! 1555: skipstr = args[2];
! 1556: } else {
! 1557: warnx("--b \"%s\": Syntax error.", str.c_str());
! 1558: return false;
! 1559: }
! 1560:
! 1561: // <cpu> を取り出す。
! 1562: bp.md = ParseCPU(cpustr);
! 1563: if (bp.md == NULL) {
! 1564: warnx("--b \"%s\": Invalid CPU Name.", str.c_str());
! 1565: return false;
! 1566: }
! 1567:
! 1568: // <addr> を取り出す。
! 1569: if (ParseVerbHex(addrstr.c_str(), &bp.addr) == false) {
! 1570: warnx("--b \"%s\": Invalid address.", str.c_str());
! 1571: return false;
! 1572: }
! 1573:
! 1574: // <skip> を取り出す。
! 1575: if (skipstr.empty() == false) {
! 1576: bp.skip = atoi(skipstr.c_str());
! 1577: }
! 1578:
! 1579: // 登録。
! 1580: bp.type = BreakpointType::Address;
! 1581: if (AddBreakpoint(bp) < 0) {
! 1582: warnx("--b \"%s\": Breakpoints full.", str.c_str());
! 1583: return false;
! 1584: }
! 1585: return true;
! 1586: }
! 1587:
1.1.1.10 root 1588: // ブレークポイント個別削除
1589: // (引数の先頭が '#' なことを判定したところで呼ばれる)
1590: // b #<n>
1.1.1.12 root 1591: Debugger::CmdAct
1.1.1.10 root 1592: Debugger::cmd_b_delete()
1593: {
1.1.1.20! root 1594: int n = atoi(&cmdargs[1][1]);
1.1.1.10 root 1595: if (n < 0 || n >= bpoint.size()) {
1.1.1.20! root 1596: fprintf(cons, "Invalid breakpoint number: %d\n", n);
1.1.1.12 root 1597: return CmdAct::Stay;
1.1.1.10 root 1598: }
1599:
1600: auto& bp = bpoint[n];
1601: if (bp.type == BreakpointType::Unused) {
1.1.1.20! root 1602: fprintf(cons, "Breakpoint #%d already removed.\n", n);
1.1.1.12 root 1603: return CmdAct::Stay;
1.1.1.10 root 1604: }
1605:
1.1.1.20! root 1606: fprintf(cons, "Breakpoint #%d removed.\n", n);
1.1.1.10 root 1607: bp.type = BreakpointType::Unused;
1608: // 今登録されている命令ブレークの必要命令長を再計算
1609: RecalcInstMask();
1.1.1.12 root 1610: return CmdAct::Stay;
1.1.1.10 root 1611: }
1612:
1.1.1.4 root 1613: // 命令ブレークポイントの設定
1.1.1.13 root 1614: // bi [<cpu>,]<insn>[:<mask>] [<skip>]
1.1.1.12 root 1615: Debugger::CmdAct
1.1.1.4 root 1616: Debugger::cmd_bi()
1617: {
1.1.1.13 root 1618: std::string cpustr;
1.1.1.4 root 1619: std::string inststr;
1.1.1.20! root 1620: std::string skipstr;
! 1621: std::string errstr;
1.1.1.4 root 1622:
1.1.1.20! root 1623: if (cmdargs.size() < 2) {
! 1624: fprintf(cons, "Usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.12 root 1625: return CmdAct::Stay;
1.1 root 1626: }
1627:
1.1.1.13 root 1628: // CPU をまず分離
1.1.1.20! root 1629: auto pos = cmdargs[1].find(',');
1.1.1.13 root 1630: if (pos == std::string::npos) {
1631: // CPU 指定なし
1.1.1.20! root 1632: inststr = cmdargs[1];
1.1.1.13 root 1633: } else {
1634: // CPU 指定あり
1.1.1.20! root 1635: cpustr = cmdargs[1].substr(0, pos);
! 1636: inststr = cmdargs[1].substr(pos + 1);
1.1.1.13 root 1637: }
1638:
1.1.1.20! root 1639: // あればスキップカウント
! 1640: if (cmdargs.size() > 2) {
! 1641: skipstr = cmdargs[2];
! 1642: }
! 1643:
! 1644: // 命令ブレークポイントを登録。
! 1645: int bi = AddBreakInst(cpustr, inststr, skipstr, &errstr);
! 1646: if (errstr.empty() == false) {
! 1647: fprintf(cons, "%s\n", errstr.c_str());
! 1648: return CmdAct::Stay;
! 1649: }
! 1650: fprintf(cons, "Breakpoint #%d added.\n", bi);
! 1651: return CmdAct::Stay;
! 1652: }
! 1653:
! 1654: // --bi オプションの引数 str から命令ブレークポイントを登録する。
! 1655: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
! 1656: bool
! 1657: Debugger::ParseOptBreakInst(const std::string& str)
! 1658: {
! 1659: std::string cpustr;
! 1660: std::string inststr;
! 1661: std::string skipstr;
! 1662: std::string errstr;
! 1663:
! 1664: // "," で分離。
! 1665: auto args = string_split(str, ',');
! 1666: if (args.size() == 1) {
! 1667: // 1個なら命令のみ。
! 1668: inststr = args[0];
! 1669: } else if (args.size() == 2) {
! 1670: // 2個なら cpu か skip のどちらかが省略されている…。
! 1671: // 先頭が CPU 文字列かどうかで判定する。
! 1672: if (IsCPUStr(args[0])) {
! 1673: cpustr = args[0];
! 1674: inststr = args[1];
! 1675: } else {
! 1676: inststr = args[0];
! 1677: skipstr = args[1];
! 1678: }
! 1679: } else if (args.size() == 3) {
! 1680: cpustr = args[0];
! 1681: inststr = args[1];
! 1682: skipstr = args[2];
! 1683: } else {
! 1684: warnx("--bi \"%s\": Syntax error.", str.c_str());
! 1685: return false;
! 1686: }
! 1687:
! 1688: // 命令ブレークポイントを登録。
! 1689: AddBreakInst(cpustr, inststr, skipstr, &errstr);
! 1690: if (errstr.empty() == false) {
! 1691: warnx("--bi \"%s\": %s", str.c_str(), errstr.c_str());
! 1692: return false;
! 1693: }
! 1694: return true;
! 1695: }
! 1696:
! 1697: // 命令ブレークポイントを登録する。cmd_bi と --bi の後半の共通部分。
! 1698: // 成功すれば *errstr は empty でブレークポイント番号(>=0)を返す。
! 1699: // 失敗すれば *errstr をセットして -1 を返す。
! 1700: int
! 1701: Debugger::AddBreakInst(const std::string& cpustr, const std::string& argstr,
! 1702: const std::string& skipstr, std::string *errstr)
! 1703: {
! 1704: DebuggerMD *md;
! 1705: breakpoint_t bp;
! 1706: std::string inststr;
! 1707: std::string maskstr;
! 1708: int instlen;
! 1709: int masklen;
! 1710:
! 1711: // CPU 部チェック
! 1712: md = ParseCPU(cpustr);
! 1713: if (md == NULL) {
! 1714: *errstr = std::string("Invalid CPU Name.");
! 1715: return CmdAct::Stay;
! 1716: }
! 1717: bp.md = md;
! 1718:
! 1719: // 命令部。
! 1720: // ニーモニックが指定できたら便利。
! 1721: inststr = md->ParseInst(argstr);
! 1722: if (inststr.empty() == false) {
1.1.1.4 root 1723: masklen = -1;
1724: } else {
1.1.1.20! root 1725: auto pos = argstr.find(':');
! 1726: if (pos == std::string::npos) {
! 1727: // マスク指定なし。
! 1728: inststr = argstr;
! 1729: masklen = -1;
! 1730: } else {
! 1731: // マスク指定あり。
! 1732: inststr = argstr.substr(0, pos);
! 1733: maskstr = argstr.substr(pos + 1);
! 1734: masklen = maskstr.size();
! 1735: }
1.1.1.4 root 1736: }
1.1.1.20! root 1737: instlen = inststr.size();
1.1.1.4 root 1738:
1739: if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
1.1.1.20! root 1740: *errstr = string_format("Invalid instruction: %s", argstr.c_str());
! 1741: return -1;
1.1.1.4 root 1742: }
1.1.1.20! root 1743: if (instlen % (md->inst_bytes * 2) != 0) {
! 1744: *errstr = string_format("Invalid instruction length: %s",
! 1745: argstr.c_str());
! 1746: return -1;
1.1.1.4 root 1747: }
1748:
1749: // マスク部チェック
1750: bp.mask = 0xffffffff;
1751: if (masklen != -1) {
1752: if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
1.1.1.20! root 1753: *errstr = string_format("Invalid mask value: %s", argstr.c_str());
! 1754: return -1;
1.1.1.4 root 1755: }
1756: if (masklen != instlen) {
1.1.1.20! root 1757: *errstr = std::string("<inst>:<mask> must be the same length");
! 1758: return -1;
1.1 root 1759: }
1760: }
1.1.1.4 root 1761: // 8バイト未満なら左詰め。
1.1.1.20! root 1762: if (md->inst_bytes < 4 && instlen < 8) {
1.1.1.4 root 1763: bp.inst <<= 32 - instlen * 4;
1764: bp.mask <<= 32 - instlen * 4;
1765: }
1766:
1.1.1.20! root 1767: // <skip> を取り出す。
! 1768: if (skipstr.empty() == false) {
! 1769: bp.skip = atoi(skipstr.c_str());
1.1.1.4 root 1770: }
1771:
1772: // 空いてるところにセット
1773: bp.type = BreakpointType::Instruction;
1.1.1.20! root 1774: int bi = AddBreakpoint(bp);
! 1775: if (bi < 0) {
! 1776: *errstr = std::string("Breakpoints full.");
! 1777: return bi;
1.1.1.4 root 1778: }
1779:
1.1.1.20! root 1780: // 今登録されている命令ブレークの必要命令長を再計算。
1.1.1.4 root 1781: RecalcInstMask();
1.1.1.20! root 1782:
! 1783: return bi;
1.1.1.4 root 1784: }
1785:
1.1.1.20! root 1786: // 例外ブレークポイントの設定。
1.1.1.13 root 1787: // bv [<cpu>,]<vector_number>[-<end_number>] [<skip>]
1.1.1.12 root 1788: Debugger::CmdAct
1.1.1.4 root 1789: Debugger::cmd_bv()
1790: {
1.1.1.13 root 1791: std::string cpustr;
1.1.1.20! root 1792: std::string vecstr;
! 1793: std::string skipstr;
! 1794: std::string errstr;
1.1.1.4 root 1795:
1.1.1.20! root 1796: if (cmdargs.size() < 2) {
! 1797: fprintf(cons, "Usage: bv [<cpu>,]<vec#>[-<end#>] [<skipcount>]\n");
1.1.1.12 root 1798: return CmdAct::Stay;
1.1.1.4 root 1799: }
1800:
1.1.1.13 root 1801: // CPU をまず分離
1.1.1.20! root 1802: auto pos = cmdargs[1].find(',');
1.1.1.13 root 1803: if (pos == std::string::npos) {
1804: // CPU 指定なし
1.1.1.20! root 1805: vecstr = cmdargs[1];
1.1.1.13 root 1806: } else {
1807: // CPU 指定あり
1.1.1.20! root 1808: cpustr = cmdargs[1].substr(0, pos);
! 1809: vecstr = cmdargs[1].substr(pos + 1);
! 1810: }
! 1811:
! 1812: if (cmdargs.size() > 2) {
! 1813: skipstr = cmdargs[2];
1.1.1.13 root 1814: }
1.1.1.20! root 1815:
! 1816: // ベクタブレークポイントを登録。
! 1817: int bi = AddBreakVector(cpustr, vecstr, skipstr, &errstr);
! 1818: if (errstr.empty() == false) {
! 1819: fprintf(cons, "%s\n", errstr.c_str());
! 1820: return CmdAct::Stay;
! 1821: }
! 1822: fprintf(cons, "Breakpoint #%d added.\n", bi);
! 1823:
! 1824: // この CPU 側に、すでに来ている例外をクリア。
! 1825: // ブレークポイント設定の有無に関わらず例外が起きたら CPU 側から常に
! 1826: // 通知されている。これをクリアするのは CheckAllBreakpoints() で、これは
! 1827: // 命令間(命令前)に呼ばれるやつ、なのでこうなる。
! 1828: // 1. 例外が起きると md->bv_vector がセットされる
! 1829: // 2. 例外ブレークポイントを設定していないとこれがクリアされない
! 1830: // 3. bv コマンドで例外ブレークを新たに設定すると、次の命令境界で
! 1831: // 1.のベクタが反応してしまう。
! 1832: // 命令ごととかにクリアしてもいいかも知れないが、ここでブレークポイントを
! 1833: // 設定したのだから、それ以前の事象には反応すべきでない、という意味では
! 1834: // ここでもいいか?
! 1835:
! 1836: // bp.md は AddBreakpoint() で bpoint 登録時にセットされるので
! 1837: // インデックスから bp をもう一度読み込む。
! 1838: auto bp = bpoint[bi];
! 1839: bp.md->bv_vector = -1;
! 1840:
! 1841: return CmdAct::Stay;
! 1842: }
! 1843:
! 1844: // --bv オプションの引数 str からベクタブレークポイントを登録する。
! 1845: // 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。
! 1846: bool
! 1847: Debugger::ParseOptBreakVector(const std::string& str)
! 1848: {
! 1849: std::string cpustr;
! 1850: std::string vecstr;
! 1851: std::string skipstr;
! 1852: std::string errstr;
! 1853:
! 1854: // "," で分離。
! 1855: auto args = string_split(str, ',');
! 1856: if (args.size() == 1) {
! 1857: // 1個ならベクタのみ。
! 1858: vecstr = args[0];
! 1859: } else if (args.size() == 2) {
! 1860: // 2個なら cpu か skip のどちらかが省略されている…。
! 1861: // 先頭が CPU 文字列かどうかで判定する。
! 1862: if (IsCPUStr(args[0])) {
! 1863: cpustr = args[0];
! 1864: vecstr = args[1];
! 1865: } else {
! 1866: vecstr = args[0];
! 1867: skipstr = args[1];
! 1868: }
! 1869: } else if (args.size() == 3) {
! 1870: cpustr = args[0];
! 1871: vecstr = args[1];
! 1872: skipstr = args[2];
! 1873: } else {
! 1874: warnx("--bv \"%s\": Syntax error.", str.c_str());
! 1875: return false;
! 1876: }
! 1877:
! 1878: // ベクタブレークポイントを登録。
! 1879: AddBreakVector(cpustr, vecstr, skipstr, &errstr);
! 1880: if (errstr.empty() == false) {
! 1881: warnx("--bv \"%s\": %s", str.c_str(), errstr.c_str());
! 1882: return false;
! 1883: }
! 1884: return true;
! 1885: }
! 1886:
! 1887: // ベクタブレークポイントを登録する。cmd_bv と --bv の後半の共通部分。
! 1888: // 成功すれば *errstr は empty でブレークポイント番号(>=0)を返す。
! 1889: // 失敗すれば *errstr をセットして -1 を返す。
! 1890: int
! 1891: Debugger::AddBreakVector(const std::string& cpustr, const std::string& vecstr,
! 1892: const std::string& skipstr, std::string *errstr)
! 1893: {
! 1894: uint32 maxvector;
! 1895: breakpoint_t bp;
! 1896: DebuggerMD *md;
! 1897:
! 1898: // CPU 部。ベクタ解釈にも使う。
1.1.1.13 root 1899: md = ParseCPU(cpustr);
1.1.1.20! root 1900: if (md == NULL) {
! 1901: *errstr = std::string("Invalid CPU Name.");
! 1902: return CmdAct::Stay;
! 1903: }
! 1904: bp.md = md;
1.1.1.13 root 1905:
1.1.1.20! root 1906: // ベクタ数 (最大値) を取得。
! 1907: if (md->arch == CPUArch::HD64180) {
! 1908: // XXX あってる?
! 1909: maxvector = 256;
! 1910: } else {
! 1911: maxvector = pVectorTable->Size();
! 1912: }
! 1913:
! 1914: // ベクタを取り出す。
! 1915: auto pos = vecstr.find('-');
1.1.1.4 root 1916: if (pos == std::string::npos) {
1917: // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
1.1.1.20! root 1918: if (ParseVector(md, vecstr.c_str(), (uint32 *)&bp.vec1) == false) {
! 1919: *errstr = string_format("Invalid vector number: %s",
! 1920: vecstr.c_str());
! 1921: return -1;
1.1.1.4 root 1922: }
1923: bp.vec2 = bp.vec1;
1924: } else {
1925: // ベクタ番号(範囲指定 [vec1, vec2])
1.1.1.20! root 1926: std::string vec1 = vecstr.substr(0, pos);
! 1927: std::string vec2 = vecstr.substr(pos + 1);
1.1.1.4 root 1928:
1.1.1.20! root 1929: // "-vec2" なら vec1 = 0、
! 1930: // "vec1-" なら vec2 は最大値 (機種による)、とする。
! 1931:
! 1932: if (vec1.empty()) {
! 1933: bp.vec1 = 0;
! 1934: } else if (ParseVector(md, vec1.c_str(), (uint32 *)&bp.vec1) == false) {
! 1935: *errstr = string_format("Invalid first vector number: %s",
! 1936: vecstr.c_str());
! 1937: return -1;
1.1.1.4 root 1938: }
1.1.1.20! root 1939: if (vec2.empty()) {
! 1940: bp.vec2 = maxvector - 1;
! 1941: } else if (ParseVector(md, vec2.c_str(), (uint32 *)&bp.vec2) == false) {
! 1942: *errstr = string_format("Invalid last vector number: %s",
! 1943: vecstr.c_str());
! 1944: return -1;
1.1.1.4 root 1945: }
1946: }
1947:
1948: // 範囲チェック
1.1.1.20! root 1949: if (bp.vec1 < 0 || bp.vec1 >= maxvector) {
! 1950: *errstr = string_format("Out of range: $%x", bp.vec1);
! 1951: return -1;
! 1952: }
! 1953: if (bp.vec2 < 0 || bp.vec2 >= maxvector) {
! 1954: *errstr = string_format("Out of range: $%x", bp.vec2);
! 1955: return -1;
1.1.1.4 root 1956: }
1957:
1958: // 大小が逆なら入れ替える?
1959: if (bp.vec1 > bp.vec2) {
1960: int tmp;
1961: tmp = bp.vec1;
1962: bp.vec1 = bp.vec2;
1963: bp.vec2 = tmp;
1964: }
1965:
1.1.1.20! root 1966: // <skip> を取り出す。
! 1967: if (skipstr.empty() == false) {
! 1968: bp.skip = atoi(skipstr.c_str());
1.1.1.4 root 1969: }
1.1 root 1970:
1971: // 空いてるところにセット
1.1.1.4 root 1972: bp.type = BreakpointType::Exception;
1.1.1.20! root 1973: int bi = AddBreakpoint(bp);
! 1974: if (bi < 0) {
! 1975: *errstr = std::string("Breakpoints full.");
1.1 root 1976: }
1.1.1.20! root 1977: return bi;
1.1 root 1978: }
1979:
1.1.1.13 root 1980: // ベクタ名かベクタ番号をパースする。
1.1.1.20! root 1981: // ベクタ番号は10進数で表記されることが多いので、10進/16進どちらも対応する。
! 1982: // - "$" か "0x" から始まれば16進数。
! 1983: // - それ以外で [0-9] から始まれば10進数。"0" プレフィックスを8進数にはしない。
! 1984: // - それ以外ならベクタ名とみなす。
1.1.1.13 root 1985: bool
1986: Debugger::ParseVector(DebuggerMD *md, const char *arg, uint32 *valp)
1987: {
1.1.1.20! root 1988: assert(arg);
! 1989:
! 1990: if (arg[0] == '$') {
! 1991: // 1文字目以降が16進数。
! 1992: return ParseVerbHex(arg + 1, valp);
! 1993: }
! 1994: if (strncmp(arg, "0x", 2) == 0) {
! 1995: // 2文字目以降が16進数。
! 1996: return ParseVerbHex(arg + 2, valp);
! 1997: }
! 1998: if ('0' <= arg[0] && arg[0] <= '9') {
! 1999: // 先頭から10進数。
! 2000: uint32 val;
! 2001: char *end;
! 2002: errno = 0;
! 2003: val = strtoul(arg, &end, 10);
! 2004: if (*end != '\0') {
! 2005: return false;
! 2006: }
! 2007: if (errno == ERANGE) {
! 2008: return false;
! 2009: }
! 2010: *valp = val;
1.1.1.13 root 2011: return true;
2012: }
2013:
1.1.1.20! root 2014: // 数値に出来なければ、機種ごとにベクタ名と比較する。
1.1.1.13 root 2015: if (md->arch == CPUArch::HD64180) {
2016: static std::vector<const char *> table = {
2017: "trap",
2018: "nmi",
2019: "int0",
2020: "int1",
2021: "int2",
2022: "inpcap",
2023: "outcmp",
2024: "timeov",
2025: "timer0",
2026: "timer1",
2027: "dma0",
2028: "dma1",
2029: "csio",
2030: "asci0",
2031: "asci1",
2032: };
2033: for (int i = 0, end = table.size(); i < end; i++) {
2034: if (strcasecmp(arg, table[i]) == 0) {
2035: *valp = i;
2036: return true;
2037: }
2038: }
2039: }
2040:
2041: return false;
2042: }
2043:
1.1 root 2044: // ブレークポイント一覧表示
1.1.1.12 root 2045: Debugger::CmdAct
1.1 root 2046: Debugger::cmd_b_list()
2047: {
1.1.1.9 root 2048: ShowMonitor(bpoint_monitor);
1.1.1.12 root 2049: return CmdAct::Stay;
1.1.1.4 root 2050: }
2051:
2052: // ブレークポイント一覧 (モニタ)
2053: void
1.1.1.9 root 2054: Debugger::MonitorUpdateBpoint(Monitor *, TextScreen& monitor)
1.1.1.4 root 2055: {
2056: // 0 1 2 3 4 5 6
2057: // 0123456789012345678901234567890123456789012345678901234567890
1.1.1.20! root 2058: // No CPU Type Parameter Matched Skip
! 2059: // #0 xp addr $01234567 123456789 123456789/123456789
! 2060: // #1 mpu inst 00000000:00000000
! 2061: // #2 mpu excp $00-$00
1.1.1.4 root 2062:
2063: monitor.Clear();
1.1.1.20! root 2064: monitor.Print(0, 0, "No CPU Type Parameter");
! 2065: monitor.Print(30, 0, "Matched");
! 2066: monitor.Print(41, 0, "Skip");
1.1 root 2067:
1.1.1.3 root 2068: for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 root 2069: const auto& bp = bpoint[i];
2070: int y = i + 1;
2071:
2072: monitor.Print(0, y, "#%d", i);
2073:
1.1.1.20! root 2074: if (bp.type == BreakpointType::Unused) {
1.1.1.4 root 2075: continue;
1.1.1.20! root 2076: }
! 2077: monitor.Print(3, y, "%s", bp.md ? bp.md->GetName().c_str() : "?");
1.1.1.4 root 2078:
1.1.1.20! root 2079: // 種別ごとの表示
! 2080: switch (bp.type) {
1.1.1.4 root 2081: case BreakpointType::Address:
1.1.1.20! root 2082: monitor.Print(7, y, "addr $%08x", bp.addr);
1.1.1.4 root 2083: break;
1.1.1.20! root 2084:
1.1.1.4 root 2085: case BreakpointType::Memory:
1.1.1.20! root 2086: monitor.Print(7, y, "mem $%08x", bp.addr);
1.1.1.4 root 2087: break;
2088:
2089: case BreakpointType::Exception:
1.1.1.20! root 2090: {
! 2091: uint vd; // ベクタの桁数
! 2092: if (bp.md->arch == CPUArch::M88xx0) {
! 2093: vd = 3;
! 2094: } else {
! 2095: vd = 2;
! 2096: }
! 2097: monitor.Print(7, y, "excp $%0*x", vd, bp.vec1);
1.1.1.4 root 2098: if (bp.vec2 != bp.vec1) {
1.1.1.20! root 2099: monitor.Print(13 + vd, y, "-$%0*x", vd, bp.vec2);
1.1.1.4 root 2100: }
2101: break;
1.1.1.20! root 2102: }
1.1.1.4 root 2103:
2104: case BreakpointType::Instruction:
1.1.1.20! root 2105: monitor.Puts(7, y, "inst");
! 2106: for (int n = bp.md->inst_bytes; n <= 4; n += bp.md->inst_bytes) {
! 2107: // 左詰め n バイト分のマスク。
! 2108: int bits = (4 - n) * 8;
! 2109: uint imask = 0xffffffffU << bits;
! 2110: // n が 4 だと ~imask の式は使えないが無条件で入る。
! 2111: if (n == 4 || ((bp.inst | bp.mask) & ~imask) == 0) {
! 2112: if (bp.mask == imask) {
! 2113: monitor.Print(12, y, "%0*x", n * 2, (bp.inst >> bits));
! 2114: } else {
! 2115: monitor.Print(12, y, "%0*x:%0*x",
! 2116: n * 2, (bp.inst >> bits),
! 2117: n * 2, (bp.mask >> bits));
! 2118: }
! 2119: break;
1.1.1.4 root 2120: }
2121: }
2122: break;
2123:
2124: default:
1.1.1.20! root 2125: monitor.Print(7, y, "type=%d", (int)bp.type);
1.1.1.4 root 2126: continue;
2127: }
2128:
2129: // マッチ回数
1.1.1.20! root 2130: monitor.Print(30, y, "%d", bp.matched);
1.1.1.4 root 2131:
2132: // スキップ
2133: if (bp.skip < 0) {
1.1.1.20! root 2134: monitor.Print(41, y, "forever");
1.1.1.4 root 2135: } else if (bp.skip > 0) {
1.1.1.20! root 2136: monitor.Print(41, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1 root 2137: }
2138: }
2139: }
2140:
2141: // ブレークポイント全削除
1.1.1.12 root 2142: Debugger::CmdAct
1.1 root 2143: Debugger::cmd_bx()
2144: {
1.1.1.3 root 2145: for (auto& bp : bpoint) {
1.1.1.4 root 2146: bp.type = BreakpointType::Unused;
1.1 root 2147: }
1.1.1.20! root 2148: fprintf(cons, "All breakpoints are removed.\n");
1.1.1.4 root 2149:
2150: // 今登録されている命令ブレークの必要命令長を再計算
2151: RecalcInstMask();
1.1.1.12 root 2152:
2153: return CmdAct::Stay;
1.1 root 2154: }
2155:
1.1.1.20! root 2156: // CPU 文字列なら true を返す。
! 2157: bool
! 2158: Debugger::IsCPUStr(const std::string& cpustr) const
! 2159: {
! 2160: if (cpustr.empty()) {
! 2161: return false;
! 2162: }
! 2163: if (ParseCPU(cpustr) == NULL) {
! 2164: return false;
! 2165: }
! 2166: return true;
! 2167: }
! 2168:
1.1.1.13 root 2169: // CPU 文字列から対応する MD を返す。
2170: // 一致しなければ NULL を返す。
2171: DebuggerMD *
2172: Debugger::ParseCPU(const std::string& cpustr) const
2173: {
2174: if (cpustr.empty()) {
2175: return curmd;
2176: } else if (cpustr == "xp") {
2177: return md_xp.get();
1.1.1.20! root 2178: } else if (cpustr == "mpu") {
1.1.1.13 root 2179: return md_mpu.get();
2180: }
2181: return NULL;
2182: }
2183:
1.1 root 2184: // ブレークポイントを設定。
1.1.1.20! root 2185: // new_bp のうち matched, skipremain はこちらで初期化する。
! 2186: // それ以外の type、md と type ごとに必要なパラメータを埋めてから呼ぶこと。
1.1.1.4 root 2187: // 設定できればその番号、できなければ -1 を返す。
1.1 root 2188: int
1.1.1.20! root 2189: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1 root 2190: {
1.1.1.3 root 2191: for (int i = 0; i < bpoint.size(); i++) {
2192: auto& bp = bpoint[i];
1.1.1.4 root 2193: if (bp.type == BreakpointType::Unused) {
2194: bp = new_bp;
1.1.1.20! root 2195: // 本当は assert レベルだが、とは言えここで死なれるとつらいので。
! 2196: if (bp.md == NULL) {
! 2197: bp.md = curmd;
! 2198: }
1.1.1.4 root 2199: bp.matched = 0;
2200: if (bp.skip > 0) {
2201: bp.skipremain = bp.skip;
2202: } else {
2203: bp.skipremain = 0;
2204: }
2205:
1.1 root 2206: return i;
2207: }
2208: }
2209: return -1;
2210: }
2211:
1.1.1.4 root 2212: // すべての命令ブレークのマスクのうち最長のものを再計算する。
2213: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
2214: void
2215: Debugger::RecalcInstMask()
2216: {
2217: uint32 mask;
2218: bi_need_bytes = 0;
2219:
2220: // 登録されている命令ブレークの最長マスクを求める
2221: mask = 0;
2222: for (const auto& bp : bpoint) {
2223: if (bp.type == BreakpointType::Instruction) {
2224: mask |= bp.mask;
2225: }
2226: }
2227:
2228: // mask の Number of Trailing Zero を求める。
2229: // (x & -x) で x の最も下の立ってるビットだけを立てる、
2230: // (x & -x) -1 でそれより下の全ビットを立てる、
2231: // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
2232: int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
2233: // 上位側から数えたマスクに必要なビット数
2234: int mlen = 32 - ntz;
2235: // 命令語単位に切り上げる
1.1.1.13 root 2236: mlen = roundup(mlen, curmd->inst_bytes * 8);
1.1.1.4 root 2237: // バイト数に変換
2238: mlen /= 8;
2239:
2240: if (mlen > bi_need_bytes) {
2241: bi_need_bytes = mlen;
2242: }
2243: }
2244:
2245: // ブランチ履歴、例外履歴表示
2246: // brhist [<maxlines>]
2247: // exhist [<maxlines>]
2248: // <maxlines> は表示する最大エントリ数。
2249: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
2250: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
2251: // する。
1.1.1.12 root 2252: Debugger::CmdAct
1.1 root 2253: Debugger::cmd_brhist()
2254: {
1.1.1.13 root 2255: auto brhist = gMainApp.GetObject<BranchHistory>(OBJ_MPU_BRHIST);
2256: return cmd_hist_common(*brhist);
1.1.1.4 root 2257: }
1.1.1.12 root 2258: Debugger::CmdAct
1.1.1.4 root 2259: Debugger::cmd_exhist()
2260: {
1.1.1.13 root 2261: auto exhist = gMainApp.GetObject<BranchHistory>(OBJ_MPU_EXHIST);
2262: return cmd_hist_common(*exhist);
1.1.1.4 root 2263: }
1.1.1.3 root 2264:
1.1.1.4 root 2265: // ブランチ履歴、例外履歴表示の共通部分。
1.1.1.12 root 2266: Debugger::CmdAct
1.1.1.9 root 2267: Debugger::cmd_hist_common(BranchHistory& hist)
1.1.1.4 root 2268: {
2269: // 表示最大行数(と向き)
2270: // 向きは bottom_to_top = true が新しいほうを下とする方向。
2271: int maxlines = 20;
2272: bool bottom_to_top = true;
1.1.1.20! root 2273: if (cmdargs.size() > 1) {
! 2274: maxlines = atoi(cmdargs[1].c_str());
1.1.1.4 root 2275: if (maxlines < 0) {
2276: bottom_to_top = false;
2277: maxlines = -maxlines;
1.1.1.3 root 2278: }
1.1.1.4 root 2279:
2280: if (maxlines < 1)
2281: maxlines = 1;
2282: if (maxlines > 256)
2283: maxlines = 256;
2284: }
2285:
2286: // コンソールでは空エントリの行は表示したくないので、
2287: // 先にエントリ数を調べる。
2288: int used = hist.GetUsed();
2289:
2290: // 表示行数 + 1行はヘッダ分
2291: int lines = std::min(used, maxlines) + 1;
2292:
2293: // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
1.1.1.17 root 2294: auto *histmon = hist.monitor;
1.1.1.9 root 2295: TextScreen screen;
1.1.1.19 root 2296: screen.Init(histmon->GetCol(), lines);
1.1.1.4 root 2297:
2298: // コンソールでは下が新しいの順のほうがいい
2299: if (bottom_to_top) {
1.1.1.9 root 2300: screen.userdata |= BranchHistory::BottomToTop;
1.1.1.3 root 2301: }
1.1.1.4 root 2302:
2303: // 表示
1.1.1.9 root 2304: MONITOR_UPDATE(histmon, screen);
2305: ShowTextScreen(screen);
1.1.1.12 root 2306:
2307: return CmdAct::Stay;
1.1 root 2308: }
2309:
2310: // 実行再開(continue): c [<addr>]
2311: // <addr> 指定があれば <addr> まで実行。
1.1.1.12 root 2312: Debugger::CmdAct
1.1 root 2313: Debugger::cmd_c()
2314: {
1.1.1.20! root 2315: if (cmdargs.size() > 1) {
1.1 root 2316: // 引数があれば
1.1.1.13 root 2317: uint32 addr;
1.1.1.20! root 2318: if (!ParseAddr(cmdargs[1].c_str(), &addr)) {
1.1.1.12 root 2319: return CmdAct::Stay;
1.1 root 2320: }
1.1.1.13 root 2321: step_type = StepType::Addr;
2322: step_md = curmd;
2323: // ネイティブ命令長に丸める
2324: step_addr = addr & ~(curmd->inst_bytes - 1);
1.1 root 2325: }
1.1.1.12 root 2326: return CmdAct::Leave;
2327: }
2328:
2329: // 指定仮想時間実行: ct [<time>]
2330: // <time> が省略されたら前回指定した時間間隔で再実行。
2331: Debugger::CmdAct
2332: Debugger::cmd_ct()
2333: {
1.1.1.20! root 2334: if (cmdargs.size() > 1) {
1.1.1.12 root 2335: // 引数があれば
1.1.1.20! root 2336: if (!ParseTime(cmdargs[1].c_str(), &ct_timespan)) {
1.1.1.12 root 2337: return CmdAct::Stay;
2338: }
2339: }
2340: if (ct_timespan == 0) {
1.1.1.20! root 2341: fprintf(cons, "Time must be specified.\n");
1.1.1.12 root 2342: return CmdAct::Stay;
2343: }
1.1.1.13 root 2344: step_type = StepType::Time;
2345: step_md = curmd;
2346: step_time = scheduler->GetVirtTime() + ct_timespan;
1.1.1.12 root 2347:
2348: return CmdAct::Leave;
1.1 root 2349: }
2350:
1.1.1.5 root 2351: // 引数などからアドレスを取得するごった煮共通ルーチン。
2352: //
1.1.1.6 root 2353: // 引数がなければ sa->addr には書き込まずに true で帰る (ので、呼び出し側が
2354: // 事前に適切なデフォルト値をセットしておくと、それがそのまま使われる)。
1.1.1.20! root 2355: // 引数が1つ(以上)あれば cmdargs[1] をアドレスとしてパースする。
1.1.1.6 root 2356: // アドレスには空間修飾子を前置可能、省略の場合はいずれも現在値を使う。
2357: // エラーならメッセージを表示して false を返す。
1.1.1.5 root 2358: // sa は in/out パラメータ。
1.1 root 2359: bool
1.1.1.15 root 2360: Debugger::GetAddr(busaddr *sa, bool default_data)
1.1 root 2361: {
1.1.1.20! root 2362: if (cmdargs.size() < 2) {
1.1.1.6 root 2363: // 引数なしなら、呼び出し側が sa にセットした前回値をそのまま使う。
1.1.1.5 root 2364: return true;
2365: }
2366:
1.1.1.6 root 2367: // 引数ありならパースする。アドレスが明示的に指定されたので、
2368: // ここから先の省略箇所は前回値ではなくデフォルト値で補完する。
2369:
1.1.1.20! root 2370: std::string& str = cmdargs[1];
1.1.1.5 root 2371: uint32 addr;
2372:
1.1.1.6 root 2373: // ':' があればその前はアドレス空間修飾子
2374: int mod_super = -1; // 'u'/'s'
2375: int mod_prog = -1; // 'd'/'p' or 'd'/'i'
2376: int mod_num = -1; // '0'..'7'
2377: auto addrpos = str.find(':');
2378: if (addrpos == std::string::npos) {
2379: // なければ先頭からアドレス
2380: addrpos = 0;
2381: } else {
2382: // ':' が途中にあれば、その前が修飾子。
2383: // ':' が先頭なら修飾子が空文字列という扱いでいいか。
2384: std::string mod = str.substr(0, addrpos);
2385: addrpos++;
2386: for (auto ch : mod) {
2387: if (ch == 'u') {
2388: if (mod_super == 1)
2389: goto error;
2390: mod_super = 0;
2391: } else if (ch == 's') {
2392: if (mod_super == 0)
2393: goto error;
2394: mod_super = 1;
2395: } else if (ch == 'd') {
2396: if (mod_prog == 1)
2397: goto error;
2398: mod_prog = 0;
2399: } else if (ch == 'p' || ch == 'i') {
2400: // m68k では 'P'rogram space、m88k では 'I'nstruction CMMU…
2401: if (mod_prog == 0)
2402: goto error;
2403: mod_prog = 1;
2404: } else if ('0' <= ch && ch <= '7') {
2405: int num = ch - '0';
1.1.1.13 root 2406: if (curmd->arch == CPUArch::M680x0) {
1.1.1.6 root 2407: // m68k では FC 指定は Super/User 指定を含んでおり、
2408: // 値指定は他の修飾子とは同時に指定できない。
2409: if (mod_super >= 0 || mod_prog >= 0 ||
2410: (mod_num >= 0 && mod_num != num)) {
2411: goto error;
2412: }
2413: // 有効な値は 1,2,5,6 のみ
2414: if (num == 0 || num == 3 || num == 4 || num == 7) {
1.1.1.20! root 2415: fprintf(cons, "Invalid function code %d.\n", num);
! 2416: return false;
1.1.1.6 root 2417: }
2418: mod_num = num;
2419: mod_super = mod_num >> 2; // FC2
2420: mod_prog = (mod_num & 3) - 1; // FC1,FC0
1.1.1.13 root 2421: } else if (curmd->arch == CPUArch::M88xx0) {
1.1.1.6 root 2422: // m88k では CMMU 指定と、Super/User 指定は独立。
2423: if (mod_prog >= 0 ||
2424: (mod_num >= 0 && mod_num != num)) {
2425: goto error;
2426: }
2427: mod_num = num;
2428: mod_prog = (mod_num & 1); // CMMU7 が Inst
1.1.1.13 root 2429: } else {
2430: fprintf(cons, "not yet\n");
1.1.1.6 root 2431: }
2432: } else {
1.1.1.20! root 2433: fprintf(cons, "Unknown address space modifier: %c\n", ch);
1.1.1.6 root 2434: return false;
2435: }
2436: continue;
2437:
2438: error:
1.1.1.20! root 2439: fprintf(cons, "Address space modifiers conflict: \"%s\"\n",
! 2440: mod.c_str());
1.1.1.6 root 2441: return false;
2442: }
2443:
2444: // 数値指定を機種ごとに読み替える
2445: if (mod_num >= 0) {
1.1.1.13 root 2446: if (curmd->arch == CPUArch::M680x0) {
1.1.1.6 root 2447: // m68k なら値指定で全部確定する
2448: // "1:" -> User/Data
2449: // "2:" -> User/Program
2450: // "5:" -> Super/Data
2451: // "6:" -> Super/Program
2452: mod_super = (mod_num & 4) ? true : false;
2453: mod_prog = (mod_num & 3) - 1;
1.1.1.13 root 2454: } else if (curmd->arch == CPUArch::M88xx0) {
1.1.1.6 root 2455: // m88k では mod_num を CMMU ID とする(?)。
2456: // XXX まだ CPU は1つしかないので雑に処理。
2457: // "6" -> Data (CPU#0)
2458: // "7" -> Program (CPU#0)
2459: if (mod_num < 6) {
1.1.1.20! root 2460: fprintf(cons, "CMMU %d not installed.\n", mod_num);
1.1.1.6 root 2461: return false;
2462: }
2463: mod_prog = mod_num - 6;
2464: }
2465: }
1.1 root 2466: }
1.1.1.5 root 2467:
1.1.1.6 root 2468: // アドレス空間修飾子のうち省略箇所はデフォルト状態で補完
2469: // - 's'/'u' いずれもなければ現在値。
2470: // - 'd'/'p'/'i' いずれもなければ、d/m コマンドごとに自然なほう。
2471: if (mod_super < 0) {
1.1.1.13 root 2472: mod_super = curmd->IsSuper();
1.1.1.6 root 2473: }
2474: if (mod_prog < 0) {
2475: mod_prog = !default_data;
2476: }
1.1.1.16 root 2477: sa->ChangeSuper(mod_super);
2478: sa->ChangeData(!mod_prog);
1.1.1.6 root 2479:
1.1.1.5 root 2480: // アドレス
2481: if (ParseAddr(str.c_str() + addrpos, &addr) == false) {
2482: return false;
2483: }
1.1.1.7 root 2484: // 命令境界に丸める
1.1.1.16 root 2485: sa->ChangeAddr(addr & ~(curmd->inst_bytes - 1));
1.1.1.5 root 2486:
1.1 root 2487: return true;
2488: }
2489:
1.1.1.13 root 2490: // ターゲット CPU を "mpu" (M680x0/M88xx0) に変更。
2491: Debugger::CmdAct
2492: Debugger::cmd_mpu()
2493: {
2494: if (curmd != md_mpu.get()) {
2495: ChangeMD(md_mpu.get());
2496: ResetStickyAddr();
2497: cmd_minus();
2498: }
2499: return CmdAct::Stay;
2500: }
2501:
2502: // ターゲット CPU を "xp" (HD647180) に変更。
2503: Debugger::CmdAct
2504: Debugger::cmd_xp()
2505: {
2506: if (curmd != md_xp.get()) {
2507: ChangeMD(md_xp.get());
2508: ResetStickyAddr();
2509: cmd_minus();
2510: }
2511: return CmdAct::Stay;
2512: }
2513:
2514: // この命令の1つ次のアドレスまでスキップする。
2515: // dbra とかのループを飛ばす用。
2516: Debugger::CmdAct
2517: Debugger::cmd_z()
2518: {
2519: // 次のアドレスを求める。
2520: if (curmd->inst_bytes_fixed) {
2521: // 固定長命令
2522: step_addr = curmd->GetPC() + curmd->inst_bytes_fixed;
2523: } else {
2524: // 可変長なら逆アセンブルしてみるしか。
2525:
1.1.1.16 root 2526: busaddr laddr = busaddr(curmd->GetPC())
2527: | BusAddr::Fetch
2528: | busaddr::SU(curmd->IsSuper());
1.1.1.15 root 2529: DebuggerMemoryStream mem(curmd, laddr);
1.1.1.13 root 2530: // XXX 失敗したらどうすべ
2531: curmd->Disassemble(mem);
1.1.1.15 root 2532: step_addr = mem.laddr.Addr();
1.1.1.13 root 2533: }
2534: step_type = StepType::Addr;
2535: step_md = curmd;
2536:
2537: return CmdAct::Leave;
2538: }
2539:
2540: void
2541: Debugger::ChangeMD(DebuggerMD *newmd)
2542: {
2543: if (newmd != curmd) {
2544: curmd = newmd;
1.1.1.20! root 2545: fprintf(cons, "Target cpu is switched to '%s'.\n",
1.1.1.13 root 2546: curmd->GetName().c_str());
2547: }
2548: }
2549:
2550: // pc と d/m の初期値をリセット。
2551: void
2552: Debugger::ResetStickyAddr()
2553: {
2554: pc = curmd->GetPC();
1.1.1.16 root 2555: m_last_addr = busaddr(pc) | BusAddr::Read | busaddr::SU(curmd->IsSuper());
2556: d_last_addr = busaddr(pc) | BusAddr::Fetch | busaddr::SU(curmd->IsSuper());
1.1.1.13 root 2557: }
2558:
1.1.1.15 root 2559: // 逆アセンブル: d [[SU:]<addr> [<cnt>]] (論理)
1.1.1.12 root 2560: Debugger::CmdAct
1.1 root 2561: Debugger::cmd_d()
2562: {
1.1.1.16 root 2563: return cmd_d_common(BusAddr::Logical);
1.1 root 2564: }
2565:
2566: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
1.1.1.12 root 2567: Debugger::CmdAct
1.1 root 2568: Debugger::cmd_D()
2569: {
1.1.1.16 root 2570: return cmd_d_common(BusAddr::Physical);
1.1 root 2571: }
2572:
2573: // 逆アセンブル共通
1.1.1.12 root 2574: Debugger::CmdAct
1.1.1.15 root 2575: Debugger::cmd_d_common(busaddr access_mode)
1.1 root 2576: {
1.1.1.15 root 2577: busaddr saddr;
1.1.1.13 root 2578: int row;
1.1 root 2579:
1.1.1.13 root 2580: row = 10;
1.1 root 2581:
1.1.1.15 root 2582: // 開始アドレス。S/U は GetAddr() がセットする。
1.1.1.6 root 2583: saddr = d_last_addr;
2584: if (GetAddr(&saddr, false) == false) {
1.1.1.12 root 2585: return CmdAct::Stay;
1.1 root 2586: }
1.1.1.15 root 2587:
2588: // 論理アドレス/物理アドレスはコマンドによって決まる。
2589: saddr |= access_mode;
2590:
1.1 root 2591: // 2つ目の引数があれば行数
1.1.1.20! root 2592: if (cmdargs.size() > 2) {
! 2593: row = strtol(cmdargs[2].c_str(), NULL, 10);
1.1 root 2594: }
2595:
1.1.1.13 root 2596: // アドレス幅を制限 (XP用) (ここ?)
2597: uint64 mask;
1.1.1.15 root 2598: if (saddr.IsPhysical()) {
1.1.1.13 root 2599: mask = (1ULL << curmd->GetPASize()) - 1;
1.1.1.15 root 2600: } else {
2601: mask = (1ULL << curmd->GetLASize()) - 1;
1.1 root 2602: }
1.1.1.15 root 2603: saddr &= (uint32)mask;
1.1.1.13 root 2604:
2605: Memdump disasm(curmd, curmd->arch);
2606: disasm.SetAddr(saddr);
2607: disasm.Print(cons, row);
1.1.1.2 root 2608:
1.1.1.15 root 2609: // アドレスを次回継続用に保存
1.1.1.13 root 2610: d_last_addr = disasm.GetAddr();
1.1.1.12 root 2611:
2612: return CmdAct::Stay;
1.1 root 2613: }
2614:
1.1.1.5 root 2615: // 表示レジスタ選択
2616: // disp 現在の内容を表示
2617: // disp <regs>... 設定
1.1.1.12 root 2618: Debugger::CmdAct
1.1.1.5 root 2619: Debugger::cmd_disp()
2620: {
1.1.1.20! root 2621: if (cmdargs.size() < 2) {
1.1.1.5 root 2622: // 表示
2623: bool first = true;
2624: for (const auto& r : disp_regs) {
1.1.1.11 root 2625: fprintf(cons, "%s%s", (first ? "" : ","), r.c_str());
1.1.1.5 root 2626: first = false;
2627: }
1.1.1.11 root 2628: fprintf(cons, "\n");
1.1.1.12 root 2629: return CmdAct::Stay;
1.1.1.5 root 2630: }
2631:
2632: // 設定 (引数を分解する)
2633: char buf[256];
2634: char *p;
2635: char *last;
1.1.1.20! root 2636: strlcpy(buf, cmdargs[1].c_str(), sizeof(buf));
1.1.1.5 root 2637: disp_regs.clear();
2638: for (p = strtok_r(buf, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
2639: disp_regs.push_back(std::string(p));
2640: }
2641:
2642: // XXX チェック
1.1.1.12 root 2643:
2644: return CmdAct::Stay;
1.1.1.5 root 2645: }
2646:
1.1.1.9 root 2647: // ログレベル設定: L <name>[=<level>][...]
1.1.1.12 root 2648: Debugger::CmdAct
1.1 root 2649: Debugger::cmd_L()
2650: {
1.1.1.20! root 2651: if (cmdargs.size() < 2) {
! 2652: fprintf(cons, "Usage: L <name1>[=<level1>][,<name2>[=<level2>]]...\n");
1.1.1.12 root 2653: return CmdAct::Stay;
1.1 root 2654: }
2655:
1.1.1.9 root 2656: // 一旦全部つなげる。
2657: // help が混ざる場合の "L foo=1,help" と "L foo=1 help" を同じ動作に
2658: // するため。
2659: std::string str;
1.1.1.20! root 2660: for (int i = 1, ac = cmdargs.size(); i < ac; i++) {
1.1.1.9 root 2661: if (str.empty() == false) {
2662: str += ',';
2663: }
1.1.1.20! root 2664: str += cmdargs[i];
1.1.1.9 root 2665: }
2666:
2667: // で、再分解
2668: std::vector<std::string> items = string_split(str, ',');
2669:
2670: // "help" があれば一覧を表示
2671: for (const auto& item : items) {
2672: if (item == "help") {
2673: std::vector<std::string> list = MainApp::GetLogNames();
2674: for (const auto& name : list) {
1.1.1.11 root 2675: fprintf(cons, " %s\n", name.c_str());
1.1.1.9 root 2676: }
1.1.1.12 root 2677: return CmdAct::Stay;
1.1.1.9 root 2678: }
2679: }
1.1 root 2680:
1.1.1.9 root 2681: // ログレベルを設定
2682: std::string errmsg;
2683: if (MainApp::SetLogopt(items, &errmsg) == false) {
1.1.1.11 root 2684: fprintf(cons, "%s\n", errmsg.c_str());
1.1.1.12 root 2685: return CmdAct::Stay;
1.1 root 2686: }
1.1.1.12 root 2687:
2688: return CmdAct::Stay;
1.1 root 2689: }
2690:
1.1.1.15 root 2691: // メモリダンプ: m [<addr> [<cnt>]] (論理)
1.1.1.12 root 2692: Debugger::CmdAct
1.1 root 2693: Debugger::cmd_m()
2694: {
1.1.1.16 root 2695: return cmd_m_common(BusAddr::Logical);
1.1 root 2696: }
2697:
2698: // メモリダンプ: M [<addr> [<cnt>]] (物理)
1.1.1.12 root 2699: Debugger::CmdAct
1.1 root 2700: Debugger::cmd_M()
2701: {
1.1.1.16 root 2702: return cmd_m_common(BusAddr::Physical);
1.1 root 2703: }
2704:
2705: // メモリダンプ共通
1.1.1.12 root 2706: Debugger::CmdAct
1.1.1.15 root 2707: Debugger::cmd_m_common(busaddr access_mode)
1.1 root 2708: {
1.1.1.15 root 2709: busaddr saddr;
1.1.1.9 root 2710: int row;
1.1 root 2711:
1.1.1.9 root 2712: row = 8;
1.1 root 2713:
1.1.1.15 root 2714: // 開始アドレス。S/U は GetAddr() がセットする。
1.1.1.6 root 2715: saddr = m_last_addr;
2716: if (GetAddr(&saddr, true) == false) {
1.1.1.12 root 2717: return CmdAct::Stay;
1.1 root 2718: }
1.1.1.15 root 2719:
2720: // 論理アドレス/物理アドレスはコマンドによって決まる。
2721: saddr |= access_mode;
2722:
1.1 root 2723: // 2つ目の引数があれば行数
1.1.1.20! root 2724: if (cmdargs.size() > 2) {
! 2725: row = strtol(cmdargs[2].c_str(), NULL, 10);
1.1.1.9 root 2726: }
2727:
1.1.1.13 root 2728: // アドレス幅を制限 (XP用) (ここ?)
2729: uint64 mask;
1.1.1.15 root 2730: if (saddr.IsPhysical()) {
1.1.1.13 root 2731: mask = (1ULL << curmd->GetPASize()) - 1;
1.1.1.15 root 2732: } else {
2733: mask = (1ULL << curmd->GetLASize()) - 1;
1.1.1.9 root 2734: }
1.1.1.15 root 2735: saddr &= (uint32)mask;
1.1.1.9 root 2736:
1.1.1.13 root 2737: Memdump memdump(curmd);
2738: memdump.SetAddr(saddr);
2739: memdump.SetFormat(Memdump::Word);
2740: memdump.Print(cons, row);
2741:
2742: // アドレスを次回継続用に保存
2743: m_last_addr = memdump.GetAddr();
1.1.1.12 root 2744:
2745: return CmdAct::Stay;
1.1.1.9 root 2746: }
2747:
1.1 root 2748: // プロンプトに来た最初に表示するいつものやつを再表示する
1.1.1.12 root 2749: Debugger::CmdAct
1.1 root 2750: Debugger::cmd_minus()
2751: {
1.1.1.5 root 2752: // 指定のレジスタ群を表示
2753: // disp_regs はレジスタ群のリスト { "r", "rf" } のような感じ。
2754: // 一方 ShowRegisters() の第2引数は1つのコマンドラインを空白で区切った
2755: // 文字列リスト { arg[0], arg[1], .. }。型が同じで意味が違うので注意。
2756: for (const auto& reg : disp_regs) {
2757: std::vector<std::string> tmpargs;
2758: tmpargs.push_back(reg);
1.1.1.13 root 2759: curmd->ShowRegister(cons, tmpargs);
1.1.1.5 root 2760: }
1.1 root 2761:
1.1.1.13 root 2762: // 現在の PC の位置を逆アセンブル。
1.1.1.2 root 2763: std::string addrbuf;
1.1.1.13 root 2764: std::string textbuf;
1.1.1.6 root 2765:
1.1.1.16 root 2766: busaddr laddr = busaddr(pc)
2767: | BusAddr::Fetch
2768: | busaddr::SU(curmd->IsSuper());
1.1.1.15 root 2769: DebuggerMemoryStream mem(curmd, laddr);
1.1.1.6 root 2770: // アドレス
2771: if (mem.FormatAddr(addrbuf) == false) {
1.1.1.11 root 2772: fprintf(cons, "%s\n", addrbuf.c_str());
1.1.1.12 root 2773: return CmdAct::Stay;
1.1.1.2 root 2774: }
1.1.1.13 root 2775: // オンライン用に逆アセンブル
2776: textbuf = curmd->Disassemble(mem);
1.1.1.6 root 2777:
1.1.1.13 root 2778: fprintf(cons, "%-18s %s\n", addrbuf.c_str(), textbuf.c_str());
1.1.1.12 root 2779: return CmdAct::Stay;
1.1 root 2780: }
2781:
1.1.1.5 root 2782: // サブルーチンとループを飛ばしながら count 命令ステップ実行
2783: // n [<count>?:1] (途中レジスタを表示しない)
1.1.1.12 root 2784: Debugger::CmdAct
1.1 root 2785: Debugger::cmd_n()
2786: {
1.1.1.12 root 2787: return cmd_n_common(false);
1.1.1.5 root 2788: }
2789:
2790: // サブルーチンとループを飛ばしながら count 命令ステップ実行
2791: // nt [<count>?:1] (1命令ずつレジスタを表示する)
1.1.1.12 root 2792: Debugger::CmdAct
1.1.1.5 root 2793: Debugger::cmd_nt()
2794: {
1.1.1.12 root 2795: return cmd_n_common(true);
1.1.1.5 root 2796: }
2797:
2798: // n と nt の共通部分
1.1.1.12 root 2799: Debugger::CmdAct
1.1.1.5 root 2800: Debugger::cmd_n_common(bool trace)
2801: {
1.1.1.13 root 2802: t_enable = trace ? curmd : NULL;
1.1.1.5 root 2803:
1.1.1.20! root 2804: if (cmdargs.size() > 1) {
1.1 root 2805: int count;
1.1.1.20! root 2806: count = strtol(cmdargs[1].c_str(), NULL, 10);
1.1 root 2807: if (count < 1) {
1.1.1.20! root 2808: fprintf(cons, "Invalid step count: %d\n", count);
1.1.1.12 root 2809: return CmdAct::Stay;
1.1 root 2810: }
2811:
1.1.1.13 root 2812: step_count = count;
1.1 root 2813: } else {
1.1.1.13 root 2814: step_count = 1;
1.1 root 2815: }
1.1.1.13 root 2816: step_type = StepType::CountSkipSub;
2817: step_md = curmd;
1.1 root 2818: SetNBreakpoint();
1.1.1.12 root 2819:
2820: return CmdAct::Leave;
1.1 root 2821: }
2822:
1.1.1.5 root 2823: // n コマンド用のブレークポイントを設定する。
2824: // この命令語によって仕掛けるブレークポイントが変わるので、都度都度
2825: // 呼び出すこと。
2826: void
1.1 root 2827: Debugger::SetNBreakpoint()
2828: {
1.1.1.16 root 2829: busaddr laddr = busaddr(curmd->GetPC())
2830: | BusAddr::Fetch
2831: | busaddr::SU(curmd->IsSuper());
1.1.1.15 root 2832: DebuggerMemoryStream mem(curmd, laddr);
1.1.1.5 root 2833:
1.1.1.6 root 2834: // 命令がサブルーチンやトラップなどステップインできる命令か。
1.1.1.13 root 2835: if (curmd->IsOpStepIn(mem)) {
1.1.1.5 root 2836: // この次の命令にブレークをかける
1.1.1.13 root 2837: if (curmd->inst_bytes_fixed != 0) {
1.1.1.5 root 2838: // 固定長命令
1.1.1.15 root 2839: step_addr = mem.laddr.Addr();
1.1.1.5 root 2840: } else {
1.1.1.13 root 2841: // 可変長なら逆アセンブルしてみるしか。
2842: // 表示用文字列は作る必要ないけど、処理分けるのも手間だし
2843: // ここは人間がコマンド打った時しかこないので気にしない。
2844:
2845: // IsOpStepIn() が mem を進めるので戻す。
2846: mem.ResetAddr(curmd->GetPC());
1.1.1.5 root 2847: // XXX 失敗したらどうすべ
1.1.1.13 root 2848: curmd->Disassemble(mem);
1.1.1.15 root 2849: step_addr = mem.laddr.Addr();
1.1.1.5 root 2850: }
1.1 root 2851: } else {
1.1.1.5 root 2852: // そうでなければ1命令進める。
1.1.1.13 root 2853: step_addr = (int64)-1;
1.1 root 2854: }
2855: }
2856:
1.1.1.2 root 2857: // デバッガ終了コマンド
1.1.1.12 root 2858: Debugger::CmdAct
1.1.1.2 root 2859: Debugger::cmd_q()
2860: {
1.1.1.20! root 2861: fprintf(cons, "Quit debugger console.\n");
1.1.1.12 root 2862: return CmdAct::Quit;
1.1 root 2863: }
2864:
1.1.1.5 root 2865: // VM リセット
1.1.1.12 root 2866: Debugger::CmdAct
1.1.1.5 root 2867: Debugger::cmd_reset()
2868: {
1.1.1.13 root 2869: GetPowerDevice()->MakeResetHard();
1.1.1.12 root 2870: return CmdAct::Leave;
1.1.1.5 root 2871: }
2872:
1.1.1.13 root 2873: // モニタ表示
1.1.1.12 root 2874: Debugger::CmdAct
1.1 root 2875: Debugger::cmd_show()
2876: {
1.1.1.20! root 2877: if (cmdargs.size() != 2) {
1.1.1.16 root 2878: std::vector<uint> list;
1.1.1.9 root 2879:
1.1.1.13 root 2880: // 登録されているモニタだけを列挙
1.1.1.9 root 2881: // (登録されていてもサブウィンドウの ID を持っているものは除外)
1.1.1.11 root 2882: for (const auto mon : gMonitorManager->GetList()) {
1.1.1.16 root 2883: uint id = mon->GetId();
1.1.1.9 root 2884: if (id <= ID_MONITOR_END) {
2885: list.push_back(id);
1.1 root 2886: }
2887: }
1.1.1.9 root 2888:
2889: // 一覧を表示
2890: std::string msg = MonitorManager::MakeListString(list);
1.1.1.20! root 2891: fprintf(cons, "Usage: show <monitor>\n");
1.1.1.11 root 2892: fprintf(cons, "%s", msg.c_str());
1.1.1.12 root 2893: return CmdAct::Stay;
1.1 root 2894: }
2895:
1.1.1.9 root 2896: std::vector<Monitor *> candidates;
2897: std::vector<std::string> cand_names;
1.1.1.20! root 2898: std::string name = cmdargs[1];
1.1.1.9 root 2899:
1.1.1.11 root 2900: for (auto mon : gMonitorManager->GetList()) {
1.1.1.16 root 2901: uint id = mon->GetId();
1.1.1.11 root 2902: const auto& aliases = gMonitorManager->GetAliases(id);
1.1.1.9 root 2903:
2904: bool matched = false;
2905: for (const auto& alias : aliases) {
2906: // 部分一致したら名前はすべて覚えておく
2907: if (starts_with_ignorecase(alias, name)) {
2908: cand_names.push_back(alias);
2909: matched = true;
2910: }
2911: }
2912:
2913: // 同じモニタで別名が複数回部分一致しても、
2914: // モニタのほうは1回として数える
2915: if (matched) {
2916: candidates.push_back(mon);
2917: }
2918: }
2919:
2920: if (candidates.empty()) {
2921: // 一致しない
1.1.1.20! root 2922: fprintf(cons, "Unknown monitor name: \"%s\"\n", name.c_str());
1.1.1.9 root 2923: } else if (candidates.size() == 1) {
2924: // 確定した
1.1.1.17 root 2925: Monitor *mon = candidates[0];
1.1.1.9 root 2926: ShowMonitor(mon);
2927: } else {
2928: // 候補が複数あった
2929: std::string candstr;
2930: for (const auto& cname : cand_names) {
2931: candstr += ' ';
2932: candstr += cname;
1.1 root 2933: }
1.1.1.20! root 2934: fprintf(cons, "Ambiguous monitor name \"%s\". Candidates are:%s\n",
1.1.1.9 root 2935: name.c_str(), candstr.c_str());
1.1 root 2936: }
1.1.1.12 root 2937:
2938: return CmdAct::Stay;
1.1 root 2939: }
2940:
1.1.1.13 root 2941: // モニタを更新して表示。
1.1.1.3 root 2942: void
1.1.1.17 root 2943: Debugger::ShowMonitor(Monitor *monitor)
1.1.1.3 root 2944: {
2945: // モニタを更新
1.1.1.4 root 2946: TextScreen ts;
2947:
1.1.1.17 root 2948: auto size = monitor->GetSize();
1.1.1.4 root 2949: ts.Init(size.width, size.height);
2950:
1.1.1.9 root 2951: MONITOR_UPDATE(monitor, ts);
1.1.1.4 root 2952: ShowTextScreen(ts);
1.1.1.3 root 2953: }
2954:
2955: // テキストスクリーンを表示。
1.1 root 2956: void
1.1.1.3 root 2957: Debugger::ShowTextScreen(TextScreen& monitor)
1.1 root 2958: {
2959: char sbuf[1024]; // 適当
2960:
2961: // monitor 内部バッファから ShiftJIS バッファを作成。
2962: // その際属性もエスケープシーケンスで再現する。
2963: int col = monitor.GetCol();
2964: int row = monitor.GetRow();
1.1.1.8 root 2965: const std::vector<uint16>& src = monitor.GetBuf();
1.1 root 2966:
2967: for (int y = 0; y < row; y++) {
2968: int sy = y * col;
2969: int sx;
2970: uint attr;
2971: char *d;
2972:
2973: memset(sbuf, 0, sizeof(sbuf));
2974: d = sbuf;
2975: attr = TA::Normal;
2976: for (sx = 0; sx < col; sx++) {
2977: uint a = src[sy + sx] & 0xff00;
2978: uint ch = src[sy + sx] & 0x00ff;
2979:
2980: // 属性の変わり目
2981: if (attr != a) {
2982: switch (a) {
2983: case TA::Normal:
2984: case TA::Off:
2985: *d++ = 0x1b;
2986: *d++ = '[';
2987: *d++ = 'm';
2988: break;
1.1.1.14 root 2989: case TA::Reverse:
2990: case TA::ReverseRed:
2991: case TA::ReverseGreen:
2992: case TA::ReverseOrange:
1.1 root 2993: *d++ = 0x1b;
2994: *d++ = '[';
2995: *d++ = '7';
2996: *d++ = 'm';
2997: break;
2998: case TA::Disable:
2999: *d++ = 0x1b;
3000: *d++ = '[';
3001: *d++ = '2';
3002: *d++ = 'm';
3003: break;
3004: case TA::Em:
3005: *d++ = 0x1b;
3006: *d++ = '[';
3007: *d++ = '1';
3008: *d++ = 'm';
3009: break;
3010: }
3011: attr = a;
3012: }
3013: *d++ = ch;
3014: }
1.1.1.3 root 3015: // 行の終わりで一旦属性を戻しておく
3016: if (attr != TA::Normal) {
3017: *d++ = 0x1b;
3018: *d++ = '[';
3019: *d++ = 'm';
3020: }
1.1 root 3021: *d = '\0';
3022:
3023: // XXX 日本語が使われてれば UTF-8 にしないといけないはず
3024:
1.1.1.11 root 3025: fprintf(cons, "%s\n", sbuf);
1.1 root 3026: }
3027: }
3028:
1.1.1.5 root 3029: // ステップ実行
3030: // s [<count>?:1] (途中レジスタを表示しない)
1.1.1.12 root 3031: Debugger::CmdAct
1.1 root 3032: Debugger::cmd_s()
3033: {
1.1.1.12 root 3034: return cmd_s_common(false);
1.1.1.5 root 3035: }
3036:
3037: // ステップ実行
3038: // st [<count>?:1] (命令ごとにレジスタを表示する)
1.1.1.12 root 3039: Debugger::CmdAct
1.1.1.5 root 3040: Debugger::cmd_st()
3041: {
1.1.1.12 root 3042: return cmd_s_common(true);
1.1.1.5 root 3043: }
3044:
3045: // ステップ実行共通部分
1.1.1.12 root 3046: Debugger::CmdAct
1.1.1.5 root 3047: Debugger::cmd_s_common(bool trace)
3048: {
1.1.1.13 root 3049: t_enable = trace ? curmd : NULL;
1.1.1.5 root 3050:
1.1.1.20! root 3051: if (cmdargs.size() > 1) {
1.1 root 3052: int count;
1.1.1.20! root 3053: count = strtol(cmdargs[1].c_str(), NULL, 10);
1.1 root 3054: if (count < 1) {
1.1.1.20! root 3055: fprintf(cons, "Invalid step count: %d\n", count);
1.1.1.12 root 3056: return CmdAct::Stay;
1.1 root 3057: }
3058:
1.1.1.13 root 3059: step_count = count;
1.1 root 3060: } else {
1.1.1.13 root 3061: step_count = 1;
1.1 root 3062: }
1.1.1.13 root 3063: step_type = StepType::Count;
3064: step_md = curmd;
1.1.1.12 root 3065:
3066: return CmdAct::Leave;
1.1 root 3067: }
3068:
1.1.1.5 root 3069: // ステップアウト (途中レジスタを表示しない)
1.1.1.12 root 3070: Debugger::CmdAct
1.1 root 3071: Debugger::cmd_so()
3072: {
1.1.1.12 root 3073: return cmd_so_common(false);
1.1.1.5 root 3074: }
3075:
3076: // ステップアウト (命令ごとにレジスタを表示する)
1.1.1.12 root 3077: Debugger::CmdAct
1.1.1.5 root 3078: Debugger::cmd_sot()
3079: {
1.1.1.12 root 3080: return cmd_so_common(true);
1.1.1.5 root 3081: }
3082:
3083: // ステップアウトの共通部分
1.1.1.12 root 3084: Debugger::CmdAct
1.1.1.5 root 3085: Debugger::cmd_so_common(bool trace)
3086: {
1.1.1.13 root 3087: t_enable = trace ? curmd : NULL;
1.1.1.5 root 3088:
1.1.1.13 root 3089: step_type = StepType::StepOut;
3090: step_md = curmd;
3091: step_md->SetStepOut();
1.1.1.12 root 3092:
3093: return CmdAct::Leave;
1.1 root 3094: }
3095:
1.1.1.5 root 3096: // t は st の省略形。互換のため。
1.1.1.12 root 3097: Debugger::CmdAct
1.1 root 3098: Debugger::cmd_t()
3099: {
1.1.1.12 root 3100: return cmd_st();
1.1 root 3101: }
3102:
1.1.1.4 root 3103: // 引数で示されるプレフィックスなしの16進数値を返す。
3104: // 正しく変換できれば値を valp に格納して true を返す。
3105: // そうでなければ false を返す。
3106: bool
3107: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
3108: {
3109: uint32 val;
3110: char *end;
3111:
3112: errno = 0;
3113: val = strtoul(arg, &end, 16);
3114: if (arg[0] == '\0' || end[0] != '\0') {
3115: return false;
3116: }
3117: if (errno == ERANGE) {
3118: return false;
3119: }
3120:
3121: *valp = val;
3122: return true;
3123: }
3124:
1.1 root 3125: // 引数で示されるアドレスを返す。
3126: // '%' で始まればレジスタ。
3127: // そうでなければ16進数値。
3128: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
3129: // そうでなければエラーメッセージを表示して偽を返す。
3130: bool
1.1.1.4 root 3131: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1 root 3132: {
1.1.1.20! root 3133: uint64 addr;
1.1 root 3134: char *end;
3135:
3136: if (arg[0] == '%') {
1.1.1.2 root 3137: // '%' から始まればレジスタ
1.1.1.20! root 3138: addr = curmd->GetRegAddr(arg + 1);
! 3139: if ((int64)addr < 0) {
! 3140: fprintf(cons, "Invalid register name.\n");
1.1 root 3141: return false;
3142: }
3143: } else {
3144: /* そうでなければ番地指定 */
3145: errno = 0;
3146: addr = strtoul(arg, &end, 16);
1.1.1.20! root 3147: if (end == arg || *end != '\0') {
! 3148: fprintf(cons, "Invalid address: '%s'\n", arg);
1.1 root 3149: return false;
3150: }
1.1.1.20! root 3151: if (addr > 0xffffffff) {
! 3152: fprintf(cons, "Out of range: %s\n", arg);
1.1 root 3153: return false;
3154: }
3155: }
3156:
1.1.1.20! root 3157: *addrp = (uint32)addr;
1.1 root 3158: return true;
3159: }
3160:
1.1.1.12 root 3161: // 引数で示される時間間隔を返す。
3162: bool
3163: Debugger::ParseTime(const char *arg, uint64 *timep)
3164: {
3165: double f;
3166: uint64 t;
3167: char *end;
3168:
3169: errno = 0;
3170: f = strtod(arg, &end);
3171: if (end == arg) {
1.1.1.20! root 3172: fprintf(cons, "Invalid time: %s\n", arg);
1.1.1.12 root 3173: return false;
3174: }
3175: if (errno == ERANGE || f < 0 || std::isinf(f) || std::isnan(f)) {
1.1.1.20! root 3176: fprintf(cons, "Out of range: %s\n", arg);
1.1.1.12 root 3177: return false;
3178: }
3179:
3180: // 単位省略は nsec とする。
3181: // 接尾語は "nsec" とかの他 C++ 書式で使ってる "_nsec" も受け付けておく。
3182: if (*end == '\0' || strcmp(end, "nsec") == 0 || strcmp(end, "_nsec") == 0) {
3183: t = f * 1_nsec;
3184: } else if (strcmp(end, "usec") == 0 || strcmp(end, "_usec") == 0) {
3185: t = f * 1_usec;
3186: } else if (strcmp(end, "msec") == 0 || strcmp(end, "_msec") == 0) {
3187: t = f * 1_msec;
3188: } else if (strcmp(end, "sec") == 0 || strcmp(end, "_sec") == 0) {
3189: t = f * 1_sec;
3190: } else {
1.1.1.20! root 3191: fprintf(cons, "Unknown time suffix: %s\n", end);
1.1.1.12 root 3192: return false;
3193: }
3194:
3195: *timep = t;
3196: return true;
3197: }
3198:
1.1.1.4 root 3199:
3200: //
1.1.1.5 root 3201: // MD
3202: //
3203:
1.1.1.13 root 3204: // コンストラクタ
3205: DebuggerMD::DebuggerMD(Debugger *parent_, CPUArch arch_)
3206: {
3207: parent = parent_;
3208: arch = arch_;
3209:
3210: bv_vector = -1;
3211: }
3212:
1.1.1.6 root 3213: // デストラクタ
3214: DebuggerMD::~DebuggerMD()
3215: {
1.1.1.5 root 3216: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.