Annotation of nono/debugger/debugger.cpp, revision 1.1.1.4

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
1.1.1.2   root        7: #include "console.h"
                      8: #include "debugger_private.h"
                      9: #include "debugger_m680x0.h"
                     10: #include "debugger_m88xx0.h"
                     11: #include "mainapp.h"
1.1       root       12: #include "mystring.h"
1.1.1.3   root       13: #include "mythread.h"
                     14: #include <algorithm>
1.1       root       15: #include <csignal>
                     16: 
1.1.1.3   root       17: static Debugger *gDebugger;
1.1.1.4 ! root       18: BreakpointMonitor *gBreakpointMonitor;
1.1       root       19: CVPrompt *gCVPrompt;
                     20: 
                     21: static void *debugger_run(void *);
                     22: 
                     23: // 初期化。
                     24: // この時点で VM が初期化されていること。
                     25: void
                     26: debugger_init()
                     27: {
                     28:        gDebugger = new Debugger();
                     29:        gDebugger->Init();
                     30: 
1.1.1.4 ! root       31:        gBreakpointMonitor = new BreakpointMonitor();
1.1       root       32:        gCVPrompt = new CVPrompt();
                     33: 
                     34:        // デバッガスレッド起動
                     35:        pthread_t th;
                     36:        pthread_create(&th, NULL, debugger_run, NULL);
                     37: }
                     38: 
                     39: // デバッガスレッドのエントリポイント
                     40: static void *
                     41: debugger_run(void *dummy)
                     42: {
1.1.1.3   root       43:        PTHREAD_SETNAME("Debugger");
1.1       root       44:        pthread_detach(pthread_self());
                     45: 
                     46:        gDebugger->ThreadRun();
                     47:        return NULL;
                     48: }
                     49: 
                     50: // コンストラクタ
                     51: Debugger::Debugger()
                     52: {
1.1.1.2   root       53:        if (gMPU680x0) {
                     54:                md = new DebuggerMD_m680x0(this, gMPU680x0->GetCPU());
                     55:        } else if (gMPU88xx0) {
                     56:                md = new DebuggerMD_m88xx0(this, gMPU88xx0->GetCPU());
                     57:        } else {
                     58:                throw "unknown mpu";
                     59:        }
1.1       root       60: }
                     61: 
                     62: // コマンドライン引数によって動作を決めるところ。
                     63: // 名前がこれでいいのかはあるけど。
                     64: void
                     65: Debugger::Init()
                     66: {
                     67:        // -d なら CPU 起動時点で停止してプロンプトを待つ
1.1.1.2   root       68:        if (gMainApp.debug_on_start) {
                     69:                md->ReqSet(CPU_REQ_PROMPT);
1.1       root       70:        }
                     71: 
                     72:        // -b <addr> ならブレークポイント設定
1.1.1.4 ! root       73:        for (auto& str : gMainApp.debug_breakaddr) {
        !            74:                breakpoint_t bp;
        !            75: 
1.1.1.2   root       76:                md->ReqSet(CPU_REQ_TRACE);
1.1.1.4 ! root       77: 
        !            78:                // <addr>,<skip> で分離できたら <skip> を取り出す
        !            79:                int pos = str.find(',');
        !            80:                if (pos != std::string::npos) {
        !            81:                        std::string skipstr = str.substr(pos + 1);
        !            82:                        bp.skip = atoi(skipstr.c_str());
        !            83: 
        !            84:                        // XXX これ出来るんだっけ?
        !            85:                        str[pos] = '\0';
        !            86:                }
        !            87:                // そしてどちらにしても <addr> を取り出す
        !            88:                if (!ParseAddr(str.c_str(), &bp.addr)) {
        !            89:                        warnx("\"%s\": Invalid breakpoint address", str.c_str());
        !            90:                        return;
        !            91:                }
        !            92:                // 登録
        !            93:                bp.type = BreakpointType::Address;
        !            94:                AddBreakpoint(bp);
1.1       root       95:        }
                     96: 
                     97:        // どこでやるべか
                     98:        signal(SIGPIPE, SIG_IGN);
                     99: }
                    100: 
                    101: // デバッガスレッド
                    102: void
                    103: Debugger::ThreadRun()
                    104: {
                    105:        // XXX どこかでコンソールの選択とパラメータの取得
1.1.1.2   root      106:        if (gMainApp.debug_on_console) {
                    107:                cons = new ConsoleStdio();
                    108:        } else {
                    109:                cons = new ConsoleTCP();
                    110:        }
1.1       root      111:        if (cons->Init() == false) {
1.1.1.3   root      112:                delete cons;
1.1       root      113:                return;
                    114:        }
                    115: 
                    116:        for (;;) {
1.1.1.3   root      117:                // 着信待ち
                    118:                if (Accept() == false) {
                    119:                        break;
                    120:                }
                    121: 
1.1.1.2   root      122:                cons->InitEditLine("> ");
1.1       root      123: 
1.1.1.3   root      124:                // デバッガプロンプトにいる間はトレースオン
                    125:                md->ReqSet(CPU_REQ_TRACE);
                    126: 
                    127:                bool first = true;
                    128:                for (;;) {
                    129:                        // 接続後の1回目だけ実行するもの。
                    130:                        if (first) {
                    131:                                first = false;
                    132: 
                    133:                                // greeting はプロンプトが取れる前にもう表示したい。
                    134:                                // 何らかの事故でプロンプトが取れなくても、ここまでは接続
                    135:                                // できてることが分かるように。
                    136:                                cons->Print("This is debugger console\n");
                    137: 
                    138:                                // 接続ごとに初期化する値
                    139:                                // XXX もうちょっときれいにしたい
                    140:                                d_last_addr = 0xffffffff;
                    141:                                m_last_addr = 0xffffffff;
                    142:                                n_enable = false;
                    143:                                s_enable = false;
                    144:                                t_enable = false;
                    145:                        }
                    146: 
                    147:                        // プロンプトが取れるのを待つ
                    148:                        if (AcquirePrompt() == false) {
                    149:                                break;
                    150:                        }
                    151: 
                    152:                        // プロンプトに来た時に表示するいつものやつ
                    153:                        pc = md->GetPC();
                    154:                        d_last_addr = pc;
                    155:                        cmd_minus();
                    156: 
                    157:                        // コマンド処理のメインループ
                    158:                        auto action = MainLoop();
                    159: 
                    160:                        // 次回との差分のため、今のレジスタセットをバックアップ
                    161:                        md->BackupRegs();
                    162: 
                    163:                        // メインループから戻ったということは Leave か Quit なので
                    164:                        // どちらにしても、ここでプロンプトを手放す。
                    165:                        gCVPrompt->NotifyRelease();
                    166: 
                    167:                        // quit ならここでループを1段抜ける
                    168:                        if (action == CommandAction::Quit) {
                    169:                                break;
                    170:                        }
                    171:                }
                    172: 
                    173:                // デバッガを抜けるのでトレースオフ
1.1.1.2   root      174:                md->ReqClr(CPU_REQ_TRACE);
1.1       root      175: 
                    176:                cons->Close();
1.1.1.3   root      177:                if (gMainApp.debug_on_console) {
                    178:                        break;
                    179:                }
1.1       root      180:        }
                    181: 
                    182:        delete cons;
                    183: }
                    184: 
1.1.1.3   root      185: // コンソールに接続されるのを待つ。
                    186: // うーん、なんだこれ。
                    187: //
                    188: // +--- debug_on_console (-D オプション)
                    189: // | +- debug_on_start   (-d オプション)
                    190: // | | 動作
                    191: // 0 0 TCP、Accept で待機。
                    192: // 0 1 TCP、Accpet で待機。
                    193: // 1 0 stdio、起動時にプロンプトで止まらない -> キー入力待ち。
                    194: // 1 1 stdio、起動時にプロンプトで止まる。
1.1       root      195: bool
1.1.1.3   root      196: Debugger::Accept()
                    197: {
                    198:        if (gMainApp.debug_on_console == false) {
                    199:                // TCP なら accept が成功するのを待つ
                    200:                cons->Accept();
                    201:        } else {
                    202:                if (gMainApp.debug_on_start == false) {
                    203:                        // stdio、起動時にプロンプトで止まらない。
                    204:                        // この場合キー入力かブレークポイントなどで止まった時点で
                    205:                        // プロンプトを出したいのでちょっと面倒なことに。
                    206:                        for (;;) {
                    207:                                bool is_prompt = gCVPrompt->WaitAcquire(200);
                    208:                                if (is_prompt) {
                    209:                                        break;
                    210:                                }
                    211: 
                    212:                                int r = cons->Poll(200);
                    213:                                if (r < 0) {
                    214:                                        cmd_q();
                    215:                                        return false;
                    216:                                }
                    217:                                if (r > 0) {
                    218:                                        break;
                    219:                                }
                    220:                        }
                    221:                } else {
                    222:                        // stdio、起動時にプロンプトで止まる。
                    223:                        // この場合は無条件に成功でよい。
                    224:                }
                    225:        }
                    226: 
                    227:        return true;
                    228: }
                    229: 
                    230: // プロンプトが取れるのを待つ
                    231: // 失敗すれば false を返す
                    232: bool
                    233: Debugger::AcquirePrompt()
1.1       root      234: {
1.1.1.3   root      235:        md->ReqSet(CPU_REQ_PROMPT);
                    236: 
1.1.1.2   root      237:        for (;;) {
                    238:                bool is_prompt = gCVPrompt->WaitAcquire(200);
1.1.1.3   root      239:                if (is_prompt)
                    240:                        break;
1.1.1.2   root      241: 
                    242:                // コンソールを定期観測して、入力があればブレーク要求
                    243:                if (cons->Poll()) {
                    244:                        // この入力は drop してみる
                    245:                        if (cons->Gets(cmdbuf) == false) {
                    246:                                // EOF or Error
                    247:                                cmd_q();
                    248:                                return false;
                    249:                        }
                    250:                        md->ReqSet(CPU_REQ_PROMPT);
                    251:                }
                    252:        }
1.1.1.3   root      253:        return true;
                    254: }
1.1       root      255: 
1.1.1.3   root      256: // コマンド処理のメインループ。
                    257: // コマンドが Stay ならこの中で処理を継続。
                    258: // コマンドが Leave か Quit ならその値を持って戻る (呼び出し側で処理する)。
                    259: Debugger::CommandAction
                    260: Debugger::MainLoop()
                    261: {
                    262:        for (;;) {
                    263:                cons->Prompt();
                    264:                if (cons->Gets(cmdbuf) == false) {
                    265:                        // EOF or Error
                    266:                        return CommandAction::Quit;
                    267:                }
1.1       root      268: 
1.1.1.3   root      269:                string_rtrim(cmdbuf);
1.1       root      270: 
1.1.1.3   root      271:                if (!cmdbuf.empty()) {
                    272:                        // コマンドが入力されれば次回のために保存
                    273:                        last_cmdbuf = cmdbuf;
                    274:                } else {
                    275:                        // 空行が入力されれば直前のコマンドをもう一度
                    276:                        // 前行がなければ何もせずもう一度プロンプトを表示するかね
                    277:                        if (last_cmdbuf.empty())
                    278:                                continue;
                    279:                        cmdbuf = last_cmdbuf;
                    280:                }
                    281: 
                    282:                // 行を args に分解
                    283:                ParseCmdbuf();
                    284: 
                    285:                // 前回の値が有効なのはコマンドが連続した時だけ
                    286: 
                    287:                // コマンドをテーブルから探す
                    288:                auto it = std::find_if(cmdtable.begin(), cmdtable.end(),
                    289:                        [=](auto x) { return strcmp(args[0].c_str(), x.name) == 0; });
                    290:                if (it != cmdtable.end()) {
                    291:                        auto cmd = *it;
                    292: 
                    293:                        // 見付かれば実行
                    294:                        (this->*(cmd.func))();
                    295: 
                    296:                        // Stay なら引き続きコマンド処理
                    297:                        if (cmd.action == CommandAction::Stay) {
                    298:                                continue;
                    299:                        }
                    300:                        // それ以外ならメインループ終了
                    301:                        return cmd.action;
                    302:                }
1.1       root      303: 
1.1.1.3   root      304:                // (コマンドテーブルになくて) "r" から始まっていればレジスタ表示系
                    305:                if (args[0][0] == 'r') {
                    306:                        // ShowRegister() は処理したら true を返す。
                    307:                        // "r" 系コマンドはすべて Stay。
                    308:                        if (md->ShowRegister(cons, args)) {
                    309:                                continue;
                    310:                        }
                    311:                }
1.1.1.2   root      312: 
1.1.1.3   root      313:                // 知らないコマンドも Stay 相当。
                    314:                cons->Print("%s: unknown command\n", args[0].c_str());
                    315:                continue;
                    316:        }
                    317:        __unreachable();
1.1       root      318: }
                    319: 
                    320: // ブレークポイントとかを調べる。
                    321: // CPU_REQ_TRACE フラグが立っている間1命令ごとに呼び出される。
                    322: bool
                    323: debugger_check()
                    324: {
                    325:        return gDebugger->Check();
                    326: }
                    327: 
                    328: // デバッガ実行中なら命令開始前にメインルーチンから呼ばれる。
                    329: // プロンプトに降りるなら true を返す。
                    330: bool
                    331: Debugger::Check()
                    332: {
                    333:        if (t_enable) {
                    334:                t_count--;
                    335:                if (t_count == 0) {
                    336:                        t_enable = false;
                    337:                        return true;
                    338:                }
                    339:        }
                    340: 
                    341:        if (s_enable) {
                    342:                s_count--;
                    343:                if (s_count == 0) {
                    344:                        s_enable = false;
                    345:                        return true;
                    346:                }
                    347:        }
                    348: 
                    349:        // n による1命令実行のブレークポイントか
1.1.1.2   root      350:        if (n_enable && n_breakenable && n_breakaddr == md->GetPC()) {
1.1       root      351:                n_breakenable = false;
                    352:                return true;
                    353:        }
                    354: 
1.1.1.4 ! root      355:        // ブレークポイント
        !           356:        if (CheckAllBreakpoints()) {
        !           357:                return true;
1.1       root      358:        }
                    359: 
                    360:        // ステップアウト
                    361:        if (so_enable) {
1.1.1.2   root      362:                if (md->IsStepOut()) {
1.1       root      363:                        so_enable = false;
                    364:                        return true;
                    365:                }
                    366:        }
                    367: 
                    368:        // アドレス指定付き continue
                    369:        if (bc_enable) {
1.1.1.2   root      370:                if (bc_addr == md->GetPC()) {
1.1       root      371:                        bc_enable = false;
                    372:                        return true;
                    373:                }
                    374:        }
                    375: 
                    376:        return false;
                    377: }
                    378: 
1.1.1.4 ! root      379: // ブレークポイントがどれかでも成立するかを調べる。
        !           380: // 1つ以上成立してブレークするなら true を返す。
        !           381: // 1つも成立しておらずブレークしないなら false を返す。
        !           382: bool
        !           383: Debugger::CheckAllBreakpoints()
        !           384: {
        !           385:        bool is_break = false;
        !           386: 
        !           387:        // 命令ごとにクリアする
        !           388:        bi_inst = 0;
        !           389:        bi_inst_bytes = 0;
        !           390: 
        !           391:        // 1つの条件でマッチしても(そこでブレークすること自体は確定するのだが)
        !           392:        // 残りの他の条件も成立すればカウントを進める必要があるため、
        !           393:        // 全部処理した上でどれか一つでもブレークしたかで判断する必要がある。
        !           394:        for (auto& bp : bpoint) {
        !           395:                switch (bp.type) {
        !           396:                 case BreakpointType::Address:
        !           397:                        if (bp.addr != md->GetPC()) {
        !           398:                                continue;
        !           399:                        }
        !           400:                        break;
        !           401: 
        !           402:                 case BreakpointType::Memory:
        !           403:                        if (!md->CheckLEA(bp.addr)) {
        !           404:                                continue;
        !           405:                        }
        !           406:                        break;
        !           407: 
        !           408:                 case BreakpointType::Exception:
        !           409:                        if (bv_vector >= 0) {
        !           410:                                if (bp.vec1 <= bv_vector && bv_vector <= bp.vec2) {
        !           411:                                        break;
        !           412:                                }
        !           413:                        }
        !           414:                        continue;
        !           415: 
        !           416:                 case BreakpointType::Instruction:
        !           417:                        if (CheckBreakpointInst(bp) == false) {
        !           418:                                continue;
        !           419:                        }
        !           420:                        break;
        !           421: 
        !           422:                 default:
        !           423:                        continue;
        !           424:                }
        !           425: 
        !           426:                // 条件は成立したのでカウントとスキップ
        !           427:                bp.matched++;
        !           428: 
        !           429:                // skip == -1 は常にブレークしない (カウントするだけ)
        !           430:                if (bp.skip < 0) {
        !           431:                        continue;
        !           432:                }
        !           433: 
        !           434:                // skip が 1 以上なら、remain を減算
        !           435:                if (bp.skip > 0 && --bp.skipremain > 0) {
        !           436:                        continue;
        !           437:                }
        !           438: 
        !           439:                // ブレーク成立
        !           440:                is_break = true;
        !           441:        }
        !           442: 
        !           443:        // 例外通知は通過ごとに常に下ろしておく
        !           444:        bv_vector = -1;
        !           445: 
        !           446:        // ブレークポイントが1つでも成立したかどうかを返す
        !           447:        return is_break;
        !           448: }
        !           449: 
        !           450: // 命令ブレークポイントが成立するか調べる。
        !           451: bool
        !           452: Debugger::CheckBreakpointInst(breakpoint_t& bp)
        !           453: {
        !           454:        saddr_t laddr;
        !           455:        uint64 paddr;
        !           456: 
        !           457:        // uint32 bi_inst が現在の PC 位置の命令データ (左詰め)
        !           458:        // bi_inst_bytes が読み込んだバイト数(bi_inst の左からの有効バイト数)。
        !           459:        // bi_need_bytes が現在のブレークポイントで読み込む必要のあるバイト数。
        !           460: 
        !           461:        laddr.addr = md->GetPC();
        !           462:        laddr.super = md->IsSuper();
        !           463:        laddr.logical = true;
        !           464:        laddr.search = true;
        !           465:        // 必要なバイト数に達するまで読み足す
        !           466:        while (bi_inst_bytes < bi_need_bytes) {
        !           467:                // アドレスを求める。
        !           468:                // 論理アクセスで ATC になければテーブルサーチも試みる。
        !           469:                if (md->MMUEnabled()) {
        !           470:                        paddr = md->TranslateAddr(laddr);
        !           471:                        if ((int64)paddr < 0) {
        !           472:                                // MMU 時点でバスエラー
        !           473:                                return false;
        !           474:                        }
        !           475:                } else {
        !           476:                        paddr = laddr.addr;
        !           477:                }
        !           478: 
        !           479:                // 命令長ずつ読み込む。
        !           480:                for (int i = 0; i < md->inst_bits / 8; i++) {
        !           481:                        uint64 data = vm_phys_peek_8(paddr);
        !           482:                        if ((int64)data < 0) {
        !           483:                                return false;
        !           484:                        }
        !           485:                        bi_inst = (bi_inst << 8) | data;
        !           486:                        bi_inst_bytes++;
        !           487:                        laddr.addr++;
        !           488:                        paddr++;
        !           489:                }
        !           490:        }
        !           491:        // 左詰めにする
        !           492:        bi_inst <<= (4 - bi_inst_bytes) * 8;
        !           493: 
        !           494:        // 必要なバイト数取得できたので比較
        !           495:        if ((bi_inst & bp.mask) == bp.inst) {
        !           496:                return true;
        !           497:        }
        !           498:        return false;
        !           499: }
        !           500: 
        !           501: // 例外通知 (MPU からの連絡用)
        !           502: void
        !           503: debugger_notifyexception(int vector)
        !           504: {
        !           505:        gDebugger->NotifyException(vector);
        !           506: }
        !           507: 
        !           508: // 例外通知 (本体)
        !           509: void
        !           510: Debugger::NotifyException(int vector)
        !           511: {
        !           512:        bv_vector = vector;
        !           513: }
        !           514: 
1.1       root      515: // コマンド一覧。
1.1.1.3   root      516: // "r" から始まるレジスタ表示系コマンドはコード中で別処理してある。
                    517: /*static*/ std::vector<Debugger::cmddef_t> Debugger::cmdtable = {
1.1.1.4 ! root      518:        { "bi",         &Debugger::cmd_bi,              Debugger::CommandAction::Stay },
        !           519:        { "bm",         &Debugger::cmd_bm,              Debugger::CommandAction::Stay },
        !           520:        { "bv",         &Debugger::cmd_bv,              Debugger::CommandAction::Stay },
1.1.1.3   root      521:        { "bx",         &Debugger::cmd_bx,              Debugger::CommandAction::Stay },
                    522:        { "b",          &Debugger::cmd_b,               Debugger::CommandAction::Stay },
                    523:        { "brhist",     &Debugger::cmd_brhist,  Debugger::CommandAction::Stay },
                    524:        { "c",          &Debugger::cmd_c,               Debugger::CommandAction::Leave },
                    525:        { "d",          &Debugger::cmd_d,               Debugger::CommandAction::Stay },
                    526:        { "dt",         &Debugger::cmd_dt,              Debugger::CommandAction::Stay },
                    527:        { "D",          &Debugger::cmd_D,               Debugger::CommandAction::Stay },
1.1.1.4 ! root      528:        { "exhist",     &Debugger::cmd_exhist,  Debugger::CommandAction::Stay },
1.1.1.3   root      529:        { "h",          &Debugger::cmd_h,               Debugger::CommandAction::Stay },
                    530:        { "help",       &Debugger::cmd_h,               Debugger::CommandAction::Stay },
                    531:        { "L",          &Debugger::cmd_L,               Debugger::CommandAction::Stay },
                    532:        { "m",          &Debugger::cmd_m,               Debugger::CommandAction::Stay },
                    533:        { "mt",         &Debugger::cmd_mt,              Debugger::CommandAction::Stay },
                    534:        { "M",          &Debugger::cmd_M,               Debugger::CommandAction::Stay },
                    535:        { "n",          &Debugger::cmd_n,               Debugger::CommandAction::Leave },
                    536:        { "q",          &Debugger::cmd_q,               Debugger::CommandAction::Quit },
                    537:        { "quit",       &Debugger::cmd_q,               Debugger::CommandAction::Quit },
                    538:        { "s",          &Debugger::cmd_s,               Debugger::CommandAction::Leave },
                    539:        { "so",         &Debugger::cmd_so,              Debugger::CommandAction::Leave },
                    540:        { "show",       &Debugger::cmd_show,    Debugger::CommandAction::Stay },
                    541:        { "t",          &Debugger::cmd_t,               Debugger::CommandAction::Leave },
                    542:        { "-",          &Debugger::cmd_minus,   Debugger::CommandAction::Stay },
1.1       root      543: };
                    544: 
                    545: // ヘルプ
                    546: void
                    547: Debugger::cmd_h()
                    548: {
1.1.1.3   root      549:        if (args.size() > 1) {
                    550:                if (args[1] == "r") {
                    551:                        Help(md->GetRegisterHelp());
                    552:                        return;
                    553:                }
                    554:        }
                    555:        // 引数なしか、あっても対応してないやつなら通常ヘルプを表示
                    556:        Help(HelpMsgMain);
                    557:        return;
                    558: }
                    559: 
                    560: /*static*/ const HelpMessages
                    561: Debugger::HelpMsgMain = {
                    562:        { "b",                                          "ブレークポイント一覧表示" },
                    563:        { "b #n",                                       "ブレークポイント n を削除" },
1.1.1.4 ! root      564:        { "b <addr> [<skip>]",          "ブレークポイント設定" },
        !           565:        { "bm <addr> [<skip>]",         "メモリブレークポイント設定" },
        !           566:        { "bi <inst>[:<mask>] [<skip>]",        "命令ブレークポイント設定" },
        !           567:        { "bv <vec>[-<vec>] [<skip>]",  "例外ブレークポイント設定" },
1.1.1.3   root      568:        { "bx",                                         "ブレークポイント全削除" },
                    569:        { "brhist [<lines>]",           "ブランチ履歴表示" },
                    570:        { "c [<addr>]",                         "実行再開(continue)" },
                    571:        { "d  [<addr> [<lines>]",       "逆アセンブル(論理、テーブルサーチなし)" },
                    572:        { "dt [<addr> [<lines>]",       "逆アセンブル(論理、テーブルサーチあり)" },
                    573:        { "D  [<addr> [<lines>]",       "逆アセンブル(物理)" },
1.1.1.4 ! root      574:        { "exhist [<lines>]",           "例外履歴表示" },
1.1.1.3   root      575:        { "h",                                          "ヘルプ(help)" },
                    576:        { "L <name>=<lv>",                      "ログレベル設定" },
                    577:        { "m  [<addr> [<lines>]",       "メモリダンプ(論理、テーブルサーチなし)" },
                    578:        { "mt [<addr> [<lines>]",       "メモリダンプ(論理、テーブルサーチあり)" },
                    579:        { "M  [<addr> [<lines>]",       "メモリダンプ(物理)" },
                    580:        { "n <cnt?:1>",                         "ステップ実行 (サブルーチンを飛ばす)" },
                    581:        { "q",                                          "quit" },
                    582:        { "r*",                                         "各種レジスタを表示 (see \"help r\")" },
1.1.1.4 ! root      583:        { "s [<cnt?:1>]",                       "ステップ実行 (途中レジスタを表示しない)" },
1.1.1.3   root      584:        { "so",                                         "ステップアウト" },
                    585:        { "show <monitor>",                     "モニター表示" },
1.1.1.4 ! root      586:        { "t [<cnt?:1>]",                       "トレース実行 (命令ごとにレジスタを表示)" },
1.1.1.3   root      587: };
                    588: 
                    589: void
                    590: Debugger::Help(HelpMessages msgs)
                    591: {
                    592:        for (const auto& pair : msgs) {
                    593:                cons->Print(" %-23s %s\n", pair.first.c_str(), pair.second.c_str());
1.1       root      594:        }
                    595: }
                    596: 
1.1.1.3   root      597: // cmdbuf を args... に分解する。
1.1       root      598: void
1.1.1.3   root      599: Debugger::ParseCmdbuf()
1.1       root      600: {
1.1.1.3   root      601:        int pos;
                    602:        int start;
                    603: 
                    604:        pos = 0;
                    605:        args.clear();
1.1       root      606: 
                    607:        // 引数を空白で分解
1.1.1.3   root      608:        for (;;) {
                    609:                // 先頭の空白はスキップ
                    610:                for (; cmdbuf[pos] != '\0'; pos++) {
                    611:                        if (!isspace((unsigned int)cmdbuf[pos]))
                    612:                                break;
                    613:                }
                    614:                if (cmdbuf[pos] == '\0')
1.1       root      615:                        break;
                    616: 
1.1.1.3   root      617:                // 終わりを探す
                    618:                start = pos;
                    619:                for (; cmdbuf[pos] != '\0'; pos++) {
                    620:                        if (isspace((unsigned int)cmdbuf[pos]))
1.1       root      621:                                break;
                    622:                }
1.1.1.3   root      623:                args.push_back(cmdbuf.substr(start, pos - start));
1.1.1.2   root      624:        }
                    625:        // デバッグ表示
                    626:        if (0) {
1.1.1.3   root      627:                for (int i = 0 ; i < args.size(); i++) {
                    628:                        printf("args[%d]=|%s|\n", i, args[i].c_str());
1.1.1.2   root      629:                }
                    630:        }
1.1       root      631: }
                    632: 
                    633: //
                    634: // デバッガコマンド
                    635: //
                    636: 
                    637: // ブレークポイント
1.1.1.4 ! root      638: // b                 ... 一覧表示
        !           639: // b #n              ... #n を削除
        !           640: // b <addr> [<skip>] ... 設定
1.1       root      641: void
                    642: Debugger::cmd_b()
                    643: {
                    644:        // 引数なしなら一覧表示
1.1.1.3   root      645:        if (args.size() < 2) {
1.1       root      646:                return cmd_b_list();
                    647:        }
                    648: 
                    649:        // 引数取得
1.1.1.3   root      650:        if (args[1][0] == '#') {
1.1       root      651:                // #<n> 形式なら、指定番号のブレークポイントを削除
1.1.1.3   root      652:                int i = atoi(&args[1][1]);
                    653:                if (i < 0 || i >= bpoint.size()) {
1.1.1.4 ! root      654:                        cons->Print("invaild breakpoint number: #%d\n", i);
1.1       root      655:                        return;
                    656:                }
1.1.1.3   root      657:                auto& bp = bpoint[i];
1.1.1.4 ! root      658:                if (bp.type == BreakpointType::Unused) {
        !           659:                        cons->Print("invalid breakpoint number: #%d\n", i);
        !           660:                        return;
1.1       root      661:                }
1.1.1.4 ! root      662:                cons->Print("breakpoint #%d (%08x) removed\n",
        !           663:                        i, bp.addr);
        !           664:                bp.type = BreakpointType::Unused;
        !           665:                // 今登録されている命令ブレークの必要命令長を再計算
        !           666:                RecalcInstMask();
1.1       root      667:                return;
                    668:        }
                    669: 
1.1.1.4 ! root      670:        return cmd_b_set(BreakpointType::Address);
        !           671: }
        !           672: 
        !           673: // メモリブレークポイントの設定
        !           674: // bm <addr> [<skip>]
        !           675: void
        !           676: Debugger::cmd_bm()
        !           677: {
        !           678:        // XXX m68k では未サポート
        !           679:        if (dynamic_cast<DebuggerMD_m680x0 *>(md)) {
        !           680:                cons->Print("bm not supported yet on m68k\n");
        !           681:                return;
        !           682:        }
        !           683:        cmd_b_set(BreakpointType::Memory);
        !           684: }
        !           685: 
        !           686: // type が違うだけの各種ブレークポイント設定の共通部分。
        !           687: void
        !           688: Debugger::cmd_b_set(BreakpointType type)
        !           689: {
        !           690:        breakpoint_t bp;
        !           691: 
        !           692:        if (args.size() < 2) {
        !           693:                cons->Print("usage: %s <addr> [<skipcount>]\n", args[0].c_str());
        !           694:                return;
        !           695:        }
        !           696: 
        !           697:        // アドレス
        !           698:        if (!ParseAddr(args[1].c_str(), &bp.addr)) {
        !           699:                return;
        !           700:        }
        !           701:        // あればスキップカウント
        !           702:        if (args.size() > 2) {
        !           703:                bp.skip = atoi(args[2].c_str());
        !           704:        }
        !           705: 
        !           706:        // 空いてるところにセット
        !           707:        // (よく似たエントリがあっても干渉しない)
        !           708:        bp.type = type;
        !           709:        int bi = AddBreakpoint(bp);
        !           710:        if (bi == -1) {
        !           711:                cons->Print("no free breakpoints\n");
1.1       root      712:        } else {
1.1.1.4 ! root      713:                cons->Print("breakpoint #%d added\n", bi);
1.1.1.3   root      714:        }
1.1.1.4 ! root      715: }
        !           716: 
        !           717: // 命令ブレークポイントの設定
        !           718: // bi <insn>[:<mask>] [<skip>]
        !           719: void
        !           720: Debugger::cmd_bi()
        !           721: {
        !           722:        breakpoint_t bp;
        !           723:        std::string inststr;
        !           724:        std::string maskstr;
        !           725:        int instlen;
        !           726:        int masklen;
        !           727: 
        !           728:        if (args.size() < 2) {
        !           729:                cons->Print("usage: bi <inst>[:<mask>] [<skipcount>]\n");
1.1.1.3   root      730:                return;
1.1       root      731:        }
                    732: 
1.1.1.4 ! root      733:        // 引数をまず分離
        !           734:        auto pos = args[1].find(':');
        !           735:        if (pos == std::string::npos) {
        !           736:                // マスク指定なし
        !           737:                inststr = args[1];
        !           738:                instlen = inststr.size();
        !           739:                masklen = -1;
        !           740:        } else {
        !           741:                // マスク指定あり
        !           742:                inststr = args[1].substr(0, pos);
        !           743:                instlen = inststr.size();
        !           744:                maskstr = args[1].substr(pos + 1);
        !           745:                masklen = maskstr.size();
        !           746:        }
        !           747: 
        !           748:        // 命令部チェック
        !           749:        if (ParseVerbHex(inststr.c_str(), &bp.inst) == false) {
        !           750:                cons->Print("%s: invalid instruction value\n", args[1].c_str());
        !           751:                return;
        !           752:        }
        !           753:        if (instlen % (md->inst_bits / 4) != 0) {
        !           754:                cons->Print("%s: invalid instruction length\n", args[1].c_str());
        !           755:                return;
        !           756:        }
        !           757: 
        !           758:        // マスク部チェック
        !           759:        bp.mask = 0xffffffff;
        !           760:        if (masklen != -1) {
        !           761:                if (ParseVerbHex(maskstr.c_str(), &bp.mask) == false) {
        !           762:                        cons->Print("%s: invalid mask value\n", args[1].c_str());
        !           763:                        return;
        !           764:                }
        !           765:                if (masklen != instlen) {
        !           766:                        cons->Print("%s: inst:mask must be the same length\n",
        !           767:                                args[1].c_str());
1.1       root      768:                        return;
                    769:                }
                    770:        }
1.1.1.4 ! root      771:        // 8バイト未満なら左詰め。
        !           772:        if (md->inst_bits < 32 && instlen < 8) {
        !           773:                bp.inst <<= 32 - instlen * 4;
        !           774:                bp.mask <<= 32 - instlen * 4;
        !           775:        }
        !           776: 
        !           777:        // あればスキップカウント
        !           778:        bp.skip = 0;
        !           779:        if (args.size() > 2) {
        !           780:                bp.skip = atoi(args[2].c_str());
        !           781:        }
        !           782: 
        !           783:        // 空いてるところにセット
        !           784:        bp.type = BreakpointType::Instruction;
        !           785:        int bi = AddBreakpoint(bp);
        !           786:        if (bi == -1) {
        !           787:                cons->Print("no free breakpoints\n");
        !           788:        } else {
        !           789:                cons->Print("breakpoint #%d added\n", bi);
        !           790:        }
        !           791: 
        !           792:        // 今登録されている命令ブレークの必要命令長を再計算
        !           793:        RecalcInstMask();
        !           794: }
        !           795: 
        !           796: // 例外ブレークポイントの設定
        !           797: // bv <vector_number>[-<end_number>] [<skip>]
        !           798: void
        !           799: Debugger::cmd_bv()
        !           800: {
        !           801:        breakpoint_t bp;
        !           802: 
        !           803:        if (args.size() < 2) {
        !           804:                cons->Print("usage: be <vec#>[-<end#>] [<skipcount>]\n");
        !           805:                return;
        !           806:        }
        !           807: 
        !           808:        auto pos = args[1].find('-');
        !           809:        if (pos == std::string::npos) {
        !           810:                // ベクタ番号が1つなら vec1, vec2 を同値にしておく。
        !           811:                if (ParseVerbHex(args[1].c_str(), (uint32 *)&bp.vec1) == false) {
        !           812:                        cons->Print("%s: invalid vector number\n", args[1].c_str());
        !           813:                        return;
        !           814:                }
        !           815:                bp.vec2 = bp.vec1;
        !           816:        } else {
        !           817:                // ベクタ番号(範囲指定 [vec1, vec2])
        !           818:                std::string str1 = args[1].substr(0, pos);
        !           819:                std::string str2 = args[1].substr(pos + 1);
        !           820: 
        !           821:                if (ParseVerbHex(str1.c_str(), (uint32 *)&bp.vec1) == false) {
        !           822:                        cons->Print("%s: invalid first vector number\n", args[1].c_str());
        !           823:                        return;
        !           824:                }
        !           825:                if (ParseVerbHex(str2.c_str(), (uint32 *)&bp.vec2) == false) {
        !           826:                        cons->Print("%s: invalid last vector number\n", args[1].c_str());
        !           827:                        return;
        !           828:                }
        !           829:        }
        !           830: 
        !           831:        // 範囲チェック
        !           832:        if (bp.vec1 < 0 || bp.vec1 >= md->vector_max) {
        !           833:                cons->Print("$%x: invalid vector number\n", bp.vec1);
        !           834:                return;
        !           835:        }
        !           836:        if (bp.vec2 < 0 || bp.vec2 >= md->vector_max) {
        !           837:                cons->Print("$%x: invalid last vector number\n", bp.vec2);
        !           838:                return;
        !           839:        }
        !           840: 
        !           841:        // 大小が逆なら入れ替える?
        !           842:        if (bp.vec1 > bp.vec2) {
        !           843:                int tmp;
        !           844:                tmp = bp.vec1;
        !           845:                bp.vec1 = bp.vec2;
        !           846:                bp.vec2 = tmp;
        !           847:        }
        !           848: 
        !           849:        // あればスキップカウント
        !           850:        bp.skip = 0;
        !           851:        if (args.size() > 2) {
        !           852:                bp.skip = atoi(args[2].c_str());
        !           853:        }
1.1       root      854: 
                    855:        // 空いてるところにセット
1.1.1.4 ! root      856:        bp.type = BreakpointType::Exception;
        !           857:        int bi = AddBreakpoint(bp);
1.1       root      858:        if (bi == -1) {
1.1.1.4 ! root      859:                cons->Print("no free breakpoints\n");
1.1       root      860:        } else {
1.1.1.4 ! root      861:                cons->Print("breakpoint #%d added\n", bi);
1.1       root      862:        }
                    863: }
                    864: 
                    865: // ブレークポイント一覧表示
                    866: void
                    867: Debugger::cmd_b_list()
                    868: {
1.1.1.4 ! root      869:        ShowMonitor(*gBreakpointMonitor);
        !           870: }
        !           871: 
        !           872: // ブレークポイント一覧 (モニタ)
        !           873: void
        !           874: Debugger::MonitorBreakpoint(TextScreen& monitor)
        !           875: {
        !           876:        // 0         1         2         3         4         5         6
        !           877:        // 0123456789012345678901234567890123456789012345678901234567890
        !           878:        // No Type Parameter         Matched    Skip
        !           879:        // #0 addr $01234567         123456789  123456789/123456789
        !           880:        // #1 inst 00000000/00000000
        !           881:        // #2 excp $00-$00
        !           882: 
        !           883:        monitor.Clear();
        !           884:        monitor.Print(0, 0, "No Type Parameter");
        !           885:        monitor.Print(26, 0, "Matched");
        !           886:        monitor.Print(37, 0, "Skip");
1.1       root      887: 
1.1.1.3   root      888:        for (int i = 0; i < bpoint.size(); i++) {
1.1.1.4 ! root      889:                const auto& bp = bpoint[i];
        !           890:                int y = i + 1;
        !           891: 
        !           892:                monitor.Print(0, y, "#%d", i);
        !           893: 
        !           894:                // 種別ごとの表示
        !           895:                switch (bp.type) {
        !           896:                 case BreakpointType::Unused:
        !           897:                        continue;
        !           898: 
        !           899:                 case BreakpointType::Address:
        !           900:                        monitor.Print(3, y, "addr $%08x", bp.addr);
        !           901:                        break;
        !           902:                 case BreakpointType::Memory:
        !           903:                        monitor.Print(3, y, "mem  $%08x", bp.addr);
        !           904:                        break;
        !           905: 
        !           906:                 case BreakpointType::Exception:
        !           907:                        monitor.Print(3, y, "excp $%02x", bp.vec1);
        !           908:                        if (bp.vec2 != bp.vec1) {
        !           909:                                monitor.Print(11, y, "-$%02x", bp.vec2);
        !           910:                        }
        !           911:                        break;
        !           912: 
        !           913:                 case BreakpointType::Instruction:
        !           914:                        monitor.Print(3, y, "inst");
        !           915:                        if (md->inst_bits == 32) {
        !           916:                                if (bp.mask == 0xffffffff) {
        !           917:                                        monitor.Print(8, y, "%08x", bp.inst);
        !           918:                                } else {
        !           919:                                        monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
        !           920:                                }
        !           921:                        } else {
        !           922:                                if (bp.mask == 0xffffffff) {
        !           923:                                        monitor.Print(8, y, "%08x", bp.inst);
        !           924:                                } else if (((bp.inst | bp.mask) & 0x0000ffff) != 0) {
        !           925:                                        monitor.Print(8, y, "%08x:%08x", bp.inst, bp.mask);
        !           926:                                } else if (bp.mask == 0xffff0000) {
        !           927:                                        monitor.Print(8, y, "%04x", bp.inst >> 16);
        !           928:                                } else {
        !           929:                                        monitor.Print(8, y, "%04x:%04x",
        !           930:                                                bp.inst >> 16, bp.mask >> 16);
        !           931:                                }
        !           932:                        }
        !           933:                        break;
        !           934: 
        !           935:                 default:
        !           936:                        monitor.Print(3, y, "type=%d", (int)bp.type);
        !           937:                        continue;
        !           938:                }
        !           939: 
        !           940:                // マッチ回数
        !           941:                monitor.Print(26, y, "%d", bp.matched);
        !           942: 
        !           943:                // スキップ
        !           944:                if (bp.skip < 0) {
        !           945:                        monitor.Print(37, y, "forever");
        !           946:                } else if (bp.skip > 0) {
        !           947:                        monitor.Print(37, y, "%d / %d", (bp.skip - bp.skipremain), bp.skip);
1.1       root      948:                }
                    949:        }
                    950: }
                    951: 
                    952: // ブレークポイント全削除
                    953: void
                    954: Debugger::cmd_bx()
                    955: {
1.1.1.3   root      956:        for (auto& bp : bpoint) {
1.1.1.4 ! root      957:                bp.type = BreakpointType::Unused;
1.1       root      958:        }
                    959:        cons->Print(" All breakpoints disabled\n");
1.1.1.4 ! root      960: 
        !           961:        // 今登録されている命令ブレークの必要命令長を再計算
        !           962:        RecalcInstMask();
1.1       root      963: }
                    964: 
                    965: // ブレークポイントを設定。
1.1.1.4 ! root      966: // new_bp のうち matched, skipremain はこちらで初期化する。
        !           967: // それ以外を埋めてから呼ぶこと。
        !           968: // 設定できればその番号、できなければ -1 を返す。
1.1       root      969: int
1.1.1.4 ! root      970: Debugger::AddBreakpoint(const breakpoint_t& new_bp)
1.1       root      971: {
1.1.1.3   root      972:        for (int i = 0; i < bpoint.size(); i++) {
                    973:                auto& bp = bpoint[i];
1.1.1.4 ! root      974:                if (bp.type == BreakpointType::Unused) {
        !           975:                        bp = new_bp;
        !           976:                        bp.matched = 0;
        !           977:                        if (bp.skip > 0) {
        !           978:                                bp.skipremain = bp.skip;
        !           979:                        } else {
        !           980:                                bp.skipremain = 0;
        !           981:                        }
        !           982: 
1.1       root      983:                        return i;
                    984:                }
                    985:        }
                    986:        return -1;
                    987: }
                    988: 
1.1.1.4 ! root      989: // すべての命令ブレークのマスクのうち最長のものを再計算する。
        !           990: // 命令ブレークポイントの追加/削除のたびに呼び出すこと。
        !           991: void
        !           992: Debugger::RecalcInstMask()
        !           993: {
        !           994:        uint32 mask;
        !           995:        bi_need_bytes = 0;
        !           996: 
        !           997:        // 登録されている命令ブレークの最長マスクを求める
        !           998:        mask = 0;
        !           999:        for (const auto& bp : bpoint) {
        !          1000:                if (bp.type == BreakpointType::Instruction) {
        !          1001:                        mask |= bp.mask;
        !          1002:                }
        !          1003:        }
        !          1004: 
        !          1005:        // mask の Number of Trailing Zero を求める。
        !          1006:        // (x & -x) で x の最も下の立ってるビットだけを立てる、
        !          1007:        // (x & -x) -1 でそれより下の全ビットを立てる、
        !          1008:        // それを popcount で数えるので、$fffffff0 なら ntz = 4 になる。
        !          1009:        int ntz = __builtin_popcount((mask & -(int32)mask) - 1);
        !          1010:        // 上位側から数えたマスクに必要なビット数
        !          1011:        int mlen = 32 - ntz;
        !          1012:        // 命令語単位に切り上げる
        !          1013:        mlen = roundup(mlen, md->inst_bits);
        !          1014:        // バイト数に変換
        !          1015:        mlen /= 8;
        !          1016: 
        !          1017:        if (mlen > bi_need_bytes) {
        !          1018:                bi_need_bytes = mlen;
        !          1019:        }
        !          1020: }
        !          1021: 
        !          1022: // ブランチ履歴、例外履歴表示
        !          1023: // brhist [<maxlines>]
        !          1024: // exhist [<maxlines>]
        !          1025: // <maxlines> は表示する最大エントリ数。
        !          1026: // コンソールではデフォルトで下を新しいの順とする。<maxlines> を負数にすると
        !          1027: // (行数は絶対値して) 並び順を逆にしてモニタウィンドウと同じ上を新しいの順に
        !          1028: // する。
1.1       root     1029: void
                   1030: Debugger::cmd_brhist()
                   1031: {
1.1.1.4 ! root     1032:        cmd_hist_common(md->GetBrHist(), 0);
        !          1033: }
        !          1034: void
        !          1035: Debugger::cmd_exhist()
        !          1036: {
        !          1037:        cmd_hist_common(md->GetExHist(), BranchHistory::ExHist);
        !          1038: }
1.1.1.3   root     1039: 
1.1.1.4 ! root     1040: // ブランチ履歴、例外履歴表示の共通部分。
        !          1041: void
        !          1042: Debugger::cmd_hist_common(BranchHistory& hist, uint64 flag)
        !          1043: {
        !          1044:        // 表示最大行数(と向き)
        !          1045:        // 向きは bottom_to_top = true が新しいほうを下とする方向。
        !          1046:        int maxlines = 20;
        !          1047:        bool bottom_to_top = true;
1.1.1.3   root     1048:        if (args.size() > 1) {
1.1.1.4 ! root     1049:                maxlines = atoi(args[1].c_str());
        !          1050:                if (maxlines < 0) {
        !          1051:                        bottom_to_top = false;
        !          1052:                        maxlines = -maxlines;
1.1.1.3   root     1053:                }
1.1.1.4 ! root     1054: 
        !          1055:                if (maxlines < 1)
        !          1056:                        maxlines = 1;
        !          1057:                if (maxlines > 256)
        !          1058:                        maxlines = 256;
        !          1059:        }
        !          1060: 
        !          1061:        // コンソールでは空エントリの行は表示したくないので、
        !          1062:        // 先にエントリ数を調べる。
        !          1063:        int used = hist.GetUsed();
        !          1064: 
        !          1065:        // 表示行数 + 1行はヘッダ分
        !          1066:        int lines = std::min(used, maxlines) + 1;
        !          1067: 
        !          1068:        // MonitorUpdate() は TextScreen 高さに合わせて出力してくれる。
        !          1069:        auto size = hist.GetMonitorSize();
        !          1070:        TextScreen tscr;
        !          1071:        tscr.Init(size.width, lines);
        !          1072: 
        !          1073:        // コンソールでは下が新しいの順のほうがいい
        !          1074:        if (bottom_to_top) {
        !          1075:                flag |= BranchHistory::BottomToTop;
1.1.1.3   root     1076:        }
1.1.1.4 ! root     1077:        tscr.userdata = flag;
        !          1078: 
        !          1079:        // 表示
        !          1080:        hist.MonitorUpdate(tscr);
        !          1081:        ShowTextScreen(tscr);
1.1       root     1082: }
                   1083: 
                   1084: // 実行再開(continue): c [<addr>]
                   1085: // <addr> 指定があれば <addr> まで実行。
                   1086: void
                   1087: Debugger::cmd_c()
                   1088: {
1.1.1.3   root     1089:        if (args.size() > 1) {
1.1       root     1090:                // 引数があれば
1.1.1.3   root     1091:                if (!ParseAddr(args[1].c_str(), &bc_addr)) {
1.1       root     1092:                        return;
                   1093:                }
                   1094:                // 偶数番地に丸める
                   1095:                bc_addr &= 0xfffffffe;
                   1096: 
                   1097:                bc_enable = true;
                   1098:        }
                   1099: }
                   1100: 
                   1101: // 引数などからアドレスを取得する共通ルーチン。
1.1.1.3   root     1102: // 引数が1つ(以上)あれば args[1] をアドレスとしてパースする。
1.1       root     1103: // なければ、lastaddr が指定されていればそれを使う。
1.1.1.2   root     1104: // それもなければ現在の PC を使う。
1.1       root     1105: bool
                   1106: Debugger::GetAddr(uint32& addr, uint32& lastaddr)
                   1107: {
1.1.1.3   root     1108:        if (args.size() > 1) {
1.1       root     1109:                // 1つ目の引数があれば、開始アドレス
1.1.1.3   root     1110:                if (ParseAddr(args[1].c_str(), &addr) == false) {
1.1       root     1111:                        return false;
                   1112:                }
                   1113:                // 偶数番地からに丸める
                   1114:                addr &= 0xfffffffe;
                   1115:        } else if (lastaddr != 0xffffffff) {
                   1116:                // 引数なしで前回値があれば前回の続き
                   1117:                addr = lastaddr;
                   1118:        } else {
                   1119:                // そうでなければ、今の PC
1.1.1.2   root     1120:                addr = md->GetPC();
1.1       root     1121:        }
                   1122:        return true;
                   1123: }
                   1124: 
                   1125: // 逆アセンブル: d [<addr> [<cnt>]] (論理、テーブルサーチなし)
                   1126: void
                   1127: Debugger::cmd_d()
                   1128: {
                   1129:        cmd_d_common(MemdumpMode::Logical_atc);
                   1130: }
                   1131: 
                   1132: // 逆アセンブル: dt [<addr>] [<cnt>]] (論理、テーブルサーチあり)
                   1133: void
                   1134: Debugger::cmd_dt()
                   1135: {
                   1136:        cmd_d_common(MemdumpMode::Logical_search);
                   1137: }
                   1138: 
                   1139: // 逆アセンブル: D [<addr> [<cnt>]] (物理)
                   1140: void
                   1141: Debugger::cmd_D()
                   1142: {
                   1143:        cmd_d_common(MemdumpMode::Physical);
                   1144: }
                   1145: 
                   1146: // 逆アセンブル共通
                   1147: void
                   1148: Debugger::cmd_d_common(MemdumpMode mode)
                   1149: {
                   1150:        uint32 addr;
1.1.1.2   root     1151:        saddr_t saddr;
1.1       root     1152:        int cnt;
                   1153: 
                   1154:        cnt = 10;
                   1155: 
                   1156:        // 開始アドレス
                   1157:        if (GetAddr(addr, d_last_addr) == false) {
                   1158:                return;
                   1159:        }
                   1160:        // 2つ目の引数があれば行数
1.1.1.3   root     1161:        if (args.size() > 2) {
                   1162:                cnt = strtol(args[2].c_str(), NULL, 10);
1.1       root     1163:        }
                   1164: 
1.1.1.2   root     1165:        saddr.addr  = addr;
                   1166:        saddr.super = md->IsSuper();
                   1167: 
                   1168:        // XXX そのうちなんとかする
                   1169:        if (mode == MemdumpMode::Logical_search) {
                   1170:                saddr.logical = true;
                   1171:                saddr.search = true;
                   1172:        } else if (mode == MemdumpMode::Logical_atc) {
                   1173:                saddr.logical = true;
                   1174:        }
                   1175: 
1.1       root     1176:        for (int i = 0; i < cnt; i++) {
1.1.1.2   root     1177:                std::string addrbuf;
                   1178:                std::string mnemonic;
                   1179:                std::string mnembuf;
                   1180:                std::vector<uint8> local_ir;
                   1181: 
                   1182:                if (FormatDumpAddr(saddr, addrbuf)) {
                   1183:                        md->Disassemble(saddr, mnemonic, local_ir);
                   1184:                        // オフライン用に整形
                   1185:                        mnembuf = md->FormatDisasm(mnemonic, local_ir);
1.1       root     1186:                }
1.1.1.2   root     1187:                // アドレス変換でバスエラーが起きてもここは表示する
                   1188:                cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
                   1189: 
                   1190:                // 変換できなければここで終了
                   1191:                if (local_ir.size() == 0)
                   1192:                        break;
                   1193:                saddr.addr += local_ir.size();
1.1       root     1194:        }
1.1.1.2   root     1195: 
                   1196:        d_last_addr = saddr.addr;
1.1       root     1197: }
                   1198: 
                   1199: // ログレベル設定: L <name> <level>
                   1200: void
                   1201: Debugger::cmd_L()
                   1202: {
                   1203:        const char *name;
                   1204:        int level;
                   1205: 
1.1.1.3   root     1206:        if (args.size() < 3) {
1.1       root     1207:                cons->Print("L <name> <level>\n");
                   1208:                return;
                   1209:        }
                   1210: 
1.1.1.3   root     1211:        name = args[1].c_str();
                   1212:        level = atoi(args[2].c_str());
1.1       root     1213: 
                   1214:        // 短縮形とかエイリアスとか
                   1215:        // lib/mainapp.cpp に同じものがあるのでどうにかしたほうがいい
                   1216:        if (strcmp(name, "sch") == 0)
                   1217:                name = "scheduler";
                   1218:        if (strcmp(name, "scc") == 0)
                   1219:                name = "sio";
                   1220: 
1.1.1.3   root     1221:        // 複数のオブジェクトが同じ logname を持ってもよい (CMMUとか)。
1.1       root     1222:        // all はどうするか
                   1223:        std::string sname = name;
                   1224:        bool found = false;
                   1225:        for (auto& obj : gObjects) {
                   1226:                if (obj->logname == sname) {
                   1227:                        obj->loglevel = level;
                   1228:                        cons->Print("%s=%d\n", name, obj->loglevel);
                   1229:                        found = true;
                   1230:                }
                   1231:        }
                   1232:        // 見付からない場合はエラー
                   1233:        if (!found) {
                   1234:                cons->Print("%s: invalid logname\n", name);
                   1235:        }
                   1236: }
                   1237: 
                   1238: // メモリダンプ: m [<addr> [<cnt>]] (論理、テーブルサーチなし)
                   1239: void
                   1240: Debugger::cmd_m()
                   1241: {
                   1242:        cmd_m_common(MemdumpMode::Logical_atc);
                   1243: }
                   1244: 
                   1245: // メモリダンプ: mt [<addr> [<cnt>]] (論理、テーブルサーチあり)
                   1246: void
                   1247: Debugger::cmd_mt()
                   1248: {
                   1249:        cmd_m_common(MemdumpMode::Logical_search);
                   1250: }
                   1251: 
                   1252: // メモリダンプ: M [<addr> [<cnt>]] (物理)
                   1253: void
                   1254: Debugger::cmd_M()
                   1255: {
                   1256:        cmd_m_common(MemdumpMode::Physical);
                   1257: }
                   1258: 
                   1259: // メモリダンプ共通
                   1260: void
                   1261: Debugger::cmd_m_common(MemdumpMode mode)
                   1262: {
                   1263:        uint32 addr;
1.1.1.2   root     1264:        saddr_t saddr;
1.1       root     1265:        int cnt;
                   1266: 
                   1267:        cnt = 10;
                   1268: 
                   1269:        // 開始アドレス
                   1270:        if (GetAddr(addr, m_last_addr) == false) {
                   1271:                return;
                   1272:        }
                   1273:        // 2つ目の引数があれば行数
1.1.1.3   root     1274:        if (args.size() > 2) {
                   1275:                cnt = strtol(args[2].c_str(), NULL, 10);
1.1       root     1276:        }
                   1277: 
1.1.1.2   root     1278:        saddr.addr  = addr;
                   1279:        saddr.super = md->IsSuper();
                   1280:        // XXX
                   1281:        if (mode == MemdumpMode::Logical_search) {
                   1282:                saddr.logical = true;
                   1283:                saddr.search = true;
                   1284:        } else if (mode == MemdumpMode::Logical_atc) {
                   1285:                saddr.logical = true;
                   1286:        }
                   1287: 
1.1       root     1288:        for (int i = 0; i < cnt; i++) {
1.1.1.2   root     1289:                std::string addrbuf;
                   1290:                std::string dumpbuf;
                   1291:                std::string charbuf;
                   1292: 
                   1293:                if (FormatDumpAddr(saddr, addrbuf) == false) {
                   1294:                        // アドレス変換でバスエラーならダンプもくそもない
                   1295:                        cons->Print("%s\n", addrbuf.c_str());
                   1296:                        // ここで終わる?
                   1297:                        // XXX 16バイト先を試してみるとか?
                   1298:                        break;
                   1299:                }
                   1300: 
                   1301:                for (int j = 0; j < 8; j++) {
                   1302:                        uint64 paddr;
                   1303:                        // もう一回アドレス変換するのやや冗長だが仕方ない
                   1304:                        if (saddr.IsLogical() && md->MMUEnabled()) {
                   1305:                                // アドレス変換
                   1306:                                paddr = md->TranslateAddr(saddr);
                   1307:                                if ((int64)paddr < 0) {
                   1308:                                        // バスエラーが起きたら改行して仕切り直し
                   1309:                                        break;
                   1310:                                }
                   1311:                        } else {
                   1312:                                paddr = saddr.addr;
                   1313:                        }
                   1314: 
                   1315:                        for (int k = 0; k < 2; k++) {
                   1316:                                uint64 b = vm_phys_peek_8(paddr + k);
                   1317: 
                   1318:                                if ((int64)b < 0) {
                   1319:                                        dumpbuf += "--";
                   1320:                                        charbuf += " ";
                   1321:                                } else {
                   1322:                                        uint32 c = (uint32)b;
                   1323:                                        dumpbuf += string_format("%02x", c);
                   1324:                                        charbuf += string_format("%c",
                   1325:                                                (0x20 <= c && c <= 0x7e) ? c : '.');
                   1326:                                }
                   1327:                        }
                   1328:                        dumpbuf += " ";
                   1329:                        saddr.addr += 2;
                   1330:                }
                   1331:                cons->Print("%-18s %-40s %s\n",
                   1332:                        addrbuf.c_str(), dumpbuf.c_str(), charbuf.c_str());
1.1       root     1333:        }
1.1.1.2   root     1334:        m_last_addr = saddr.addr;
1.1       root     1335: }
                   1336: 
                   1337: // プロンプトに来た最初に表示するいつものやつを再表示する
                   1338: void
                   1339: Debugger::cmd_minus()
                   1340: {
1.1.1.3   root     1341:        // 通常レジスタを表示
                   1342:        std::vector<std::string> tmpargs {{ "r" }};
                   1343:        md->ShowRegister(cons, tmpargs);
1.1       root     1344: 
                   1345:        // 論理アクセス時、ATC ミスが分かったほうが便利だと思う
1.1.1.2   root     1346:        saddr_t laddr;
                   1347:        laddr.addr  = pc;
                   1348:        laddr.super = md->IsSuper();
                   1349:        laddr.logical = true;
                   1350:        laddr.search = false;
                   1351: 
                   1352:        // 現在の PC の位置を逆アセンブル。ここで ir を更新。
                   1353:        std::string addrbuf;
                   1354:        std::string mnemonic;
                   1355:        std::string mnembuf;
                   1356:        ir.clear();
                   1357:        // アドレスを表示用に
                   1358:        if (FormatDumpAddr(laddr, addrbuf)) {
                   1359:                md->Disassemble(laddr, mnemonic, ir);
                   1360:                // ライブ表示用に整形
                   1361:                mnembuf = md->FormatDisasmLive(mnemonic, ir);
                   1362:        }
                   1363:        cons->Print("%-18s %s\n", addrbuf.c_str(), mnembuf.c_str());
                   1364: 
                   1365:        // XXX m88k?
                   1366:        nextpc = pc + ir.size();
1.1       root     1367: }
                   1368: 
                   1369: // ステップ実行: n[<count>?=1] (サブルーチンを飛ばす)
                   1370: void
                   1371: Debugger::cmd_n()
                   1372: {
1.1.1.3   root     1373:        if (args.size() > 1) {
1.1       root     1374:                int count;
1.1.1.3   root     1375:                count = strtol(args[1].c_str(), NULL, 10);
1.1       root     1376:                if (count < 1) {
                   1377:                        cons->Print(" invalid step count: %d\n", count);
                   1378:                        return;
                   1379:                }
                   1380: 
                   1381:                n_count = count;
                   1382:        } else {
                   1383:                n_count = 1;
                   1384:        }
                   1385:        n_enable = true;
                   1386:        SetNBreakpoint();
                   1387: }
                   1388: 
                   1389: // n コマンド用のサブルーチンを飛ばすブレークポイントを設定。
                   1390: // 戻り値は、プロンプトをスキップするなら true。
                   1391: bool
                   1392: Debugger::SetNBreakpoint()
                   1393: {
                   1394:        // n コマンドは、通常はブレークポイントを次の命令に仕掛けながら
                   1395:        // 実行するが、いくつかの命令ではブレークポイントではなく、
                   1396:        // 命令トレース (t コマンド) のように動作させる。
                   1397:        // トレース動作する命令は以下の通り。
                   1398:        //  Bcc, BRA
                   1399:        //  JMP
                   1400:        //  RTD, RTR, RTS
                   1401:        //  RTE
                   1402:        //  FBcc
                   1403:        //  FDBcc (2ワード目無視)
                   1404:        //  (RTM は 68020 専用なので対応しない)
                   1405: #define IsBcc(x)       ((((x) & 0xf000) == 0x6000) && (((x) & 0xff00) != 0x6100))
                   1406: #define IsJMP(x)       (((x) & 0xffc0) == 0x4ec0)
                   1407: #define IsRTD(x)       (((x) & 0xffff) == 0x4e74)
                   1408: #define IsRTE(x)       (((x) & 0xffff) == 0x4e73)
                   1409: #define IsRTR(x)       (((x) & 0xffff) == 0x4e77)
                   1410: #define IsRTS(x)       (((x) & 0xffff) == 0x4e75)
                   1411: #define IsFBcc(x)      (((x) & 0xf180) == 0xf080)
                   1412: #define IsFDBcc(x)     (((x) & 0xf1f8) == 0xf048)
                   1413: 
                   1414:        uint16 op;
                   1415: 
                   1416:        // XXX この辺統一的にしたほうがいい
                   1417:        // XXX logical peek のような気がする
1.1.1.2   root     1418:        op = (((uint16)vm_phys_peek_8(pc)) << 8)
                   1419:           | vm_phys_peek_8(pc + 1);
1.1       root     1420: 
                   1421:        if (IsBcc(op)
                   1422:         || IsJMP(op)
                   1423:         || IsRTD(op)
                   1424:         || IsRTE(op)
                   1425:         || IsRTR(op)
                   1426:         || IsRTS(op)
                   1427:         || IsFBcc(op)
                   1428:         || IsFDBcc(op)
                   1429:        ) {
                   1430:                n_breakenable = false;
                   1431:                s_enable = true;
                   1432:                s_count = 1;
                   1433:                return false;
                   1434:        } else {
                   1435:                n_breakenable = true;
                   1436:                n_breakaddr = nextpc;
                   1437:                return true;
                   1438:        }
                   1439: }
                   1440: 
1.1.1.2   root     1441: // デバッガ終了コマンド
                   1442: void
                   1443: Debugger::cmd_q()
                   1444: {
                   1445:        cons->Print("quit debugger console.\n");
1.1       root     1446: }
                   1447: 
                   1448: // モニター表示
                   1449: void
                   1450: Debugger::cmd_show()
                   1451: {
1.1.1.3   root     1452:        if (args.size() != 2) {
1.1       root     1453:                cons->Print("usage: show <monitor>\n");
                   1454:                for (auto& d : gObjects) {
                   1455:                        if (d->logname != "?") {
                   1456:                                cons->Print(" %s\n", d->logname.c_str());
                   1457:                        }
                   1458:                }
                   1459:                return;
                   1460:        }
                   1461: 
1.1.1.3   root     1462:        std::string name = args[1];
1.1       root     1463:        for (auto& d : gObjects) {
                   1464:                if (d->logname == name) {
1.1.1.3   root     1465:                        ShowMonitor(*d);
1.1       root     1466:                        return;
                   1467:                }
                   1468:        }
                   1469:        // 一致しない
                   1470:        cons->Print("show: no such objects\n");
                   1471: }
                   1472: 
1.1.1.3   root     1473: // モニターを更新して表示。
                   1474: void
1.1.1.4 ! root     1475: Debugger::ShowMonitor(IMonitor& monitor)
1.1.1.3   root     1476: {
                   1477:        // モニタを更新
1.1.1.4 ! root     1478:        TextScreen ts;
        !          1479: 
        !          1480:        auto size = monitor.GetMonitorSize();
        !          1481:        ts.Init(size.width, size.height);
        !          1482: 
        !          1483:        monitor.MonitorUpdate(ts);
        !          1484:        ShowTextScreen(ts);
1.1.1.3   root     1485: }
                   1486: 
                   1487: // テキストスクリーンを表示。
1.1       root     1488: void
1.1.1.3   root     1489: Debugger::ShowTextScreen(TextScreen& monitor)
1.1       root     1490: {
                   1491:        char sbuf[1024];        // 適当
                   1492: 
                   1493:        // monitor 内部バッファから ShiftJIS バッファを作成。
                   1494:        // その際属性もエスケープシーケンスで再現する。
                   1495:        int col = monitor.GetCol();
                   1496:        int row = monitor.GetRow();
                   1497:        const uint16 *src = monitor.GetBuf();
                   1498: 
                   1499:        for (int y = 0; y < row; y++) {
                   1500:                int sy = y * col;
                   1501:                int sx;
                   1502:                uint attr;
                   1503:                char *d;
                   1504: 
                   1505:                memset(sbuf, 0, sizeof(sbuf));
                   1506:                d = sbuf;
                   1507:                attr = TA::Normal;
                   1508:                for (sx = 0; sx < col; sx++) {
                   1509:                        uint a  = src[sy + sx] & 0xff00;
                   1510:                        uint ch = src[sy + sx] & 0x00ff;
                   1511: 
                   1512:                        // 属性の変わり目
                   1513:                        if (attr != a) {
                   1514:                                switch (a) {
                   1515:                                 case TA::Normal:
                   1516:                                 case TA::Off:
                   1517:                                        *d++ = 0x1b;
                   1518:                                        *d++ = '[';
                   1519:                                        *d++ = 'm';
                   1520:                                        break;
                   1521:                                 case TA::On:
                   1522:                                        *d++ = 0x1b;
                   1523:                                        *d++ = '[';
                   1524:                                        *d++ = '7';
                   1525:                                        *d++ = 'm';
                   1526:                                        break;
                   1527:                                 case TA::Disable:
                   1528:                                        *d++ = 0x1b;
                   1529:                                        *d++ = '[';
                   1530:                                        *d++ = '2';
                   1531:                                        *d++ = 'm';
                   1532:                                        break;
                   1533:                                 case TA::Em:
                   1534:                                        *d++ = 0x1b;
                   1535:                                        *d++ = '[';
                   1536:                                        *d++ = '1';
                   1537:                                        *d++ = 'm';
                   1538:                                        break;
                   1539:                                }
                   1540:                                attr = a;
                   1541:                        }
                   1542:                        *d++ = ch;
                   1543:                }
1.1.1.3   root     1544:                // 行の終わりで一旦属性を戻しておく
                   1545:                if (attr != TA::Normal) {
                   1546:                        *d++ = 0x1b;
                   1547:                        *d++ = '[';
                   1548:                        *d++ = 'm';
                   1549:                }
1.1       root     1550:                *d = '\0';
                   1551: 
                   1552:                // XXX 日本語が使われてれば UTF-8 にしないといけないはず
                   1553: 
                   1554:                cons->Print("%s\n", sbuf);
                   1555:        }
                   1556: }
                   1557: 
                   1558: // ステップ実行: s[<count>?:1]
1.1.1.4 ! root     1559: // 途中レジスタを表示せずに <count> 命令実行する
1.1       root     1560: void
                   1561: Debugger::cmd_s()
                   1562: {
1.1.1.3   root     1563:        if (args.size() > 1) {
1.1       root     1564:                int count;
1.1.1.3   root     1565:                count = strtol(args[1].c_str(), NULL, 10);
1.1       root     1566:                if (count < 1) {
                   1567:                        cons->Print(" invalid step count: %d\n", count);
                   1568:                        return;
                   1569:                }
                   1570: 
                   1571:                s_count = count;
                   1572:        } else {
                   1573:                s_count = 1;
                   1574:        }
                   1575:        s_enable = true;
                   1576: }
                   1577: 
                   1578: // ステップアウト
                   1579: void
                   1580: Debugger::cmd_so()
                   1581: {
                   1582:        so_enable = true;
1.1.1.2   root     1583:        md->SetStepOut();
1.1       root     1584: }
                   1585: 
                   1586: // トレース実行: t[<count>?:1]
1.1.1.4 ! root     1587: // レジスタを表示しながら <count> 命令実行する
1.1       root     1588: void
                   1589: Debugger::cmd_t()
                   1590: {
1.1.1.3   root     1591:        if (args.size() > 1) {
1.1       root     1592:                int count;
1.1.1.3   root     1593:                count = strtol(args[1].c_str(), NULL, 10);
1.1       root     1594:                if (count < 1) {
                   1595:                        cons->Print(" invalid trace count: %d\n", count);
                   1596:                        return;
                   1597:                }
                   1598:                t_count = count;
                   1599:        } else {
                   1600:                t_count = 1;
                   1601:        }
                   1602:        t_enable = true;
                   1603: }
                   1604: 
1.1.1.4 ! root     1605: // 引数で示されるプレフィックスなしの16進数値を返す。
        !          1606: // 正しく変換できれば値を valp に格納して true を返す。
        !          1607: // そうでなければ false を返す。
        !          1608: bool
        !          1609: Debugger::ParseVerbHex(const char *arg, uint32 *valp)
        !          1610: {
        !          1611:        uint32 val;
        !          1612:        char *end;
        !          1613: 
        !          1614:        errno = 0;
        !          1615:        val = strtoul(arg, &end, 16);
        !          1616:        if (arg[0] == '\0' || end[0] != '\0') {
        !          1617:                return false;
        !          1618:        }
        !          1619:        if (errno == ERANGE) {
        !          1620:                return false;
        !          1621:        }
        !          1622: 
        !          1623:        *valp = val;
        !          1624:        return true;
        !          1625: }
        !          1626: 
1.1       root     1627: // 引数で示されるアドレスを返す。
                   1628: // '%' で始まればレジスタ。
                   1629: // そうでなければ16進数値。
                   1630: // アドレスが正しく取得できればアドレスを addr に格納し真を返す。
                   1631: // そうでなければエラーメッセージを表示して偽を返す。
                   1632: bool
1.1.1.4 ! root     1633: Debugger::ParseAddr(const char *arg, uint32 *addrp)
1.1       root     1634: {
1.1.1.4 ! root     1635:        uint32 addr;
1.1       root     1636:        char *end;
                   1637: 
                   1638:        if (arg[0] == '%') {
1.1.1.2   root     1639:                // '%' から始まればレジスタ
                   1640:                uint64 r;
                   1641:                r = md->GetRegAddr(arg + 1);
                   1642:                if ((int64)r < 0) {
                   1643: #if 0 // not yet
1.1       root     1644:                        cons->Print("valid register name are: %s%s\n",
                   1645:                                "%d0-%d7,%a0-%a7,%sp,%usp,%isp,%pc",
                   1646:                                ",%msp,%vbr,%srp,%crp");
1.1.1.2   root     1647: #endif
                   1648:                        cons->Print("invalid register name\n");
1.1       root     1649:                        return false;
                   1650:                }
1.1.1.2   root     1651:                addr = (uint32)r;
1.1       root     1652:        } else {
                   1653:                /* そうでなければ番地指定 */
                   1654:                errno = 0;
                   1655:                addr = strtoul(arg, &end, 16);
                   1656:                if (arg[0] == '\0' || end[0] != '\0') {
                   1657:                        cons->Print("invalid address: '%s'\n", arg);
                   1658:                        return false;
                   1659:                }
                   1660:                if (errno == ERANGE) {
                   1661:                        cons->Print("out of range: %s\n", arg);
                   1662:                        return false;
                   1663:                }
                   1664:        }
                   1665: 
                   1666:        *addrp = addr;
                   1667:        return true;
                   1668: }
                   1669: 
1.1.1.2   root     1670: // (論理、物理)アドレスを表示用に整形。
                   1671: // 逆アセンブル表示、メモリダンプ表示等で共通。
                   1672: // アドレス変換でバスエラーが起きたら false を返す (この場合も str は有効)。
                   1673: // 末尾を空白パディングはしていないので必要なら "%-18s" 等して使うこと。
                   1674: //  123456789012345678
                   1675: // "00112233:44556677:"
1.1       root     1676: bool
1.1.1.2   root     1677: Debugger::FormatDumpAddr(saddr_t laddr, std::string& str)
1.1       root     1678: {
1.1.1.2   root     1679:        bool rv = true;
1.1       root     1680: 
1.1.1.2   root     1681:        // laddr は常に表示
                   1682:        str = string_format("%08x", laddr.addr);
1.1       root     1683: 
1.1.1.2   root     1684:        if (laddr.IsLogical() && md->MMUEnabled()) {
                   1685:                // アドレス変換してみる
                   1686:                uint64 paddr = md->TranslateAddr(laddr);
                   1687:                if ((int64)paddr < 0) {
                   1688:                        // MMU 時点でバスエラー
                   1689:                        str += ":BusErr";
                   1690:                        rv = false;
                   1691:                } else if (paddr == laddr.addr) {
                   1692:                        // PA==VA
                   1693:                        str += "=PA";
1.1       root     1694:                } else {
1.1.1.2   root     1695:                        // PA!=VA
                   1696:                        str += string_format(":%08x:", (uint32)paddr);
1.1       root     1697:                }
1.1.1.2   root     1698:        } else {
                   1699:                // 物理アクセス
                   1700:                str += ":";
1.1       root     1701:        }
                   1702: 
1.1.1.2   root     1703:        return rv;
1.1       root     1704: }
1.1.1.4 ! root     1705: 
        !          1706: //
        !          1707: // ブレークポイントモニター
        !          1708: //
        !          1709: 
        !          1710: // コンストラクタ
        !          1711: BreakpointMonitor::BreakpointMonitor()
        !          1712: {
        !          1713:        monitor_size = nnSize(55, 9);
        !          1714: }
        !          1715: 
        !          1716: void
        !          1717: BreakpointMonitor::MonitorUpdate(TextScreen& monitor)
        !          1718: {
        !          1719:        gDebugger->MonitorBreakpoint(monitor);
        !          1720: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.