--- nono/debugger/debugger_private.h 2026/04/29 17:04:30 1.1 +++ nono/debugger/debugger_private.h 2026/04/29 17:05:09 1.1.1.9 @@ -1,194 +1,134 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt +// + +// +// デバッガ (内部用) // #pragma once -#include "header.h" +#include "branchhistory.h" #include "bus.h" -#include "cvprompt.h" +#include "monitor.h" #include "textscreen.h" +#include "thread.h" +#include +#include +#include -// 諸情報付きのアドレス -struct saddr_t -{ - uint32 addr = 0; // アドレス(32bit) +using ConstStringPair = std::pair; +using HelpMessages = std::vector; - // addr が論理アドレスなら true。 - // MMU の有効/無効状態はここにセットするのではなく、メモリアクセスに - // 際してアドレス変換を行う側がその時点で改めてチェックするので、 - // ここはどちらかというと物理アドレスなら false くらいの意味合い。 - bool logical = false; - - // addr が論理アドレスの場合に、テーブルサーチも行うなら true。 - // false なら TT、ATC のみ検索する。 - bool search = false; +class HostCOMDevice; +class VectorTable; - // addr がスーパバイザアクセスなら true。 - bool super = false; +// 諸情報付きのアドレス +class saddr_t +{ + public: + // コンストラクタ + saddr_t() { } - // addr がデータアクセスなら true。 - bool data = false; + saddr_t(uint32 addr_, bool super_) + : saddr_t(addr_, super_, false) { } - // コンストラクタ - saddr_t() {} + saddr_t(uint32 addr_, bool super_, bool data_) { + Set(addr_, super_, data_); + } // コピーコンストラクタ - saddr_t(const saddr_t& lhs) { - addr = lhs.addr; - logical = lhs.logical; - search = lhs.search; - super = lhs.super; - data = lhs.super; + saddr_t(const saddr_t& rhs) { + Set(rhs); } - - bool IsLogical() const { return logical; } - bool IsSearch() const { return search; } - bool IsSuper() const { return super; } - bool IsData() const { return data; } - bool IsInst() const { return !data; } - - // (論理)アドレスと Super/User ビットを合わせた 33bit アドレスを返す - uint64 GetSaddr() const { - uint64 sa = addr; - if (IsSuper()) { - sa |= 0x100000000ULL; - } - return sa; + // 代入演算子 + saddr_t& operator=(const saddr_t& rhs) { + Set(rhs); + return *this; } -}; - -enum MemdumpMode { - Physical = 0, // 物理アドレス - Logical_atc, // 論理アドレス (ただし TT、ATC のみ検索) - Logical_search, // 論理アドレス (TT、ATC、テーブルサーチすべて行う) -}; - -class Console; -class DebuggerMD; -class Disasm; -class Debugger -{ - friend class DebuggerMD_m680x0; - friend class DebuggerMD_m88xx0; - private: - // 型 - typedef void (Debugger::*cmdfunc_t)(); - typedef struct { - const char *name; - cmdfunc_t func; - } cmddef_t; - - // ブレークポイント - typedef struct { - bool enable; - bool ismemory; - uint32 addr; - uint32 count; - } breakpoint_t; - static const int MAX_BREAKPOINTS = 8; + // 代入演算子 (uint32 を代入するとアドレスだけ差し替える) + saddr_t& operator=(uint32 rhs) { + SetAddr(rhs); + return *this; + } - public: - Debugger(); - virtual ~Debugger() { } + // 代入 + void Set(uint32 addr_, bool super_, bool data_) { + addr = addr_; + super = super_; + data = data_; + } - void Init(); - void ThreadRun(); - bool Check(); + uint32 GetAddr() const { return addr; } + bool IsSuper() const { return super; } + bool IsData() const { return data; } + void SetAddr(uint32 addr_) { addr = addr_; } + void SetSuper(bool super_) { super = super_; } + void SetData(bool data_) { data = data_; } + + // (uint32)saddr でアドレスだけ取り出せると楽 + explicit operator uint32() const { return GetAddr(); } + + // saddr += n でアドレス進められると楽 + uint32 operator+=(uint32 val) { + addr += val; + return addr; + } + // saddr++ でアドレス進められると楽 + void operator++(int n) { + addr++; + } private: - bool MainLoop(); - cmddef_t *ParseCmd(); - // コマンド名は大文字小文字を区別する関係でスネークスタイル。 - void cmd_b(); - void cmd_b_list(); - void cmd_brhist(); - void cmd_bx(); - void cmd_c(); - void cmd_d(); - void cmd_dt(); - void cmd_D(); - void cmd_h(); - void cmd_L(); - void cmd_m(); - void cmd_mt(); - void cmd_M(); - void cmd_minus(); - void cmd_n(); - void cmd_q(); - void cmd_r(); - void cmd_ra(); - void cmd_rc(); - void cmd_rf(); - void cmd_rm(); - void cmd_ro(); - void cmd_s(); - void cmd_so(); - void cmd_show(); - void cmd_t(); - void cmd_unknown(); - - int AddBreakpoint(uint32 addr, bool ismemory); - void Continue(); - bool GetAddr(uint32& addr, uint32& lastaddr); - void cmd_d_common(MemdumpMode mode); - void cmd_m_common(MemdumpMode mode); - bool ParseAddr(const char *arg, uint32_t *addrp); - bool SetNBreakpoint(); - - void HelpMain(); - - void ShowMonitor(TextScreen& monitor); - // アドレスをダンプ表示用に整形して返す - bool FormatDumpAddr(saddr_t addr, std::string& addrstr); + void Set(const saddr_t& rhs) { + addr = rhs.addr; + super = rhs.super; + data = rhs.data; + } - DebuggerMD *md = NULL; // 機種依存部分 + uint32 addr {}; // アドレス (32bit) + bool super {}; // スーパーバイザアクセスなら true + bool data {}; // データ空間アクセスなら true +}; - Console *cons = NULL; // 入出力 - const char *prompt; // プロンプト文字列 - std::string cmdbuf {}; // 現在のコマンドライン - std::string last_cmdbuf {}; // 直前のコマンドライン - int ac = 0; - std::string strav[10] { {}, }; - const char *av[10] {}; +enum class MemoryMode : bool +{ + Logical = false, + Physical = true, +}; - uint32 pc = 0; - uint32 nextpc = 0; - std::vector ir {}; // この後実行する命令のバイナリ列 - breakpoint_t bpoint[MAX_BREAKPOINTS] {}; - bool bc_enable = false; - uint32 bc_addr = 0; - uint32 d_last_addr = 0; - uint32 m_last_addr = 0; - bool n_enable = false; - bool n_breakenable = false; - uint32 n_breakaddr = 0; - uint32 n_count = 0; - bool s_enable = false; - uint32 s_count = 0; - bool so_enable = false; - bool t_enable = false; - uint32 t_count = 0; +enum class MMULookupMode : bool +{ + False = false, + True = true, + // MemoryMode::Physical ならこの項は意味を持たないので、 + // True でも False でもないと言いたい。 + None = false, +}; - // 一度VMを実行して再びプロンプトに来たら true - bool is_continued = false; +// saddr_t の後で Debugger の前... +#include "debugger_memory.h" - static cmddef_t cmdtable[]; - static cmddef_t cmdtable_unknown; -}; +class Debugger; +class Disasm; // デバッガの CPU 依存部分 class DebuggerMD { public: - DebuggerMD(Debugger *arg) { - parent = arg; + enum class Arch { + M680x0, + M88xx0, + }; - // 奇数にしておくことで初回に必ずアドレス変換を起こさせる。 - last_lpage.addr = 0xffffffff; + public: + DebuggerMD(Debugger *parent_, Arch arch_) { + parent = parent_; + arch = arch_; } - virtual ~DebuggerMD() { } + virtual ~DebuggerMD(); // CPU のリクエストフラグに flag を立てる virtual void ReqSet(uint32 flag) = 0; @@ -202,8 +142,9 @@ class DebuggerMD // アドレス変換をする。 // MMUEnabled() が true であることは呼び出し側が確認すること。 + // lookup が true ならテーブルサーチまで行う。 // 変換できなければ (uint64)-1 を返す。 - virtual uint64 TranslateAddr(saddr_t laddr) = 0; + virtual uint64 TranslateAddr(saddr_t laddr, bool lookup) const = 0; // 現在の命令先頭アドレスを返す virtual uint32 GetPC() const = 0; @@ -213,6 +154,9 @@ class DebuggerMD // ステップアウトなら true を返す virtual bool IsStepOut() const = 0; + // この命令がステップイン出来るなら true を返す (cmd_n 用) + virtual bool IsOpStepIn(DebuggerMemoryStream& mem) = 0; + // name で示されるのレジスタの値を返す。 // メモリダンプで指定するケースを想定しているので、明らかにアドレスでは // ないレジスタは含まなくてよい (m680x0 の SR など)。ただし Dn は含めて @@ -223,26 +167,29 @@ class DebuggerMD // 現在のレジスタセットを内部にバックアップする virtual void BackupRegs() = 0; - // レジスタの一覧を cons に出力 - virtual void ShowRegMain(Console *cons) = 0; - virtual void ShowRegCtrl(Console *cons) = 0; - virtual void ShowRegFPU(Console *cons) = 0; - virtual void ShowRegMMU(Console *cons) = 0; - virtual void ShowRegATC(Console *cons) = 0; - virtual void ShowRegOther(Console *cons) = 0; + // レジスタ表示系のコマンドを実行。処理すれば true を返す。 + virtual bool ShowRegister(FILE *, const std::vector& args) = 0; + // レジスタ表示系コマンドのヘルプ一覧を取得 + virtual const HelpMessages& GetHelpListReg() const = 0; + // レジスタ表示系コマンドのヘルプメッセージを取得 + virtual const HelpMessages& GetHelpReg() const = 0; + + // ブランチ履歴・例外履歴(オブジェクト)を取得 + virtual BranchHistory& GetBrHist() const = 0; + virtual BranchHistory& GetExHist() const = 0; // 直近の命令でアクセスした laddr が一致したら true を返す // 1 命令で複数にアクセスする場合を考慮して、md 側で判断する virtual bool CheckLEA(uint32 laddr) = 0; // 逆アセンブル。 - // laddr の位置の命令を一つ逆アセンブルする。 + // mem.laddr の位置の命令を一つ逆アセンブルする。 // 成功すれば true を返し、失敗すれば(?) false を返す。 // 成功した場合 mnemonic に逆アセンブルした文字列が、 // bin にこの命令のバイナリ列が格納されている。 // mnemonic は単純に命令部分のみ、16進ダンプや付随情報は含まない。 // bin はメモリイメージのバイト順で格納すること。 - virtual bool Disassemble(saddr_t laddr, + virtual bool Disassemble(DebuggerMemoryStream& mem, std::string& mnemonic, std::vector& bin) = 0; // 逆アセンブルをフォーマット。 @@ -253,14 +200,251 @@ class DebuggerMD virtual std::string FormatDisasmLive(const std::string& mnemonic, const std::vector& bin) = 0; + // 機種 + Arch arch {}; + + // 最短の1命令のバイト数 + uint inst_bytes {}; + // 命令が固定長ならそのバイト数、可変長なら 0 + uint inst_bytes_fixed {}; + protected: - Debugger *parent; // 親クラス + Debugger *parent {}; // 親クラス +}; + +class Debugger : public ThreadDevice +{ + using inherited = ThreadDevice; + friend class DebuggerMD_m680x0; + friend class DebuggerMD_m88xx0; + + private: + // リクエストフラグ + static const uint32 REQUEST_EXIT = 0x0001; // 終了 + static const uint32 REQUEST_RXCHAR = 0x0002; // 1文字受信 + static const uint32 REQUEST_ACCEPT = 0x0004; // 着信通知 + static const uint32 REQUEST_PROMPT = 0x0008; // プロンプト要求 + + // 型 + enum CommandAction { + Stay, // デバッガプロンプトに留まる + Leave, // デバッガプロンプトを抜けて VM を実行する + Quit, // デバッガとの接続を終了する + }; + using cmdfunc_t = void (Debugger::*)(); + struct cmddef_t { + const char *name; + cmdfunc_t func; + CommandAction action; + }; + + // ブレークポイント種別 + enum BreakpointType { + Unused = 0, + Address, // このアドレスに来る直前 + Memory, // このアドレスをアクセスした直後 + Exception, // この例外を検出した直後 + Instruction, // この命令を実行する直前 + }; + + // ブレークポイント + struct breakpoint_t { + BreakpointType type {}; // 種別 + // 種別ごとのパラメータ + union { + uint32 addr {}; // ターゲットアドレス (Address/Memory) + struct { + int vec1; // ベクタ番号 (開始) + int vec2; // ベクタ番号 (終了) + }; + struct { + uint32 inst; // 命令ワード + uint32 mask; // マスク + }; + }; + + uint32 matched {}; // 成立回数 (積算) + + // skip はユーザ指定値。ただし + // -1 なら常にスキップ、つまりブレークせずカウントのみ行う、 + // 0 ならスキップなし、つまり成立ごとにブレーク(これがデフォルト)、 + // n(>0) なら n 回の成立をスキップし、(n+1) 回目でブレークの意。 + int32 skip {}; // スキップ回数 (ユーザ指定値) + int32 skipremain {}; // 残りスキップ回数 (0 で成立) + }; + static const int MAX_BREAKPOINTS = 8; + + public: + Debugger(); + ~Debugger() override; + + void SetLogLevel(int) override; + bool Create() override; + bool Init() override; + void ThreadRun() override; + void Terminate() override; + + void Exec(); + void NotifyException(int vector); + + // MPU トレースが必要なら true を返す + bool IsTrace() const; + + // FILE コールバック + int ReadFunc(char *, int); + int WriteFunc(const char *, int); + + // メモリダンプモニタ + // GUI 用 (show コマンドでも見れるけど) + std::array memdump_monitor {}; + + // m/M コマンド用のメモリダンプモニタ + Monitor m_monitor { this }; + + private: + void Close(); + void RxCallback(); + void AcceptCallback(); + void Input(int); + void EnterPrompt(); + void LeavePrompt(); + void PrintPrompt(); + + CommandAction Command(); + bool CheckAllBreakpoints(); + bool CheckBreakpointInst(breakpoint_t&); + void ParseCmdbuf(); + // コマンド名は大文字小文字を区別する関係でスネークスタイル。 + void cmd_b(); + void cmd_bi(); + void cmd_bm(); + void cmd_bv(); + void cmd_b_list(); + void cmd_b_set(BreakpointType); + void cmd_b_delete(); + void cmd_brhist(); + void cmd_bx(); + void cmd_c(); + void cmd_d(); + void cmd_dt(); + void cmd_D(); + void cmd_disp(); + void cmd_exhist(); + void cmd_h(); + void cmd_hb(); + void cmd_hr(); + void cmd_L(); + void cmd_m(); + void cmd_mt(); + void cmd_M(); + void cmd_minus(); + void cmd_n(); + void cmd_nt(); + void cmd_q(); + void cmd_reset(); + void cmd_s(); + void cmd_st(); + void cmd_so(); + void cmd_sot(); + void cmd_show(); + void cmd_t(); + + bool Check(); + int AddBreakpoint(const breakpoint_t&); + void RecalcInstMask(); + bool GetAddr(saddr_t *addr, bool); + void cmd_d_common(MemoryMode access_mode, MMULookupMode lookup_mode); + void cmd_hist_common(BranchHistory& hist); + void cmd_m_common(MemoryMode access_mode, MMULookupMode lookup_mode); + void cmd_n_common(bool); + void cmd_s_common(bool); + void cmd_so_common(bool); + bool ParseVerbHex(const char *arg, uint32 *valp); + bool ParseAddr(const char *arg, uint32_t *addrp); + void SetNBreakpoint(); + + static const HelpMessages HelpListMain; + static const HelpMessages HelpListBreakpoints; + static const HelpMessages HelpDetails; + void ShowHelpList(const HelpMessages& msgs); + + // 個別ヘルプメッセージを出力用に置換 + std::string HelpConvert(const std::string& msg); + + // モニターを更新して表示 + void ShowMonitor(Monitor& monitor); + // テキストスクリーンを表示 + void ShowTextScreen(TextScreen& screen); + + std::unique_ptr md {}; // 機種依存部分 + + std::mutex mtx {}; + // このスレッドへのリクエスト + std::condition_variable cv_request {}; + uint32 request {}; + // コマンドモード(プロンプト)なら true + std::condition_variable cv_prompt {}; + bool prompt_released {}; + + bool is_prompt {}; + + // コンソール + std::unique_ptr hostcom /*{}*/; + FILE *cons {}; - // 前回アクセス時の論理ページ。.addr == 0xffffffff なら無効。 - saddr_t last_lpage; - uint32 last_ppage; + const char *prompt {}; // プロンプト文字列 + std::string cmdbuf {}; // 現在のコマンドライン + std::string last_cmdbuf {}; // 直前のコマンドライン + std::vector args {}; + + uint32 pc {}; + std::vector ir {}; // この後実行する命令のバイナリ列 + std::array bpoint {}; + bool bc_enable {}; + uint32 bc_addr {}; + uint32 bi_inst {}; + int bi_inst_bytes {}; + int bi_need_bytes {}; + int bv_vector = -1; + saddr_t d_last_addr {}; + saddr_t m_last_addr {}; + bool n_enable {}; + uint32 n_breakaddr {}; + uint32 n_count {}; + bool s_enable {}; + uint32 s_count {}; + bool so_enable {}; + bool t_enable {}; + uint32 t_count {}; + + // 一度VMを実行して再びプロンプトに来たら true + bool is_continued {}; + + // MPU を一時停止するとき true (ワンショット) + bool is_pause {}; + + // プロンプトで表示するレジスタ群 + std::vector disp_regs {}; + + // ブレークポイント到達メッセージ + // (コンソールが獲得できるまで保留しておくため) + std::string bpointmsg {}; + + // ブレークポイントモニタ + DECLARE_MONITOR_CALLBACK(MonitorUpdateBpoint); + Monitor bpoint_monitor { this }; + + // メモリダンプモニタ + DECLARE_MONITOR_CALLBACK(MonitorUpdateMemdump); + + // ベクタテーブル + std::unique_ptr pVectorTable /*{}*/; + + static std::vector cmdtable; }; +extern Debugger *gDebugger; + #define NORM "\x1b[0m" #define BOLD "\x1b[1m" #define BOLDIF(n) ((n) ? BOLD : "")