|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.11 root 7: //
8: // メモリダンプウィンドウ
9: //
10:
1.1 root 11: #include "wxdumpmonitor.h"
1.1.1.13 root 12: #include "mainapp.h"
13: #include "mainbus.h"
1.1 root 14:
15: enum {
1.1.1.11 root 16: ID_TEXT = IDGROUP_MEMDUMP,
1.1.1.13 root 17: ID_PREVLINE,
18: ID_PREVPAGE,
19: ID_NEXTLINE,
20: ID_NEXTPAGE,
21: ID_FORMAT,
22:
23: ID_EDIT,
1.1.1.11 root 24:
25: ID_local_end, // 最後に置く (チェック用)
1.1 root 26: };
1.1.1.12 root 27: static_assert(ID_local_end - 1 <= (int)IDGROUP_MEMDUMP_END, "ID exceeded");
1.1 root 28:
29: // イベントテーブル
30: wxBEGIN_EVENT_TABLE(WXMemdumpWindow, inherited)
31: EVT_TEXT_ENTER(ID_TEXT, WXMemdumpWindow::OnTextEnter)
1.1.1.13 root 32: EVT_COMMAND(ID_PREVLINE, NONO_EVT_BUTTON, WXMemdumpWindow::OnPrevLine)
33: EVT_COMMAND(ID_PREVPAGE, NONO_EVT_BUTTON, WXMemdumpWindow::OnPrevPage)
34: EVT_COMMAND(ID_NEXTLINE, NONO_EVT_BUTTON, WXMemdumpWindow::OnNextLine)
35: EVT_COMMAND(ID_NEXTPAGE, NONO_EVT_BUTTON, WXMemdumpWindow::OnNextPage)
36: EVT_CHOICE(ID_FORMAT, WXMemdumpWindow::OnFormat)
1.1 root 37: wxEND_EVENT_TABLE()
38:
39: // コンストラクタ
1.1.1.9 root 40: // (タイトルは UpdateAddr() でセットする)
1.1.1.13 root 41: WXMemdumpWindow::WXMemdumpWindow(wxWindow *parent, int monid_)
1.1.1.12 root 42: : inherited(parent, wxID_ANY, wxEmptyString,
43: DEFAULT_STYLE | wxRESIZE_BORDER)
1.1 root 44: {
1.1.1.13 root 45: monid = monid_;
46: if (IS_MONITOR_XPMEMDUMP(monid)) {
47: myidx = monid - ID_MONITOR_XPMEMDUMP0;
48: } else {
49: myidx = monid - ID_MONITOR_MEMDUMP0;
50: }
51:
52: // ↓ topsizer
53: // +--------------------------------------------+
54: // |上枠 → |
55: // |+-----+------+-----+----------+-----+------+|
56: // ||余白 | "<<" | "<" | アドレス | ">" | ">>" ||
57: // |+-----+------+-----+----------+-----+------+|
58: // +--------------------------------------------+
59: // | TextScreen |
60: // +--------------------------------------------+
1.1.1.11 root 61:
62: auto *topsizer = new wxBoxSizer(wxVERTICAL);
1.1.1.5 root 63:
1.1.1.9 root 64: // 上枠用の下敷きパネル
1.1.1.13 root 65: auto *ctrlpanel = new wxPanel(this);
1.1.1.11 root 66: topsizer->Add(ctrlpanel, 0, wxEXPAND);
1.1.1.9 root 67:
1.1.1.5 root 68: // 上枠用の横 sizer
69: ctrlbox = new wxBoxSizer(wxHORIZONTAL);
1.1 root 70:
71: // アドレスコントロール
1.1.1.13 root 72: addrctrl = new wxTextCtrl(ctrlpanel, ID_TEXT,
1.1 root 73: wxEmptyString, wxDefaultPosition, wxDefaultSize,
74: wxTE_PROCESS_ENTER);
1.1.1.13 root 75: buttons[0] = new WXButton(ctrlpanel, ID_PREVPAGE, wxDefaultSize, "<<");
76: buttons[1] = new WXButton(ctrlpanel, ID_PREVLINE, wxDefaultSize, "<");
77: buttons[2] = new WXButton(ctrlpanel, ID_NEXTLINE, wxDefaultSize, ">");
78: buttons[3] = new WXButton(ctrlpanel, ID_NEXTPAGE, wxDefaultSize, ">>");
79:
80: ctrlbox->AddSpacer(3);
81: ctrlbox->Add(buttons[0], 0, wxALIGN_CENTER);
82: ctrlbox->Add(buttons[1], 0, wxALIGN_CENTER);
83: ctrlbox->AddSpacer(3);
84: ctrlbox->Add(addrctrl, 0, wxALIGN_CENTER);
85: ctrlbox->AddSpacer(3);
86: ctrlbox->Add(buttons[2], 0, wxALIGN_CENTER);
87: ctrlbox->Add(buttons[3], 0, wxALIGN_CENTER);
88: ctrlbox->AddSpacer(3);
89:
90: // 表示形式 (機種によって選択肢と名前が異なる)
91: if (gMainApp.Has(VMCap::M68K)) {
92: fmts.emplace_back(Memdump::Byte, _("Byte"));
93: fmts.emplace_back(Memdump::Word, _("Word"));
94: fmts.emplace_back(Memdump::Long, _("Long Word"));
95: fmts.emplace_back(Memdump::M68030PageShort,
96: _("MMU Descriptor (Short Format)"));
97: fmts.emplace_back(Memdump::M68030Disasm, _("68030 Disassemble"));
98: } else {
99: fmts.emplace_back(Memdump::Byte, _("Byte"));
100: fmts.emplace_back(Memdump::Word, _("Half Word"));
101: fmts.emplace_back(Memdump::Long, _("Word"));
102: fmts.emplace_back(Memdump::M88200Page, _("88200 Descriptor"));
103: fmts.emplace_back(Memdump::M88100Disasm, _("88100 Disassemble"));
104: }
105: if (gMainApp.Has(VMCap::LUNA)) {
106: fmts.emplace_back(Memdump::HD64180Disasm, _("HD64180 Disassemble"));
107: }
108:
109: // fmts から名前だけの配列を作る
110: std::vector<wxString> fmt_choice;
111: for (auto p : fmts) {
112: fmt_choice.push_back(p.second);
113: }
114: fmtctrl = new wxChoice(ctrlpanel, ID_FORMAT,
115: wxDefaultPosition, wxDefaultSize,
116: fmt_choice.size(), &fmt_choice[0]);
117: ctrlbox->Add(fmtctrl, 0, wxALIGN_CENTER);
1.1 root 118:
1.1.1.9 root 119: // 上枠の sizer と下敷きを紐付ける
120: ctrlpanel->SetSizer(ctrlbox);
121: ctrlbox->SetSizeHints(ctrlpanel);
122:
1.1.1.13 root 123: screen = new WXMonitorPanel(this, gMonitorManager->Get(monid));
1.1.1.11 root 124: topsizer->Add(screen, 1, wxEXPAND);
1.1 root 125:
1.1.1.4 root 126: SetSizer(topsizer);
1.1 root 127:
1.1.1.11 root 128: // ウィンドウサイズを確定させる
129: fixed_height = ctrlbox->GetSize().y;
1.1.1.13 root 130: FontChanged();
1.1.1.11 root 131: DoSize();
132:
1.1.1.13 root 133: // パネルに来るマウスイベントをこっちに回す
134: screen->Connect(wxEVT_MOUSEWHEEL,
135: wxMouseEventHandler(WXMemdumpWindow::OnMouseWheel), NULL, this);
136: screen->Connect(wxEVT_LEFT_DCLICK,
137: wxMouseEventHandler(WXMemdumpWindow::OnDClick), NULL, this);
138:
139: // メモリダンプオブジェクトを取得
140: auto mon = screen->GetMonitor();
141: memdump = dynamic_cast<MemdumpMonitor *>(mon.obj);
142: assert(memdump);
143:
144: // 初期値をコントロールに設定
145: auto fmt = memdump->GetFormat();
146: fmtctrl->SetSelection(Format2Selection(fmt));
147: DoFormat(fmt);
1.1.1.9 root 148: UpdateAddr();
1.1 root 149: }
150:
151: // デストラクタ
152: WXMemdumpWindow::~WXMemdumpWindow()
153: {
154: }
155:
1.1.1.12 root 156: bool
157: WXMemdumpWindow::Layout()
158: {
159: // 縦リサイズ可能レイアウト
160: return LayoutTextVResize(screen, fixed_height);
161: }
162:
1.1.1.13 root 163: void
164: WXMemdumpWindow::FontChanged()
165: {
166: inherited::FontChanged();
167:
168: // 最小 24x24 にしてみる
169: wxSize sz(screen->GetFontWidth() * 2 + 4,
170: screen->GetFontHeight() + 4);
171: if (sz.x < 24)
172: sz.x = 24;
173: if (sz.y < 24)
174: sz.y = 24;
175: for (auto *btn : buttons) {
176: btn->SetClientSize(sz);
177: btn->SetMinClientSize(sz);
178: btn->FontChanged();
179: }
180: }
181:
1.1 root 182: // テキスト入力イベント
183: void
184: WXMemdumpWindow::OnTextEnter(wxCommandEvent& event)
185: {
186: // 入力文字列(16進文字列)を数値に変換
1.1.1.5 root 187: const wxString& wstr = event.GetString();
1.1.1.6 root 188: uint32 val = strtoul((const char *)wstr.mb_str(), NULL, 16);
1.1 root 189:
1.1.1.9 root 190: // アドレスを更新
1.1.1.13 root 191: memdump->SetAddr(val);
1.1.1.9 root 192: UpdateAddr();
1.1 root 193: }
194:
1.1.1.13 root 195: // "<"(前行) ボタン押下イベント
1.1 root 196: void
1.1.1.13 root 197: WXMemdumpWindow::OnPrevLine(wxCommandEvent& event)
1.1 root 198: {
1.1.1.13 root 199: // 1行戻る
200: memdump->Offset(memdump->GetLineOffset(-1));
1.1.1.9 root 201:
1.1.1.13 root 202: UpdateAddr();
203: }
204:
205: // "<<"(前ページ) ボタン押下イベント
206: void
207: WXMemdumpWindow::OnPrevPage(wxCommandEvent& event)
208: {
209: // 1ページ戻る
210: memdump->Offset(memdump->GetPageOffset(-1));
1.1.1.9 root 211:
212: UpdateAddr();
1.1 root 213: }
214:
1.1.1.13 root 215: // ">"(次行) ボタン押下イベント
1.1 root 216: void
1.1.1.13 root 217: WXMemdumpWindow::OnNextLine(wxCommandEvent& event)
1.1 root 218: {
1.1.1.13 root 219: // 1行進む
220: memdump->Offset(memdump->GetLineOffset(1));
221:
222: UpdateAddr();
223: }
1.1.1.9 root 224:
1.1.1.13 root 225: // ">>"(次ページ) ボタン押下イベント
226: void
227: WXMemdumpWindow::OnNextPage(wxCommandEvent& event)
228: {
229: // 1ページ進む
230: memdump->Offset(memdump->GetPageOffset(1));
1.1.1.9 root 231:
232: UpdateAddr();
1.1 root 233: }
234:
1.1.1.9 root 235: // アドレス欄の表示を更新する
236: void
237: WXMemdumpWindow::UpdateAddr()
238: {
1.1.1.13 root 239: int width;
240: std::string title;
241: if (IS_MONITOR_XPMEMDUMP(monid)) {
242: width = 5;
243: title = _("XP Memory Dump");
244: } else {
245: width = 8;
246: title = _("Main Memory Dump");
247: }
1.1.1.9 root 248:
1.1.1.14! root 249: uint32 addr = memdump->GetAddr().Addr();
1.1.1.13 root 250: std::string addrstr = strhex(addr, width);
1.1.1.9 root 251:
252: // EVT_TEXT を起こさず変更する
1.1.1.13 root 253: addrctrl->ChangeValue(addrstr);
1.1.1.9 root 254:
255: // カーソルを末尾に
256: addrctrl->SetInsertionPointEnd();
257:
258: // タイトルを変更
1.1.1.13 root 259: title += string_format(" %d: $", myidx);
260: title += addrstr;
261: SetTitle(title);
262:
263: // 即更新
264: Refresh();
1.1.1.9 root 265: }
266:
1.1.1.13 root 267: // 表示フォーマット変更イベント
268: void
269: WXMemdumpWindow::OnFormat(wxCommandEvent& event)
1.1.1.9 root 270: {
1.1.1.13 root 271: int idx = event.GetSelection();
272: DoFormat(Selection2Format(idx));
1.1 root 273: }
1.1.1.9 root 274:
1.1.1.13 root 275: // 表示フォーマット変更処理
1.1.1.9 root 276: void
1.1.1.13 root 277: WXMemdumpWindow::DoFormat(Memdump::Format fmt)
1.1.1.9 root 278: {
1.1.1.13 root 279: memdump->SetFormat(fmt);
280:
281: // SetFormat() によってアドレスが切り捨てられる場合があるので
282: // アドレス欄の表示ごと更新。
283: UpdateAddr();
284: }
285:
286: // マウスホイールイベント
287: void
288: WXMemdumpWindow::OnMouseWheel(wxMouseEvent& event)
289: {
290: int delta = event.GetWheelRotation();
291:
292: // 1回の変化で +/-120 が来る。
293: // たいていのアプリケーションで3行ずつの移動に換算することが多いようだが
294: // ここはメモリダンプなので 4行ずつにする。
295:
296: if (delta != 0) {
297: memdump->Offset(memdump->GetLineOffset(-delta / 30));
298: UpdateAddr();
299: }
300: }
301:
302: // ダブルクリックイベント
303: void
304: WXMemdumpWindow::OnDClick(wxMouseEvent& event)
305: {
306: event.Skip();
307:
308: auto fmt = memdump->GetFormat();
309:
310: // テキストの桁数、行数にする
311: wxPoint tpos = screen->GetTextPosition(event.GetPosition());
312: int x = tpos.x;
313: int y = tpos.y;
314:
315: // ウィンドウサイズを変えた時に出来る下の空き地にはヒットさせない
316: if (y >= screen->GetRow()) {
317: return;
318: }
319:
320: // アドレス部分は常に除外でいいか
321: if (x < 19) {
322: return;
323: }
324:
325: // 現在のフォーマットでのクリック位置のアドレスを求める
326: int len = 0;
327: uint64 addr = -1;
328: switch (fmt) {
329: case Memdump::Byte:
330: len = 1;
331: addr = GetAddrAtHexdump(x, y, len);
332: break;
333: case Memdump::Word:
334: len = 2;
335: addr = GetAddrAtHexdump(x, y, len);
336: break;
337: case Memdump::Long:
338: len = 4;
339: addr = GetAddrAtHexdump(x, y, len);
340: break;
341: case Memdump::M68030PageShort:
342: case Memdump::M88200Page:
343: len = 4;
344: addr = GetAddrAtPageShort(x, y);
345: break;
346:
347: case Memdump::M68030PageLong:
348: PANIC("not supported");
349: break;
350:
351: case Memdump::M68030Disasm:
352: len = 2;
353: addr = GetAddrAtDisasm(x, y, len);
354: break;
355:
356: case Memdump::M88100Disasm:
357: len = 4;
358: addr = GetAddrAtDisasm(x, y, len);
359: break;
360:
361: case Memdump::HD64180Disasm:
362: len = 1;
363: addr = GetAddrAtDisasm(x, y, len);
364: break;
365:
366: default:
367: break;
368: }
369: if ((int64)addr < 0) {
370: return;
371: }
372: addr &= ~(len - 1);
373:
374: // このアドレスが編集可能 (≒メモリ) か?
375: if (memdump->CanPoke(addr) == false) {
376: ::wxMessageBox(_("Can not edit here"), _("Memory Edit"),
377: wxICON_EXCLAMATION | wxOK | wxOK_DEFAULT, this);
378: return;
379: }
380:
381: // 出来ればここをハイライトとかしたい
382:
383: // 現在値を(再)取得。
384: uint32 val = memdump->Peek(addr, len);
385:
386: // sizestr は表示形式ではなく単なるサイズ。(DescShort なら Long)
387: std::string sizestr;
388: switch (len) {
389: case 1:
390: sizestr = Format2String(Memdump::Byte);
391: break;
392: case 2:
393: sizestr = Format2String(Memdump::Word);
394: break;
395: case 4:
396: sizestr = Format2String(Memdump::Long);
397: break;
398: default:
399: PANIC("corrupted len=%d", len);
400: }
401:
402: // 編集
403: auto dlg = new MemEditDialog(this, addr, val, len, sizestr);
404: if (dlg->ShowModal() == wxID_OK) {
405: uint32 data = dlg->GetData();
406:
407: // 更新
1.1.1.14! root 408: if (memdump->Poke(addr, data, len) == false) {
1.1.1.13 root 409: ::wxMessageBox(_("Could not edit"), _("Memory Edit"),
410: wxICON_EXCLAMATION | wxOK | wxOK_DEFAULT, this);
411: }
412: }
413: }
414:
415: // 16進ダンプ画面のテキスト座標 (x, y) に対応するアドレスを返す。
416: // 対応するアドレスがない場合は (uint64)-1 を返す。
417: uint64
418: WXMemdumpWindow::GetAddrAtHexdump(int x, int y, int len)
419: {
420: if (x < 68) {
421: // 16進ダンプ部分
422:
423: // 空白部分なら除外
424: x -= 19;
425: if (x % (len * 2 + 1) == len * 2) {
426: return (uint64)-1;
427: }
428: x = (x / (len * 2 + 1)) * len;
429: } else {
430: // 文字ダンプ部分
431: x -= 68;
432: }
433:
434: if (x > 15) {
435: return (uint64)-1;
436: }
437:
1.1.1.14! root 438: return memdump->GetAddr().Addr() + (y * memdump->GetStride()) + x;
1.1.1.9 root 439: }
440:
1.1.1.13 root 441: // 32bit ページテーブル画面のテキスト座標 (x, y) に対応するアドレスを返す。
442: // 対応するアドレスがない場合は (uint64)-1 を返す。
443: uint64
444: WXMemdumpWindow::GetAddrAtPageShort(int x, int y)
445: {
446: // 32bit ページテーブルなら1行で1ロングワード。
447: // 16進の場合との親和性のため、ダンプ値のところだけ反応。
448: if (x >= 19 + 8) {
449: return (uint64)-1;
450: }
1.1.1.14! root 451: return memdump->GetAddr().Addr() + y * 4;
1.1.1.13 root 452: }
453:
454: // 逆アセンブル画面のテキスト座標 (x, y) に対応するアドレスを返す。
455: // minlen は命令の最小バイト数。
456: // 対応するアドレスがない場合は (uint64)-1 を返す。
457: uint64
458: WXMemdumpWindow::GetAddrAtDisasm(int x, int y, int minlen)
459: {
460: // 先頭から y 行目のアドレス
461: uint32 yaddr = memdump->GetLineOffset(y);
462:
463: // y 行目の命令のバイト数
464: uint32 y1addr = memdump->GetLineOffset(y + 1);
465: uint32 bytes = y1addr - yaddr;
466:
467: // 空白部分なら除外
468: x -= 19;
469:
470: if (x % (minlen * 2 + 1) == minlen * 2) {
471: return (uint64)-1;
472: }
473: x = (x / (minlen * 2 + 1)) * minlen;
474:
475: if (x >= bytes) {
476: return (uint64)-1;
477: }
478:
1.1.1.14! root 479: return memdump->GetAddr().Addr() + yaddr + x;
1.1.1.13 root 480: }
481:
482: // セレクション番号からフォーマット番号への変換
483: Memdump::Format
484: WXMemdumpWindow::Selection2Format(int idx)
485: {
486: assert(idx < fmts.size());
487: return fmts[idx].first;
488: }
489:
490: // フォーマット番号からセレクション番号への変換
491: int
492: WXMemdumpWindow::Format2Selection(Memdump::Format fmt_)
493: {
494: for (int idx = 0, end = fmts.size(); idx < end; idx++) {
495: auto p = fmts[idx];
496: if (fmt_ == p.first) {
497: return idx;
498: }
499: }
500: // ?
501: return 0;
502: }
503:
504: // フォーマット番号から文字列への変換
505: wxString
506: WXMemdumpWindow::Format2String(Memdump::Format fmt_)
507: {
508: for (int idx = 0, end = fmts.size(); idx < end; idx++) {
509: auto p = fmts[idx];
510: if (fmt_ == p.first) {
511: return p.second;
512: }
513: }
514: // ?
515: return wxEmptyString;
516: }
517:
518:
519: //
520: // 編集ダイアログ
521: //
522:
523: wxBEGIN_EVENT_TABLE(MemEditDialog, inherited)
524: EVT_TEXT_ENTER(ID_EDIT, MemEditDialog::OnTextEnter)
525: wxEND_EVENT_TABLE()
526:
527: // コンストラクタ
528: MemEditDialog::MemEditDialog(wxWindow *parent, uint32 addr, uint32 oldval,
529: int len, const std::string& sizestr)
530: : inherited(parent, wxID_ANY, _("Edit memory"))
531: {
532: auto topsizer = new wxBoxSizer(wxVERTICAL);
533:
534: auto gridsizer = new wxFlexGridSizer(2);
535: topsizer->Add(gridsizer, 0, wxEXPAND | wxALL, 3);
536:
537: gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Address:")),
538: 0, wxEXPAND);
539:
540: std::string addrstr = "$";
541: addrstr += strhex(addr);
542: auto addrctrl = new wxStaticText(this, wxID_ANY, addrstr,
543: wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN);
544: gridsizer->Add(addrctrl, 0, wxEXPAND);
545:
546: gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Size:")),
547: 0, wxEXPAND);
548: gridsizer->Add(new wxStaticText(this, wxID_ANY, sizestr),
549: 0, wxEXPAND);
550:
551: gridsizer->Add(new wxStaticText(this, wxID_ANY, _("Data:")),
552: 0, wxEXPAND);
553:
554: std::string oldstr = strhex(oldval, len * 2);
555: datactrl = new wxTextCtrl(this, ID_EDIT, oldstr,
556: wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
557: gridsizer->Add(datactrl, 0, wxEXPAND);
558:
559: topsizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxNO_DEFAULT),
560: 0, wxEXPAND | wxALL, 3);
561:
562: SetSizer(topsizer);
563: wxSize sz = topsizer->Fit(this);
564: SetClientSize(sz);
565: SetMinClientSize(sz);
566: }
567:
568: // デストラクタ
569: MemEditDialog::~MemEditDialog()
570: {
571: }
572:
573: // テキスト入力
574: void
575: MemEditDialog::OnTextEnter(wxCommandEvent& event)
576: {
577: EndModal(wxID_OK);
578: }
579:
580: // テキストコントロールの値を返す。
581: // 変換できなければ (uint64)-1 を返す。
582: uint64
583: MemEditDialog::GetData() const
584: {
585: unsigned long int ul;
586: char *end;
587:
588: // wxString だと地味にめんどい
589: wxString wtext = datactrl->GetValue();
590: std::string text((const char *)wtext.mb_str());
591:
592: errno = 0;
593: ul = strtoul(&text[0], &end, 16);
594: if (end == &text[0] || *end != '\0' || errno != 0) {
595: return (uint64)-1;
596: }
597: return (uint32)ul;
598: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.