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