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