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