--- nono/wx/wxdumpmonitor.cpp 2026/04/29 17:05:12 1.1.1.12 +++ nono/wx/wxdumpmonitor.cpp 2026/04/29 17:05:16 1.1.1.13 @@ -9,14 +9,18 @@ // #include "wxdumpmonitor.h" -#include "wxmonitor.h" -#include "bus.h" -#include "debugger.h" +#include "mainapp.h" +#include "mainbus.h" enum { ID_TEXT = IDGROUP_MEMDUMP, - ID_PREV, - ID_NEXT, + ID_PREVLINE, + ID_PREVPAGE, + ID_NEXTLINE, + ID_NEXTPAGE, + ID_FORMAT, + + ID_EDIT, ID_local_end, // 最後に置く (チェック用) }; @@ -25,62 +29,122 @@ static_assert(ID_local_end - 1 <= (int)I // イベントテーブル wxBEGIN_EVENT_TABLE(WXMemdumpWindow, inherited) EVT_TEXT_ENTER(ID_TEXT, WXMemdumpWindow::OnTextEnter) - EVT_BUTTON(ID_PREV, WXMemdumpWindow::OnPrev) - EVT_BUTTON(ID_NEXT, WXMemdumpWindow::OnNext) + EVT_COMMAND(ID_PREVLINE, NONO_EVT_BUTTON, WXMemdumpWindow::OnPrevLine) + EVT_COMMAND(ID_PREVPAGE, NONO_EVT_BUTTON, WXMemdumpWindow::OnPrevPage) + EVT_COMMAND(ID_NEXTLINE, NONO_EVT_BUTTON, WXMemdumpWindow::OnNextLine) + EVT_COMMAND(ID_NEXTPAGE, NONO_EVT_BUTTON, WXMemdumpWindow::OnNextPage) + EVT_CHOICE(ID_FORMAT, WXMemdumpWindow::OnFormat) wxEND_EVENT_TABLE() // コンストラクタ // (タイトルは UpdateAddr() でセットする) -WXMemdumpWindow::WXMemdumpWindow(wxWindow *parent, int idx_) +WXMemdumpWindow::WXMemdumpWindow(wxWindow *parent, int monid_) : inherited(parent, wxID_ANY, wxEmptyString, DEFAULT_STYLE | wxRESIZE_BORDER) { - idx = idx_; - - // ↓+-------------------------+ - // | 上枠 (アドレス、△、▽) | - // +-------------------------+ - // | TextScreen | - // +-------------------------+ + monid = monid_; + if (IS_MONITOR_XPMEMDUMP(monid)) { + myidx = monid - ID_MONITOR_XPMEMDUMP0; + } else { + myidx = monid - ID_MONITOR_MEMDUMP0; + } + + // ↓ topsizer + // +--------------------------------------------+ + // |上枠 → | + // |+-----+------+-----+----------+-----+------+| + // ||余白 | "<<" | "<" | アドレス | ">" | ">>" || + // |+-----+------+-----+----------+-----+------+| + // +--------------------------------------------+ + // | TextScreen | + // +--------------------------------------------+ auto *topsizer = new wxBoxSizer(wxVERTICAL); // 上枠用の下敷きパネル - auto *ctrlpanel = new WXBitmapPanel(this); + auto *ctrlpanel = new wxPanel(this); topsizer->Add(ctrlpanel, 0, wxEXPAND); // 上枠用の横 sizer ctrlbox = new wxBoxSizer(wxHORIZONTAL); // アドレスコントロール - addrctrl = new wxTextCtrl(this, ID_TEXT, + addrctrl = new wxTextCtrl(ctrlpanel, ID_TEXT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); - ctrlbox->Add(addrctrl); - ctrlbox->AddSpacer(10); - - // △ボタン - // XXX とりあえずね - prev_btn = new wxButton(this, ID_PREV, wxT("△")); - next_btn = new wxButton(this, ID_NEXT, wxT("▽")); - ctrlbox->Add(prev_btn); - ctrlbox->Add(next_btn); + buttons[0] = new WXButton(ctrlpanel, ID_PREVPAGE, wxDefaultSize, "<<"); + buttons[1] = new WXButton(ctrlpanel, ID_PREVLINE, wxDefaultSize, "<"); + buttons[2] = new WXButton(ctrlpanel, ID_NEXTLINE, wxDefaultSize, ">"); + buttons[3] = new WXButton(ctrlpanel, ID_NEXTPAGE, wxDefaultSize, ">>"); + + ctrlbox->AddSpacer(3); + ctrlbox->Add(buttons[0], 0, wxALIGN_CENTER); + ctrlbox->Add(buttons[1], 0, wxALIGN_CENTER); + ctrlbox->AddSpacer(3); + ctrlbox->Add(addrctrl, 0, wxALIGN_CENTER); + ctrlbox->AddSpacer(3); + ctrlbox->Add(buttons[2], 0, wxALIGN_CENTER); + ctrlbox->Add(buttons[3], 0, wxALIGN_CENTER); + ctrlbox->AddSpacer(3); + + // 表示形式 (機種によって選択肢と名前が異なる) + if (gMainApp.Has(VMCap::M68K)) { + fmts.emplace_back(Memdump::Byte, _("Byte")); + fmts.emplace_back(Memdump::Word, _("Word")); + fmts.emplace_back(Memdump::Long, _("Long Word")); + fmts.emplace_back(Memdump::M68030PageShort, + _("MMU Descriptor (Short Format)")); + fmts.emplace_back(Memdump::M68030Disasm, _("68030 Disassemble")); + } else { + fmts.emplace_back(Memdump::Byte, _("Byte")); + fmts.emplace_back(Memdump::Word, _("Half Word")); + fmts.emplace_back(Memdump::Long, _("Word")); + fmts.emplace_back(Memdump::M88200Page, _("88200 Descriptor")); + fmts.emplace_back(Memdump::M88100Disasm, _("88100 Disassemble")); + } + if (gMainApp.Has(VMCap::LUNA)) { + fmts.emplace_back(Memdump::HD64180Disasm, _("HD64180 Disassemble")); + } + + // fmts から名前だけの配列を作る + std::vector fmt_choice; + for (auto p : fmts) { + fmt_choice.push_back(p.second); + } + fmtctrl = new wxChoice(ctrlpanel, ID_FORMAT, + wxDefaultPosition, wxDefaultSize, + fmt_choice.size(), &fmt_choice[0]); + ctrlbox->Add(fmtctrl, 0, wxALIGN_CENTER); // 上枠の sizer と下敷きを紐付ける ctrlpanel->SetSizer(ctrlbox); ctrlbox->SetSizeHints(ctrlpanel); - screen = new WXMonitorPanel(this, debugger_memdump_monitor(idx)); + screen = new WXMonitorPanel(this, gMonitorManager->Get(monid)); topsizer->Add(screen, 1, wxEXPAND); SetSizer(topsizer); // ウィンドウサイズを確定させる fixed_height = ctrlbox->GetSize().y; + FontChanged(); DoSize(); - // 表示を初期化する - screen->SetUserData(sticky_addr[idx]); + // パネルに来るマウスイベントをこっちに回す + screen->Connect(wxEVT_MOUSEWHEEL, + wxMouseEventHandler(WXMemdumpWindow::OnMouseWheel), NULL, this); + screen->Connect(wxEVT_LEFT_DCLICK, + wxMouseEventHandler(WXMemdumpWindow::OnDClick), NULL, this); + + // メモリダンプオブジェクトを取得 + auto mon = screen->GetMonitor(); + memdump = dynamic_cast(mon.obj); + assert(memdump); + + // 初期値をコントロールに設定 + auto fmt = memdump->GetFormat(); + fmtctrl->SetSelection(Format2Selection(fmt)); + DoFormat(fmt); UpdateAddr(); } @@ -96,6 +160,25 @@ WXMemdumpWindow::Layout() return LayoutTextVResize(screen, fixed_height); } +void +WXMemdumpWindow::FontChanged() +{ + inherited::FontChanged(); + + // 最小 24x24 にしてみる + wxSize sz(screen->GetFontWidth() * 2 + 4, + screen->GetFontHeight() + 4); + if (sz.x < 24) + sz.x = 24; + if (sz.y < 24) + sz.y = 24; + for (auto *btn : buttons) { + btn->SetClientSize(sz); + btn->SetMinClientSize(sz); + btn->FontChanged(); + } +} + // テキスト入力イベント void WXMemdumpWindow::OnTextEnter(wxCommandEvent& event) @@ -104,42 +187,48 @@ WXMemdumpWindow::OnTextEnter(wxCommandEv const wxString& wstr = event.GetString(); uint32 val = strtoul((const char *)wstr.mb_str(), NULL, 16); - // 16 に整列 - val &= ~15; - // アドレスを更新 - union64 udata; - udata.q = screen->GetUserData(); - udata.l = val; - screen->SetUserData(udata.q); + memdump->SetAddr(val); UpdateAddr(); } -// △(PREV) ボタン押下イベント +// "<"(前行) ボタン押下イベント void -WXMemdumpWindow::OnPrev(wxCommandEvent& event) +WXMemdumpWindow::OnPrevLine(wxCommandEvent& event) { - union64 udata; + // 1行戻る + memdump->Offset(memdump->GetLineOffset(-1)); - // 1ページ戻る (横16バイト固定 × 表示行数分) - udata.q = screen->GetUserData(); - udata.l -= screen->GetRow() * 16; + UpdateAddr(); +} + +// "<<"(前ページ) ボタン押下イベント +void +WXMemdumpWindow::OnPrevPage(wxCommandEvent& event) +{ + // 1ページ戻る + memdump->Offset(memdump->GetPageOffset(-1)); - screen->SetUserData(udata.q); UpdateAddr(); } -// ▽(NEXT) ボタン押下イベント +// ">"(次行) ボタン押下イベント void -WXMemdumpWindow::OnNext(wxCommandEvent& event) +WXMemdumpWindow::OnNextLine(wxCommandEvent& event) { - union64 udata; + // 1行進む + memdump->Offset(memdump->GetLineOffset(1)); + + UpdateAddr(); +} - // 1ページ進む (横16バイト固定 × 表示行数分) - udata.q = screen->GetUserData(); - udata.l += screen->GetRow() * 16; +// ">>"(次ページ) ボタン押下イベント +void +WXMemdumpWindow::OnNextPage(wxCommandEvent& event) +{ + // 1ページ進む + memdump->Offset(memdump->GetPageOffset(1)); - screen->SetUserData(udata.q); UpdateAddr(); } @@ -147,39 +236,365 @@ WXMemdumpWindow::OnNext(wxCommandEvent& void WXMemdumpWindow::UpdateAddr() { - uint32 addr = screen->GetUserData(); + int width; + std::string title; + if (IS_MONITOR_XPMEMDUMP(monid)) { + width = 5; + title = _("XP Memory Dump"); + } else { + width = 8; + title = _("Main Memory Dump"); + } - // 二次情報にコピー。 - sticky_addr[idx] = addr; + uint32 addr = memdump->GetAddr(); + std::string addrstr = strhex(addr, width); // EVT_TEXT を起こさず変更する - addrctrl->ChangeValue(wxString(string_format("%08x", addr))); + addrctrl->ChangeValue(addrstr); // カーソルを末尾に addrctrl->SetInsertionPointEnd(); // タイトルを変更 - SetTitle(_("Memory Dump") + string_format(" %d: $%08x", idx, addr)); + title += string_format(" %d: $", myidx); + title += addrstr; + SetTitle(title); + + // 即更新 + Refresh(); +} + +// 表示フォーマット変更イベント +void +WXMemdumpWindow::OnFormat(wxCommandEvent& event) +{ + int idx = event.GetSelection(); + DoFormat(Selection2Format(idx)); } -// ウィンドウ ID に対応する sticky address を返す -uint32 -WXMemdumpWindow::GetStickyAddr(int id) +// 表示フォーマット変更処理 +void +WXMemdumpWindow::DoFormat(Memdump::Format fmt) { - int n = id - ID_MONITOR_MEMDUMP0; - assert(n < sticky_addr.size()); - return sticky_addr[n]; + memdump->SetFormat(fmt); + + // SetFormat() によってアドレスが切り捨てられる場合があるので + // アドレス欄の表示ごと更新。 + UpdateAddr(); +} + +// マウスホイールイベント +void +WXMemdumpWindow::OnMouseWheel(wxMouseEvent& event) +{ + int delta = event.GetWheelRotation(); + + // 1回の変化で +/-120 が来る。 + // たいていのアプリケーションで3行ずつの移動に換算することが多いようだが + // ここはメモリダンプなので 4行ずつにする。 + + if (delta != 0) { + memdump->Offset(memdump->GetLineOffset(-delta / 30)); + UpdateAddr(); + } } -// ウィンドウ ID に対応する sticky address を設定する +// ダブルクリックイベント void -WXMemdumpWindow::SetStickyAddr(int id, uint32 addr) +WXMemdumpWindow::OnDClick(wxMouseEvent& event) +{ + event.Skip(); + + auto fmt = memdump->GetFormat(); + + // テキストの桁数、行数にする + wxPoint tpos = screen->GetTextPosition(event.GetPosition()); + int x = tpos.x; + int y = tpos.y; + + // ウィンドウサイズを変えた時に出来る下の空き地にはヒットさせない + if (y >= screen->GetRow()) { + return; + } + + // アドレス部分は常に除外でいいか + if (x < 19) { + return; + } + + // 現在のフォーマットでのクリック位置のアドレスを求める + int len = 0; + uint64 addr = -1; + switch (fmt) { + case Memdump::Byte: + len = 1; + addr = GetAddrAtHexdump(x, y, len); + break; + case Memdump::Word: + len = 2; + addr = GetAddrAtHexdump(x, y, len); + break; + case Memdump::Long: + len = 4; + addr = GetAddrAtHexdump(x, y, len); + break; + case Memdump::M68030PageShort: + case Memdump::M88200Page: + len = 4; + addr = GetAddrAtPageShort(x, y); + break; + + case Memdump::M68030PageLong: + PANIC("not supported"); + break; + + case Memdump::M68030Disasm: + len = 2; + addr = GetAddrAtDisasm(x, y, len); + break; + + case Memdump::M88100Disasm: + len = 4; + addr = GetAddrAtDisasm(x, y, len); + break; + + case Memdump::HD64180Disasm: + len = 1; + addr = GetAddrAtDisasm(x, y, len); + break; + + default: + break; + } + if ((int64)addr < 0) { + return; + } + addr &= ~(len - 1); + + // このアドレスが編集可能 (≒メモリ) か? + if (memdump->CanPoke(addr) == false) { + ::wxMessageBox(_("Can not edit here"), _("Memory Edit"), + wxICON_EXCLAMATION | wxOK | wxOK_DEFAULT, this); + return; + } + + // 出来ればここをハイライトとかしたい + + // 現在値を(再)取得。 + uint32 val = memdump->Peek(addr, len); + + // sizestr は表示形式ではなく単なるサイズ。(DescShort なら Long) + std::string sizestr; + switch (len) { + case 1: + sizestr = Format2String(Memdump::Byte); + break; + case 2: + sizestr = Format2String(Memdump::Word); + break; + case 4: + sizestr = Format2String(Memdump::Long); + break; + default: + PANIC("corrupted len=%d", len); + } + + // 編集 + auto dlg = new MemEditDialog(this, addr, val, len, sizestr); + if (dlg->ShowModal() == wxID_OK) { + uint32 data = dlg->GetData(); + + // 更新 + uint64 r = memdump->Poke(addr, data, len); + if ((int64)r < 0) { + ::wxMessageBox(_("Could not edit"), _("Memory Edit"), + wxICON_EXCLAMATION | wxOK | wxOK_DEFAULT, this); + return; + } + } +} + +// 16進ダンプ画面のテキスト座標 (x, y) に対応するアドレスを返す。 +// 対応するアドレスがない場合は (uint64)-1 を返す。 +uint64 +WXMemdumpWindow::GetAddrAtHexdump(int x, int y, int len) +{ + if (x < 68) { + // 16進ダンプ部分 + + // 空白部分なら除外 + x -= 19; + if (x % (len * 2 + 1) == len * 2) { + return (uint64)-1; + } + x = (x / (len * 2 + 1)) * len; + } else { + // 文字ダンプ部分 + x -= 68; + } + + if (x > 15) { + return (uint64)-1; + } + + return memdump->GetAddr() + (y * memdump->GetStride()) + x; +} + +// 32bit ページテーブル画面のテキスト座標 (x, y) に対応するアドレスを返す。 +// 対応するアドレスがない場合は (uint64)-1 を返す。 +uint64 +WXMemdumpWindow::GetAddrAtPageShort(int x, int y) { - int n = id - ID_MONITOR_MEMDUMP0; - assert(n < sticky_addr.size()); - sticky_addr[n] = addr; + // 32bit ページテーブルなら1行で1ロングワード。 + // 16進の場合との親和性のため、ダンプ値のところだけ反応。 + if (x >= 19 + 8) { + return (uint64)-1; + } + return memdump->GetAddr() + y * 4; } -// アドレス -/*static*/ std::array -WXMemdumpWindow::sticky_addr; +// 逆アセンブル画面のテキスト座標 (x, y) に対応するアドレスを返す。 +// minlen は命令の最小バイト数。 +// 対応するアドレスがない場合は (uint64)-1 を返す。 +uint64 +WXMemdumpWindow::GetAddrAtDisasm(int x, int y, int minlen) +{ + // 先頭から y 行目のアドレス + uint32 yaddr = memdump->GetLineOffset(y); + + // y 行目の命令のバイト数 + uint32 y1addr = memdump->GetLineOffset(y + 1); + uint32 bytes = y1addr - yaddr; + + // 空白部分なら除外 + x -= 19; + + if (x % (minlen * 2 + 1) == minlen * 2) { + return (uint64)-1; + } + x = (x / (minlen * 2 + 1)) * minlen; + + if (x >= bytes) { + return (uint64)-1; + } + + return memdump->GetAddr() + yaddr + x; +} + +// セレクション番号からフォーマット番号への変換 +Memdump::Format +WXMemdumpWindow::Selection2Format(int idx) +{ + assert(idx < fmts.size()); + return fmts[idx].first; +} + +// フォーマット番号からセレクション番号への変換 +int +WXMemdumpWindow::Format2Selection(Memdump::Format fmt_) +{ + for (int idx = 0, end = fmts.size(); idx < end; idx++) { + auto p = fmts[idx]; + if (fmt_ == p.first) { + return idx; + } + } + // ? + return 0; +} + +// フォーマット番号から文字列への変換 +wxString +WXMemdumpWindow::Format2String(Memdump::Format fmt_) +{ + for (int idx = 0, end = fmts.size(); idx < end; idx++) { + auto p = fmts[idx]; + if (fmt_ == p.first) { + return p.second; + } + } + // ? + return wxEmptyString; +} + + +// +// 編集ダイアログ +// + +wxBEGIN_EVENT_TABLE(MemEditDialog, inherited) + EVT_TEXT_ENTER(ID_EDIT, MemEditDialog::OnTextEnter) +wxEND_EVENT_TABLE() + +// コンストラクタ +MemEditDialog::MemEditDialog(wxWindow *parent, uint32 addr, uint32 oldval, + int len, const std::string& sizestr) + : inherited(parent, wxID_ANY, _("Edit memory")) +{ + auto topsizer = new wxBoxSizer(wxVERTICAL); + + auto gridsizer = new wxFlexGridSizer(2); + topsizer->Add(gridsizer, 0, wxEXPAND | wxALL, 3); + + gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Address:")), + 0, wxEXPAND); + + std::string addrstr = "$"; + addrstr += strhex(addr); + auto addrctrl = new wxStaticText(this, wxID_ANY, addrstr, + wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN); + gridsizer->Add(addrctrl, 0, wxEXPAND); + + gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Size:")), + 0, wxEXPAND); + gridsizer->Add(new wxStaticText(this, wxID_ANY, sizestr), + 0, wxEXPAND); + + gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Data:")), + 0, wxEXPAND); + + std::string oldstr = strhex(oldval, len * 2); + datactrl = new wxTextCtrl(this, ID_EDIT, oldstr, + wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); + gridsizer->Add(datactrl, 0, wxEXPAND); + + topsizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxNO_DEFAULT), + 0, wxEXPAND | wxALL, 3); + + SetSizer(topsizer); + wxSize sz = topsizer->Fit(this); + SetClientSize(sz); + SetMinClientSize(sz); +} + +// デストラクタ +MemEditDialog::~MemEditDialog() +{ +} + +// テキスト入力 +void +MemEditDialog::OnTextEnter(wxCommandEvent& event) +{ + EndModal(wxID_OK); +} + +// テキストコントロールの値を返す。 +// 変換できなければ (uint64)-1 を返す。 +uint64 +MemEditDialog::GetData() const +{ + unsigned long int ul; + char *end; + + // wxString だと地味にめんどい + wxString wtext = datactrl->GetValue(); + std::string text((const char *)wtext.mb_str()); + + errno = 0; + ul = strtoul(&text[0], &end, 16); + if (end == &text[0] || *end != '\0' || errno != 0) { + return (uint64)-1; + } + return (uint32)ul; +}