Fix scaling-related crashes
[lsnes.git] / src / platform / wxwidgets / tracelogger.cpp
blobfd4af546ce5305ed97707b0fe6c77eea12d482a7
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/statline.h>
7 #include "platform/wxwidgets/platform.hpp"
8 #include "platform/wxwidgets/textrender.hpp"
9 #include "platform/wxwidgets/scrollbar.hpp"
10 #include "platform/wxwidgets/loadsave.hpp"
11 #include "core/command.hpp"
12 #include "core/debug.hpp"
13 #include "core/instance.hpp"
14 #include "core/mainloop.hpp"
15 #include "core/memorymanip.hpp"
16 #include "core/project.hpp"
17 #include "core/ui-services.hpp"
18 #include "interface/disassembler.hpp"
19 #include "library/minmax.hpp"
20 #include "library/hex.hpp"
21 #include "library/memoryspace.hpp"
22 #include "library/serialization.hpp"
23 #include <wx/frame.h>
24 #include <wx/clipbrd.h>
25 #include <wx/msgdlg.h>
26 #include <wx/menu.h>
27 #include <wx/button.h>
28 #include <wx/checkbox.h>
29 #include <wx/listbox.h>
30 #include <wx/stattext.h>
31 #include <wx/combobox.h>
32 #include <wx/textctrl.h>
33 #include <wx/spinctrl.h>
34 #include <wx/statusbr.h>
35 #include <wx/dataobj.h>
36 #include <wx/sizer.h>
37 #include <boost/regex.hpp>
38 #include <set>
40 namespace
42 enum
44 wxID_FIND_NEXT = wxID_HIGHEST + 1,
45 wxID_FIND_PREV,
46 wxID_GOTO,
47 wxID_DISASM,
48 wxID_DISASM_MORE,
49 wxID_SINGLESTEP,
50 wxID_BREAKPOINTS,
51 wxID_CONTINUE,
52 wxID_FRAMEADVANCE,
53 wxID_CLEAR,
56 int prompt_for_save(wxWindow* parent, const std::string& what)
58 CHECK_UI_THREAD;
59 wxMessageDialog* d = new wxMessageDialog(parent, towxstring(what + " has unsaved changes, "
60 "save before closing?"), towxstring("Save on exit?"), wxCENTER | wxYES_NO | wxCANCEL |
61 wxYES_DEFAULT);
62 d->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
63 int r = d->ShowModal();
64 d->Destroy();
65 if(r == wxID_YES) return 1;
66 if(r == wxID_NO) return 0;
67 if(r == wxID_CANCEL) return -1;
68 return -1;
71 class dialog_find : public wxDialog
73 public:
74 dialog_find(wxWindow* parent);
75 std::string get_pattern();
76 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
77 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
78 private:
79 wxTextCtrl* text;
80 wxComboBox* type;
81 wxButton* ok;
82 wxButton* cancel;
85 dialog_find::dialog_find(wxWindow* parent)
86 : wxDialog(parent, wxID_ANY, wxT("Find"))
88 CHECK_UI_THREAD;
89 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
90 SetSizer(top_s);
91 wxBoxSizer* t_s = new wxBoxSizer(wxHORIZONTAL);
92 t_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
93 0, NULL, wxCB_READONLY), 1, wxGROW);
94 type->Append(towxstring("Literal"));
95 type->Append(towxstring("Wildcards"));
96 type->Append(towxstring("Regexp"));
97 type->SetSelection(0);
98 t_s->Add(text = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(350, -1),
99 wxTE_PROCESS_ENTER), 0, wxGROW);
100 top_s->Add(t_s, 1, wxGROW);
101 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
102 pbutton_s->AddStretchSpacer();
103 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
104 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
105 text->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
106 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
107 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_cancel), NULL,
108 this);
109 top_s->Add(pbutton_s, 0, wxGROW);
110 top_s->SetSizeHints(this);
111 Fit();
114 std::string dialog_find::get_pattern()
116 CHECK_UI_THREAD;
117 if(tostdstring(text->GetValue()) == "")
118 return "";
119 if(type->GetSelection() == 2)
120 return "R" + tostdstring(text->GetValue());
121 else if(type->GetSelection() == 1)
122 return "W" + tostdstring(text->GetValue());
123 else
124 return "F" + tostdstring(text->GetValue());
127 class dialog_disassemble : public wxDialog
129 public:
130 dialog_disassemble(wxWindow* parent, emulator_instance& _inst);
131 dialog_disassemble(wxWindow* parent, emulator_instance& _inst, uint64_t dflt_base,
132 const std::string& dflt_lang);
133 std::string get_disassembler();
134 uint64_t get_address();
135 uint64_t get_count();
136 void on_change(wxCommandEvent& e);
137 void on_ok(wxCommandEvent& e);
138 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
139 private:
140 void init(bool spec, uint64_t dflt_base, std::string dflt_lang);
141 emulator_instance& inst;
142 wxComboBox* type;
143 wxComboBox* endian;
144 wxComboBox* vma;
145 wxTextCtrl* address;
146 wxSpinCtrl* count;
147 wxButton* ok;
148 wxButton* cancel;
149 bool has_default;
150 unsigned code_types;
151 static std::string old_dflt_lang;
152 static uint64_t old_dflt_base;
155 std::string dialog_disassemble::old_dflt_lang;
156 uint64_t dialog_disassemble::old_dflt_base;
158 dialog_disassemble::dialog_disassemble(wxWindow* parent, emulator_instance& _inst)
159 : wxDialog(parent, wxID_ANY, wxT("Disassemble region")), inst(_inst)
161 CHECK_UI_THREAD;
162 init(false, 0, "");
165 dialog_disassemble::dialog_disassemble(wxWindow* parent, emulator_instance& _inst, uint64_t dflt_base,
166 const std::string& dflt_lang)
167 : wxDialog(parent, wxID_ANY, wxT("Disassemble region")), inst(_inst)
169 CHECK_UI_THREAD;
170 init(true, dflt_base, dflt_lang);
173 void dialog_disassemble::init(bool spec, uint64_t dflt_base, std::string dflt_lang)
175 CHECK_UI_THREAD;
176 std::map<std::string, std::pair<uint64_t, uint64_t>> regions;
177 std::set<std::string> disasms;
178 inst.iqueue->run([&regions, &disasms]() {
179 for(auto i : CORE().memory->get_regions())
180 regions[i->name] = std::make_pair(i->base, i->size);
181 disasms = disassembler::list();
184 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
185 SetSizer(top_s);
187 wxBoxSizer* type_s = new wxBoxSizer(wxHORIZONTAL);
188 type_s->Add(new wxStaticText(this, wxID_ANY, wxT("Language:")), 0, wxGROW);
189 type_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
190 0, NULL, wxCB_READONLY), 0, wxGROW);
191 for(auto& i : disasms)
192 type->Append(towxstring(i));
193 code_types = type->GetCount();
194 type->Append(towxstring("Data (signed byte)"));
195 type->Append(towxstring("Data (unsigned byte)"));
196 type->Append(towxstring("Data (hex byte)"));
197 type->Append(towxstring("Data (signed word)"));
198 type->Append(towxstring("Data (unsigned word)"));
199 type->Append(towxstring("Data (hex word)"));
200 type->Append(towxstring("Data (signed onehalfword)"));
201 type->Append(towxstring("Data (unsigned onehalfword)"));
202 type->Append(towxstring("Data (hex onehalfword)"));
203 type->Append(towxstring("Data (signed doubleword)"));
204 type->Append(towxstring("Data (unsigned doubleword)"));
205 type->Append(towxstring("Data (hex doubleword)"));
206 type->Append(towxstring("Data (signed quadword)"));
207 type->Append(towxstring("Data (unsigned quadword)"));
208 type->Append(towxstring("Data (hex quadword)"));
209 type->Append(towxstring("Data (float)"));
210 type->Append(towxstring("Data (double)"));
211 type->SetSelection(0);
212 type->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
213 NULL, this);
214 top_s->Add(type_s, 0, wxGROW);
216 wxBoxSizer* endian_s = new wxBoxSizer(wxHORIZONTAL);
217 endian_s->Add(new wxStaticText(this, wxID_ANY, wxT("Endian:")), 0, wxGROW);
218 endian_s->Add(endian = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
219 0, NULL, wxCB_READONLY), 0, wxGROW);
220 endian->Append(towxstring("(Memory area default)"));
221 endian->Append(towxstring("Little-endian"));
222 endian->Append(towxstring("Host-endian"));
223 endian->Append(towxstring("Big-endian"));
224 endian->SetSelection(0);
225 endian->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
226 NULL, this);
227 top_s->Add(endian_s, 0, wxGROW);
229 wxBoxSizer* vma_s = new wxBoxSizer(wxHORIZONTAL);
230 vma_s->Add(new wxStaticText(this, wxID_ANY, wxT("Area:")), 0, wxGROW);
231 vma_s->Add(vma = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
232 0, NULL, wxCB_READONLY), 0, wxGROW);
233 vma->Append(towxstring("(Any)"));
234 for(auto& i : regions)
235 vma->Append(towxstring(i.first));
236 vma->SetSelection(0);
237 vma->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
238 NULL, this);
239 top_s->Add(vma_s, 0, wxGROW);
241 wxBoxSizer* addr_s = new wxBoxSizer(wxHORIZONTAL);
242 addr_s->Add(new wxStaticText(this, wxID_ANY, wxT("Address:")), 0, wxGROW);
243 addr_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)),
244 0, wxGROW);
245 address->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(dialog_disassemble::on_change),
246 NULL, this);
247 top_s->Add(addr_s, 0, wxGROW);
249 wxBoxSizer* cnt_s = new wxBoxSizer(wxHORIZONTAL);
250 cnt_s->Add(new wxStaticText(this, wxID_ANY, wxT("Count:")), 0, wxGROW);
251 cnt_s->Add(count = new wxSpinCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
252 wxSP_ARROW_KEYS, 1, 1000000000, 10), 0, wxGROW);
253 count->Connect(wxEVT_SPINCTRL, wxCommandEventHandler(dialog_disassemble::on_change), NULL,
254 this);
255 top_s->Add(cnt_s, 0, wxGROW);
257 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
258 pbutton_s->AddStretchSpacer();
259 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
260 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
261 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_ok), NULL,
262 this);
263 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_cancel),
264 NULL, this);
265 top_s->Add(pbutton_s, 0, wxGROW);
266 top_s->SetSizeHints(this);
267 Fit();
269 has_default = spec;
270 if(!spec) {
271 dflt_lang = old_dflt_lang;
272 dflt_base = old_dflt_base;
274 //Set default language.
275 if(regex_match("\\$data:.*", dflt_lang)) {
276 switch(dflt_lang[6]) {
277 case 'b': type->SetSelection(code_types + 0); break;
278 case 'B': type->SetSelection(code_types + 1); break;
279 case 'c': type->SetSelection(code_types + 2); break;
280 case 'w': type->SetSelection(code_types + 3); break;
281 case 'W': type->SetSelection(code_types + 4); break;
282 case 'C': type->SetSelection(code_types + 5); break;
283 case 'h': type->SetSelection(code_types + 6); break;
284 case 'H': type->SetSelection(code_types + 7); break;
285 case 'i': type->SetSelection(code_types + 8); break;
286 case 'd': type->SetSelection(code_types + 9); break;
287 case 'D': type->SetSelection(code_types + 10); break;
288 case 'I': type->SetSelection(code_types + 11); break;
289 case 'q': type->SetSelection(code_types + 12); break;
290 case 'Q': type->SetSelection(code_types + 13); break;
291 case 'r': type->SetSelection(code_types + 14); break;
292 case 'f': type->SetSelection(code_types + 15); break;
293 case 'F': type->SetSelection(code_types + 16); break;
295 switch(dflt_lang[7]) {
296 case 'l': endian->SetSelection(1); break;
297 case 'h': endian->SetSelection(2); break;
298 case 'b': endian->SetSelection(3); break;
300 } else {
301 unsigned j = 0;
302 //Set default disasm.
303 for(auto& i : disasms) {
304 if(i == dflt_lang)
305 break;
306 j++;
308 if(j < disasms.size())
309 type->SetSelection(j);
311 //Set default address.
312 int k = 0;
313 for(auto& i : regions) {
314 if(dflt_base >= i.second.first && dflt_base < i.second.first + i.second.second) {
315 vma->SetSelection(k + 1);
316 dflt_base -= i.second.first;
317 break;
319 k++;
321 address->SetValue(towxstring((stringfmt() << std::hex << dflt_base).str()));
323 wxCommandEvent e;
324 on_change(e);
327 void dialog_disassemble::on_ok(wxCommandEvent& e)
329 CHECK_UI_THREAD;
330 EndModal(wxID_OK);
333 std::string dialog_disassemble::get_disassembler()
335 CHECK_UI_THREAD;
336 if(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() < (ssize_t)type->GetCount()) {
337 int _endian = endian->GetSelection();
338 int dtsel = type->GetSelection() - code_types;
339 std::string _vma = tostdstring(vma->GetStringSelection());
340 if(_endian <= 0 || _endian > 3) {
341 _endian = 1;
342 inst.iqueue->run([&_endian, _vma]() {
343 for(auto i : CORE().memory->get_regions()) {
344 if(i->name == _vma) {
345 _endian = i->endian + 2;
350 if(dtsel < 0) dtsel = 0;
351 if(dtsel > 16) dtsel = 16;
352 static const char* typechars = "bBcwWChHidDIqQrfF";
353 static const char* endianchars = " lhb";
354 std::string res = std::string("$data:") + std::string(1, typechars[dtsel]) +
355 std::string(1, endianchars[_endian]);
356 if(!has_default)
357 old_dflt_lang = res;
358 return res;
359 } else {
360 std::string res = tostdstring(type->GetStringSelection());
361 if(!has_default)
362 old_dflt_lang = res;
363 return res;
367 uint64_t dialog_disassemble::get_address()
369 CHECK_UI_THREAD;
370 uint64_t base = 0;
371 if(vma->GetSelection() && vma->GetSelection() != wxNOT_FOUND) {
372 std::string _vma = tostdstring(vma->GetStringSelection());
373 inst.iqueue->run([&base, _vma]() {
374 for(auto i : CORE().memory->get_regions()) {
375 if(i->name == _vma) {
376 base = i->base;
381 uint64_t off = hex::from<uint64_t>(tostdstring(address->GetValue()));
382 uint64_t res = base + off;
383 if(!has_default)
384 old_dflt_base = res;
385 return res;
388 uint64_t dialog_disassemble::get_count()
390 CHECK_UI_THREAD;
391 return count->GetValue();
394 void dialog_disassemble::on_change(wxCommandEvent& e)
396 CHECK_UI_THREAD;
397 bool is_ok = true;
398 try {
399 hex::from<uint64_t>(tostdstring(address->GetValue()));
400 } catch(std::exception& e) {
401 is_ok = false;
403 is_ok = is_ok && (type->GetSelection() != wxNOT_FOUND);
404 is_ok = is_ok && (vma->GetSelection() != wxNOT_FOUND);
405 endian->Enable(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() <
406 (ssize_t)type->GetCount());
407 is_ok = is_ok && (!endian->IsEnabled() || endian->GetSelection() != wxNOT_FOUND);
408 //If VMA is global, ensure there is valid endian.
409 is_ok = is_ok && (vma->GetSelection() != 0 || !endian->IsEnabled() || endian->GetSelection() != 0);
410 ok->Enable(is_ok);
413 class wxwin_tracelog;
415 class dialog_breakpoint_add : public wxDialog
417 public:
418 dialog_breakpoint_add(wxWindow* parent, std::list<memory_space::region*> regions);
419 std::pair<uint64_t, debug_context::etype> get_result();
420 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
421 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
422 void on_address_change(wxCommandEvent& e);
423 private:
424 std::list<memory_space::region*> regions;
425 wxComboBox* vmasel;
426 wxTextCtrl* address;
427 wxComboBox* typesel;
428 wxButton* ok;
429 wxButton* cancel;
432 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow* parent, std::list<memory_space::region*> _regions)
433 : wxDialog(parent, wxID_ANY, wxT("Add breakpoint"))
435 CHECK_UI_THREAD;
436 regions = _regions;
437 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
438 SetSizer(top_s);
440 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Memory region:")), 0, wxGROW);
441 top_s->Add(vmasel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
442 0, NULL, wxCB_READONLY), 1, wxGROW);
443 vmasel->Append(towxstring(""));
444 for(auto i : regions)
445 vmasel->Append(towxstring(i->name));
446 vmasel->SetSelection(0);
448 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Offset (hexadecimal):")), 0, wxGROW);
449 top_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(350, -1)), 0,
450 wxGROW);
452 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Breakpoint type:")), 0, wxGROW);
453 top_s->Add(typesel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
454 0, NULL, wxCB_READONLY), 1, wxGROW);
455 typesel->Append(towxstring("Read"));
456 typesel->Append(towxstring("Write"));
457 typesel->Append(towxstring("Execute"));
458 typesel->SetSelection(0);
460 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
461 pbutton_s->AddStretchSpacer();
462 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
463 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
464 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_ok), NULL,
465 this);
466 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_cancel),
467 NULL, this);
468 top_s->Add(pbutton_s, 0, wxGROW);
469 top_s->SetSizeHints(this);
470 Fit();
473 void dialog_breakpoint_add::on_address_change(wxCommandEvent& e)
475 CHECK_UI_THREAD;
476 try {
477 hex::from<uint64_t>(tostdstring(address->GetValue()));
478 ok->Enable(true);
479 } catch(...) {
480 ok->Enable(false);
484 std::pair<uint64_t, debug_context::etype> dialog_breakpoint_add::get_result()
486 CHECK_UI_THREAD;
487 std::string vmaname = tostdstring(vmasel->GetStringSelection());
488 std::string addrtext = tostdstring(address->GetValue());
489 uint64_t base = 0;
490 if(vmaname != "") {
491 for(auto i : regions)
492 if(i->name == vmaname)
493 base = i->base;
495 uint64_t addr;
496 try {
497 addr = base + hex::from<uint64_t>(addrtext);
498 } catch(std::exception& e) {
499 addr = base;
501 debug_context::etype dtype = debug_context::DEBUG_EXEC;
502 if(typesel->GetSelection() == 0)
503 dtype = debug_context::DEBUG_READ;
504 if(typesel->GetSelection() == 1)
505 dtype = debug_context::DEBUG_WRITE;
506 if(typesel->GetSelection() == 2)
507 dtype = debug_context::DEBUG_EXEC;
508 return std::make_pair(addr, dtype);
512 class dialog_breakpoints : public wxDialog
514 public:
515 dialog_breakpoints(wxwin_tracelog* parent, emulator_instance& _inst);
516 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
517 void on_add(wxCommandEvent& e);
518 void on_delete(wxCommandEvent& e);
519 void on_selchange(wxCommandEvent& e);
520 private:
521 std::string format_line(std::pair<uint64_t, debug_context::etype> entry);
522 size_t get_insert_pos(std::pair<uint64_t, debug_context::etype> entry);
523 void populate_breakpoints();
524 std::list<memory_space::region*> regions;
525 emulator_instance& inst;
526 wxButton* ok;
527 wxButton* addb;
528 wxButton* delb;
529 wxListBox* brklist;
530 wxwin_tracelog* pwin;
531 std::vector<std::pair<uint64_t, debug_context::etype>> listsyms;
534 class wxwin_tracelog : public wxFrame, public debug_context::callback_base
536 public:
537 wxwin_tracelog(wxWindow* parent, emulator_instance& _inst, int _cpuid, const std::string& cpuname);
538 ~wxwin_tracelog();
539 bool ShouldPreventAppExit() const { return false; }
540 scroll_bar* get_scroll() { return scroll; }
541 void on_wclose(wxCloseEvent& e);
542 void on_enabled(wxCommandEvent& e);
543 void on_menu(wxCommandEvent& e);
544 void process_lines();
545 uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
546 std::set<std::pair<uint64_t, debug_context::etype>> get_breakpoints();
547 void add_breakpoint(uint64_t addr, debug_context::etype dtype);
548 void remove_breakpoint(uint64_t addr, debug_context::etype dtype);
549 private:
550 class _panel : public text_framebuffer_panel
552 public:
553 _panel(wxwin_tracelog* parent, emulator_instance& _inst);
554 void on_size(wxSizeEvent& e);
555 void on_mouse(wxMouseEvent& e);
556 wxSize DoGetBestSize() const;
557 uint64_t pos;
558 std::vector<std::string> rows;
559 void on_popup_menu(wxCommandEvent& e);
560 bool scroll_to_end_on_repaint;
561 protected:
562 void prepare_paint();
563 private:
564 emulator_instance& inst;
565 uint64_t pressed_row;
566 uint64_t current_row;
567 bool holding;
568 wxwin_tracelog* p;
570 emulator_instance& inst;
571 bool do_exit_save();
572 void scroll_pane(uint64_t line);
573 int cpuid;
574 volatile bool trace_active;
575 void callback(const debug_context::params& params);
576 void killed(uint64_t addr, debug_context::etype type);
577 void do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type);
578 void kill_debug_hooks(bool kill_hard = false);
579 scroll_bar* scroll;
580 _panel* panel;
581 bool broken;
582 bool broken2;
583 wxCheckBox* enabled;
584 threads::lock buffer_mutex;
585 std::list<std::string> lines_waiting;
586 bool unprocessed_lines;
587 bool closing;
588 bool find_active;
589 uint64_t find_line;
590 std::string find_string;
591 bool dirty;
592 bool singlestepping;
593 std::map<std::pair<uint64_t, debug_context::etype>, bool> rwx_breakpoints;
594 wxMenuItem* m_singlestep;
597 wxwin_tracelog::~wxwin_tracelog()
601 void wxwin_tracelog::on_wclose(wxCloseEvent& e)
603 CHECK_UI_THREAD;
604 if(dirty && !wxwidgets_exiting) {
605 int r = prompt_for_save(this, "Trace log");
606 if(r < 0 || (r > 0 && !do_exit_save()))
607 return;
609 if(trace_active)
610 inst.iqueue->run([this]() { kill_debug_hooks(); });
611 trace_active = false;
612 if(!closing)
613 Destroy();
614 closing = true;
617 void wxwin_tracelog::kill_debug_hooks(bool kill_hard)
619 CORE().dbg->remove_callback(cpuid, debug_context::DEBUG_FRAME, *this);
620 if(!kill_hard)
621 CORE().dbg->remove_callback(cpuid, debug_context::DEBUG_TRACE, *this);
622 threads::alock h(buffer_mutex);
623 for(auto& i : rwx_breakpoints) {
624 if(!i.second)
625 continue;
626 if(!kill_hard)
627 CORE().dbg->remove_callback(i.first.first, i.first.second, *this);
628 i.second = false;
630 trace_active = false;
631 convert_break_to_pause();
634 wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent, emulator_instance& _inst)
635 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL), inst(_inst)
637 CHECK_UI_THREAD;
638 p = parent;
639 pos = 0;
640 pressed_row = 0;
641 current_row = 0;
642 holding = false;
643 scroll_to_end_on_repaint = false;
644 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
645 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
646 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
647 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
648 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
651 void wxwin_tracelog::_panel::on_size(wxSizeEvent& e)
653 wxSize newsize = e.GetSize();
654 auto tcell = get_cell();
655 size_t lines = newsize.y / tcell.second;
656 size_t linelen = newsize.x / tcell.first;
657 if(lines < 1) lines = 1;
658 if(linelen < 1) linelen = 1;
659 set_size(linelen, lines);
660 p->get_scroll()->set_page_size(lines);
661 request_paint();
662 e.Skip();
665 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent& e)
667 CHECK_UI_THREAD;
668 uint64_t local_line = pos + e.GetY() / get_cell().second;
669 if(e.RightDown()) {
670 if(local_line < rows.size()) {
671 holding = true;
672 pressed_row = local_line;
674 }else if(e.RightUp()) {
675 holding = false;
676 wxMenu menu;
677 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
678 wxCommandEventHandler(wxwin_tracelog::_panel::on_popup_menu), NULL, this);
679 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
680 menu.Append(wxID_SAVE, wxT("Save to file"));
681 menu.AppendSeparator();
682 menu.Append(wxID_DELETE, wxT("Delete"));
683 PopupMenu(&menu);
684 } else {
685 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
686 request_paint();
688 unsigned speed = 1;
689 if(e.ShiftDown())
690 speed = 10;
691 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
694 wxSize wxwin_tracelog::_panel::DoGetBestSize() const
696 return wxSize(120 * 8, 25 * 16);
699 void wxwin_tracelog::_panel::prepare_paint()
701 p->get_scroll()->set_range(rows.size());
702 if(scroll_to_end_on_repaint) {
703 scroll_to_end_on_repaint = false;
704 p->get_scroll()->set_position(rows.size());
705 pos = p->get_scroll()->get_position();
707 uint64_t m = min(pressed_row, current_row);
708 uint64_t M = max(pressed_row, current_row);
709 auto s = get_characters();
710 uint64_t fline = p->get_find_line();
711 for(uint64_t i = pos; i < pos + s.second && i < rows.size(); i++) {
712 bool selected = holding && (i >= m) && (i <= M);
713 bool isfl = (i == fline);
714 uint32_t fg = selected ? 0x0000FF : 0x000000;
715 uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
716 write(rows[i], s.first, 0, i - pos, fg, bg);
718 for(uint64_t i = rows.size(); i < pos + s.second; i++)
719 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
722 void wxwin_tracelog::process_lines()
724 CHECK_UI_THREAD;
725 threads::alock h(this->buffer_mutex);
726 size_t osize = panel->rows.size();
727 if(broken) {
728 panel->rows.push_back(std::string(120, '-'));
729 broken = false;
731 for(auto& i : lines_waiting)
732 panel->rows.push_back(i);
733 lines_waiting.clear();
734 unprocessed_lines = false;
735 if(panel->rows.size() != osize) {
736 panel->scroll_to_end_on_repaint = true;
737 dirty = true;
739 panel->request_paint();
742 void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type)
744 inst.dbg->request_break();
747 void wxwin_tracelog::callback(const debug_context::params& p)
749 switch(p.type) {
750 case debug_context::DEBUG_READ:
751 case debug_context::DEBUG_WRITE:
752 case debug_context::DEBUG_EXEC:
753 do_rwx_break(p.rwx.addr, p.rwx.value, p.type);
754 break;
755 case debug_context::DEBUG_TRACE: {
756 if(!trace_active)
757 return;
758 //Got tracelog line, send it.
759 threads::alock h(buffer_mutex);
760 lines_waiting.push_back(p.trace.decoded_insn);
761 if(!unprocessed_lines) {
762 unprocessed_lines = true;
763 runuifun([this]() { this->process_lines(); });
765 if(singlestepping && p.trace.true_insn) {
766 inst.dbg->request_break();
767 singlestepping = false;
769 break;
771 case debug_context::DEBUG_FRAME: {
772 std::ostringstream xstr;
773 xstr << "------------ ";
774 xstr << "Frame " << p.frame.frame;
775 if(p.frame.loadstated) xstr << " (loadstated)";
776 xstr << " ------------";
777 std::string str = xstr.str();
778 threads::alock h(buffer_mutex);
779 lines_waiting.push_back(str);
780 if(!unprocessed_lines) {
781 unprocessed_lines = true;
782 runuifun([this]() { this->process_lines(); });
784 break;
789 void wxwin_tracelog::killed(uint64_t addr, debug_context::etype type)
791 switch(type) {
792 case debug_context::DEBUG_READ:
793 case debug_context::DEBUG_WRITE:
794 case debug_context::DEBUG_EXEC: {
795 //We need to kill this hook if still active.
796 auto i2 = std::make_pair(addr, type);
797 rwx_breakpoints[i2] = false;
798 break;
800 case debug_context::DEBUG_TRACE:
801 //Dtor!
802 if(!trace_active)
803 return;
804 kill_debug_hooks(true);
805 runuifun([this]() {
806 this->enabled->SetValue(false);
807 this->enabled->Enable(false);
808 this->m_singlestep->Enable(false);
810 break;
811 case debug_context::DEBUG_FRAME:
812 //Do nothing.
813 break;
817 void wxwin_tracelog::on_enabled(wxCommandEvent& e)
819 CHECK_UI_THREAD;
820 bool enable = enabled->GetValue();
821 inst.iqueue->run([this, enable]() {
822 if(enable) {
823 threads::alock h(buffer_mutex);
824 broken = broken2;
825 broken2 = true;
826 for(auto& i : rwx_breakpoints) {
827 auto i2 = i.first;
828 CORE().dbg->add_callback(i2.first, i2.second, *this);
829 i.second = true;
831 CORE().dbg->add_callback(cpuid, debug_context::DEBUG_TRACE, *this);
832 CORE().dbg->add_callback(0, debug_context::DEBUG_FRAME, *this);
833 this->trace_active = true;
834 } else if(trace_active) {
835 this->trace_active = false;
836 this->kill_debug_hooks();
839 m_singlestep->Enable(enable);
842 bool find_match(const std::string& pattern, const std::string& candidate)
844 static std::string last_find;
845 static boost::regex regex;
846 if(pattern == "")
847 return false;
848 if(pattern[0] == 'F') {
849 //Substring find.
850 if(pattern != last_find) {
851 std::string tmp = pattern;
852 tmp = tmp.substr(1);
853 regex = boost::regex(tmp, boost::regex_constants::literal |
854 boost::regex_constants::icase);
855 last_find = pattern;
858 if(pattern[0] == 'W') {
859 //wildcard find.
860 if(pattern != last_find) {
861 std::ostringstream y;
862 for(size_t i = 1; i < pattern.length(); i++)
863 if(pattern[i] == '?')
864 y << ".";
865 else if(pattern[i] == '*')
866 y << ".*";
867 else if(pattern[i] >= 'A' && pattern[i] <= 'Z')
868 y << pattern[i];
869 else if(pattern[i] >= 'a' && pattern[i] <= 'z')
870 y << pattern[i];
871 else if(pattern[i] >= '0' && pattern[i] <= '9')
872 y << pattern[i];
873 else
874 y << "\\" << pattern[i];
875 std::string tmp = y.str();
876 regex = boost::regex(tmp, boost::regex_constants::extended);
877 last_find = pattern;
880 if(pattern[0] == 'R') {
881 //regexp find.
882 if(pattern != last_find) {
883 std::string tmp = pattern;
884 tmp = tmp.substr(1);
885 regex = boost::regex(tmp, boost::regex_constants::extended |
886 boost::regex_constants::icase);
887 last_find = pattern;
890 return regex_search(candidate, regex);
893 void wxwin_tracelog::on_menu(wxCommandEvent& e)
895 CHECK_UI_THREAD;
896 if(e.GetId() == wxID_EXIT) {
897 if(dirty) {
898 int r = prompt_for_save(this, "Trace log");
899 if(r < 0 || (r > 0 && !do_exit_save()))
900 return;
902 if(trace_active) {
903 inst.iqueue->run([this]() { this->kill_debug_hooks(); });
905 trace_active = false;
906 Destroy();
907 return;
908 } else if(e.GetId() == wxID_SAVE) {
909 try {
910 std::string filename = choose_file_save(this, "Save tracelog to",
911 UI_get_project_otherpath(inst), filetype_trace);
912 std::ofstream s(filename, std::ios::app);
913 if(!s) throw std::runtime_error("Error opening output file");
914 for(auto& i : panel->rows)
915 s << i << std::endl;
916 if(!s) throw std::runtime_error("Error writing output file");
917 dirty = false;
918 } catch(canceled_exception& e) {
919 } catch(std::exception& e) {
920 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
921 wxOK, this);
923 } else if(e.GetId() == wxID_FIND) {
924 std::string tmp;
925 dialog_find* d = new dialog_find(this);
926 if(d->ShowModal() != wxID_OK) {
927 d->Destroy();
928 return;
930 tmp = d->get_pattern();
931 d->Destroy();
932 if(tmp == "") {
933 find_active = false;
934 return;
936 find_string = tmp;
937 find_active = true;
938 find_line = 0;
939 while(find_line < panel->rows.size()) {
940 if(find_match(find_string, panel->rows[find_line]))
941 break;
942 find_line++;
944 if(find_line == panel->rows.size()) {
945 //Not found.
946 find_active = false;
947 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
948 wxICON_EXCLAMATION | wxOK, this);
949 } else
950 scroll_pane(find_line);
951 } else if(e.GetId() == wxID_FIND_NEXT) {
952 if(!find_active)
953 return;
954 uint64_t old_find_line = find_line;
955 find_line++;
956 while(!panel->rows.empty() && find_line != old_find_line) {
957 if(find_line >= panel->rows.size())
958 find_line = 0;
959 if(find_match(find_string, panel->rows[find_line]))
960 break;
961 find_line++;
963 scroll_pane(find_line);
964 } else if(e.GetId() == wxID_FIND_PREV) {
965 if(!find_active)
966 return;
967 uint64_t old_find_line = find_line;
968 find_line--;
969 while(!panel->rows.empty() && find_line != old_find_line) {
970 if(find_line >= panel->rows.size())
971 find_line = panel->rows.size() - 1;
972 if(find_match(find_string, panel->rows[find_line]))
973 break;
974 find_line--;
976 scroll_pane(find_line);
977 } else if(e.GetId() == wxID_SINGLESTEP) {
978 inst.iqueue->run_async([this]() {
979 this->singlestepping = true;
980 CORE().command->invoke("unpause-emulator");
981 }, [](std::exception& e) {});
982 } else if(e.GetId() == wxID_FRAMEADVANCE) {
983 inst.iqueue->run_async([this]() {
984 CORE().command->invoke("+advance-frame");
985 CORE().command->invoke("-advance-frame");
986 }, [](std::exception& e) {});
987 } else if(e.GetId() == wxID_CONTINUE) {
988 inst.iqueue->run_async([this]() {
989 CORE().command->invoke("unpause-emulator");
990 }, [](std::exception& e) {});
991 } else if(e.GetId() == wxID_BREAKPOINTS) {
992 dialog_breakpoints* d = new dialog_breakpoints(this, inst);
993 d->ShowModal();
994 d->Destroy();
995 } else if(e.GetId() == wxID_CLEAR) {
996 int r = prompt_for_save(this, "Trace log");
997 if(r < 0 || (r > 0 && !do_exit_save()))
998 return;
999 panel->rows.clear();
1000 panel->request_paint();
1001 find_active = false;
1005 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent& e)
1007 CHECK_UI_THREAD;
1008 std::string str;
1009 uint64_t m = min(pressed_row, current_row);
1010 uint64_t M = max(pressed_row, current_row) + 1;
1011 m = min(m, (uint64_t)rows.size());
1012 M = min(M, (uint64_t)rows.size());
1013 size_t lines = 0;
1015 for(uint64_t i = m; i < M && i < rows.size(); i++) {
1016 try {
1017 std::string mline = rows[i];
1018 if(lines == 1) str += "\n";
1019 str += mline;
1020 if(lines >= 1) str += "\n";
1021 lines++;
1022 } catch(...) {
1026 switch(e.GetId()) {
1027 case wxID_COPY:
1028 if (wxTheClipboard->Open()) {
1029 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
1030 wxTheClipboard->Close();
1032 break;
1033 case wxID_SAVE:
1034 try {
1035 std::string filename = choose_file_save(this, "Save tracelog fragment to",
1036 UI_get_project_otherpath(inst), filetype_trace);
1037 std::ofstream s(filename, std::ios::app);
1038 if(!s) throw std::runtime_error("Error opening output file");
1039 if(lines == 1) str += "\n";
1040 s << str;
1041 if(!s) throw std::runtime_error("Error writing output file");
1042 } catch(canceled_exception& e) {
1043 } catch(std::exception& e) {
1044 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1045 wxOK, this);
1047 break;
1048 case wxID_DELETE:
1049 rows.erase(rows.begin() + m, rows.begin() + M);
1050 if(m != M)
1051 p->dirty = true;
1052 request_paint();
1053 break;
1057 void wxwin_tracelog::scroll_pane(uint64_t line)
1059 unsigned r = panel->get_characters().second;
1060 unsigned offset = r / 2;
1061 if(offset > line)
1062 scroll->set_position(panel->pos = 0);
1063 else if(line + r <= panel->rows.size())
1064 scroll->set_position(panel->pos = line - offset);
1065 else
1066 scroll->set_position(panel->pos = panel->rows.size() - r);
1067 panel->request_paint();
1070 bool wxwin_tracelog::do_exit_save()
1072 CHECK_UI_THREAD;
1073 back:
1074 try {
1075 std::string filename = choose_file_save(this, "Save tracelog to",
1076 UI_get_project_otherpath(inst), filetype_trace);
1077 std::ofstream s(filename, std::ios::app);
1078 if(!s) throw std::runtime_error("Error opening output file");
1079 for(auto& i : panel->rows)
1080 s << i << std::endl;
1081 if(!s) throw std::runtime_error("Error writing output file");
1082 dirty = false;
1083 } catch(canceled_exception& e) {
1084 return false;
1085 } catch(std::exception& e) {
1086 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1087 wxOK, this);
1088 goto back;
1090 return true;
1093 std::set<std::pair<uint64_t, debug_context::etype>> wxwin_tracelog::get_breakpoints()
1095 std::set<std::pair<uint64_t, debug_context::etype>> ret;
1096 inst.iqueue->run([this, &ret]() {
1097 for(auto i : rwx_breakpoints)
1098 ret.insert(i.first);
1100 return ret;
1103 void wxwin_tracelog::add_breakpoint(uint64_t addr, debug_context::etype dtype)
1105 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1106 if(!trace_active) {
1107 //We'll register this later.
1108 rwx_breakpoints[i2] = false;
1109 return;
1111 inst.dbg->add_callback(i2.first, i2.second, *this);
1112 rwx_breakpoints[i2] = true;
1115 void wxwin_tracelog::remove_breakpoint(uint64_t addr, debug_context::etype dtype)
1117 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1118 auto& h = rwx_breakpoints[i2];
1119 if(h)
1120 inst.dbg->remove_callback(i2.first, i2.second, *this);
1121 rwx_breakpoints.erase(i2);
1124 wxwin_tracelog::wxwin_tracelog(wxWindow* parent, emulator_instance& _inst, int _cpuid,
1125 const std::string& cpuname)
1126 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
1127 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1128 wxCLIP_CHILDREN), inst(_inst)
1130 CHECK_UI_THREAD;
1131 cpuid = _cpuid;
1132 singlestepping = false;
1133 find_active = false;
1134 find_line = 0;
1135 closing = false;
1136 trace_active = false;
1137 unprocessed_lines = false;
1138 broken = false;
1139 broken2 = false;
1140 dirty = false;
1141 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1142 SetSizer(top_s);
1143 wxBoxSizer* bottom_s = new wxBoxSizer(wxHORIZONTAL);
1144 top_s->Add(enabled = new wxCheckBox(this, wxID_ANY, wxT("Enabled")), 0, wxGROW);
1145 bottom_s->Add(panel = new _panel(this, inst), 1, wxGROW);
1146 bottom_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1147 top_s->Add(bottom_s, 1, wxGROW);
1148 enabled->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxwin_tracelog::on_enabled),
1149 NULL, this);
1150 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_tracelog::on_wclose),
1151 NULL, this);
1152 scroll->set_page_size(panel->get_characters().second);
1153 scroll->set_handler([this](scroll_bar& s) {
1154 this->panel->pos = s.get_position();
1155 this->panel->request_paint();
1157 wxMenuBar* mb;
1158 wxStatusBar* sb;
1159 wxMenu* menu;
1161 SetMenuBar(mb = new wxMenuBar);
1162 SetStatusBar(sb = new wxStatusBar(this));
1163 mb->Append(menu = new wxMenu(), wxT("File"));
1164 menu->Append(wxID_SAVE, wxT("Save"));
1165 menu->AppendSeparator();
1166 menu->Append(wxID_EXIT, wxT("Close"));
1167 mb->Append(menu = new wxMenu(), wxT("Edit"));
1168 menu->Append(wxID_FIND, wxT("Find..."));
1169 menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
1170 menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
1171 menu->AppendSeparator();
1172 menu->Append(wxID_CLEAR, towxstring("Clear"));
1173 mb->Append(menu = new wxMenu(), wxT("Debug"));
1174 m_singlestep = menu->Append(wxID_SINGLESTEP, towxstring("Singlestep\tF2"));
1175 menu->Append(wxID_FRAMEADVANCE, towxstring("Frame advance\tF4"));
1176 menu->Append(wxID_CONTINUE, towxstring("Continue\tF5"));
1177 menu->AppendSeparator();
1178 menu->Append(wxID_BREAKPOINTS, towxstring("Breakpoints"));
1179 m_singlestep->Enable(false);
1180 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
1181 NULL, this);
1182 //Very nasty hack.
1183 wxSize tmp = panel->GetMinSize();
1184 panel->SetMinSize(panel->DoGetBestSize());
1185 top_s->SetSizeHints(this);
1186 wxSize tmp2 = GetClientSize();
1187 panel->SetMinSize(tmp);
1188 top_s->SetSizeHints(this);
1189 SetClientSize(tmp2);
1192 struct disasm_row
1194 uint64_t cover;
1195 std::string language;
1196 std::string row;
1199 class wxwin_disassembler : public wxFrame
1201 public:
1202 wxwin_disassembler(wxWindow* parent, emulator_instance& _inst);
1203 bool ShouldPreventAppExit() const { return false; }
1204 scroll_bar* get_scroll() { return scroll; }
1205 void on_menu(wxCommandEvent& e);
1206 void on_wclose(wxCloseEvent& e);
1207 private:
1208 class _panel : public text_framebuffer_panel
1210 public:
1211 _panel(wxwin_disassembler* parent, emulator_instance& _inst);
1212 void on_size(wxSizeEvent& e);
1213 void on_mouse(wxMouseEvent& e);
1214 wxSize DoGetBestSize() const;
1215 uint64_t pos;
1216 std::vector<uint64_t> rows;
1217 std::map<uint64_t, disasm_row> row_map;
1218 void on_popup_menu(wxCommandEvent& e);
1219 protected:
1220 void prepare_paint();
1221 private:
1222 emulator_instance& inst;
1223 uint64_t pressed_row;
1224 uint64_t current_row;
1225 bool holding;
1226 wxwin_disassembler* p;
1228 bool do_exit_save();
1229 void add_row(uint64_t addr, const disasm_row& row, bool last);
1230 void add_rows(const std::map<uint64_t, disasm_row>& rowdata);
1231 void add_rows_main(const std::map<uint64_t, disasm_row>& rowdata);
1232 void run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count);
1233 void scroll_pane(uint64_t line);
1234 emulator_instance& inst;
1235 scroll_bar* scroll;
1236 _panel* panel;
1237 bool dirty;
1238 bool closing;
1241 wxwin_disassembler::_panel::_panel(wxwin_disassembler* parent, emulator_instance& _inst)
1242 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL), inst(_inst)
1244 CHECK_UI_THREAD;
1245 p = parent;
1246 pos = 0;
1247 pressed_row = 0;
1248 current_row = 0;
1249 holding = false;
1250 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_disassembler::_panel::on_size), NULL, this);
1251 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1252 this);
1253 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1254 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1255 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1256 this);
1259 void wxwin_disassembler::_panel::on_size(wxSizeEvent& e)
1261 wxSize newsize = e.GetSize();
1262 auto tcell = get_cell();
1263 size_t lines = newsize.y / tcell.second;
1264 size_t linelen = newsize.x / tcell.first;
1265 if(lines < 1) lines = 1;
1266 if(linelen < 1) linelen = 1;
1267 set_size(linelen, lines);
1268 p->get_scroll()->set_page_size(lines);
1269 request_paint();
1270 e.Skip();
1273 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent& e)
1275 CHECK_UI_THREAD;
1276 uint64_t local_line = pos + e.GetY() / get_cell().second;
1277 if(e.RightDown()) {
1278 if(local_line < rows.size()) {
1279 holding = true;
1280 pressed_row = local_line;
1282 }else if(e.RightUp()) {
1283 holding = false;
1284 wxMenu menu;
1285 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
1286 wxCommandEventHandler(wxwin_disassembler::_panel::on_popup_menu), NULL, this);
1287 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
1288 menu.Append(wxID_SAVE, wxT("Save to file"));
1289 menu.AppendSeparator();
1290 menu.Append(wxID_DISASM_MORE, wxT("Disassemble more"));
1291 menu.AppendSeparator();
1292 menu.Append(wxID_DELETE, wxT("Delete"));
1293 PopupMenu(&menu);
1294 } else {
1295 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
1296 request_paint();
1298 unsigned speed = 1;
1299 if(e.ShiftDown())
1300 speed = 10;
1301 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
1304 wxSize wxwin_disassembler::_panel::DoGetBestSize() const
1306 return wxSize(40 * 8, 25 * 16);
1309 void wxwin_disassembler::_panel::prepare_paint()
1311 p->get_scroll()->set_range(rows.size());
1312 uint64_t m = min(pressed_row, current_row);
1313 uint64_t M = max(pressed_row, current_row);
1314 auto s = get_characters();
1315 uint64_t i;
1316 for(i = pos; i < pos + s.second && i < rows.size(); i++) {
1317 bool selected = holding && (i >= m) && (i <= M);
1318 uint32_t fg = selected ? 0x0000FF : 0x000000;
1319 uint32_t bg = selected ? 0x000000 : 0xFFFFFF;
1320 write(row_map[rows[i]].row, s.first, 0, i - pos, fg, bg);
1322 for(; i < pos + s.second; i++) {
1323 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
1327 void wxwin_disassembler::on_menu(wxCommandEvent& e)
1329 CHECK_UI_THREAD;
1330 if(e.GetId() == wxID_EXIT) {
1331 if(dirty) {
1332 int r = prompt_for_save(this, "Disassembly");
1333 if(r < 0 || (r > 0 && !do_exit_save()))
1334 return;
1336 Destroy();
1337 return;
1338 } else if(e.GetId() == wxID_SAVE) {
1339 try {
1340 std::string filename = choose_file_save(this, "Save disassembly to",
1341 UI_get_project_otherpath(inst), filetype_disassembly);
1342 std::ofstream s(filename, std::ios::app);
1343 if(!s) throw std::runtime_error("Error opening output file");
1344 for(auto& i : panel->rows)
1345 s << panel->row_map[i].row << std::endl;
1346 if(!s) throw std::runtime_error("Error writing output file");
1347 dirty = false;
1348 } catch(canceled_exception& e) {
1349 } catch(std::exception& e) {
1350 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1351 wxOK, this);
1353 } else if(e.GetId() == wxID_DISASM) {
1354 std::string tmp;
1355 dialog_disassemble* d = new dialog_disassemble(this, inst);
1356 if(d->ShowModal() != wxID_OK) {
1357 d->Destroy();
1358 return;
1360 std::string disasm = d->get_disassembler();
1361 uint64_t addr = d->get_address();
1362 uint64_t count = d->get_count();
1363 d->Destroy();
1364 inst.iqueue->run_async([this, disasm, addr, count]() {
1365 this->run_disassembler(disasm, addr, count);
1366 }, [](std::exception& e) {});
1367 } else if(e.GetId() == wxID_GOTO) {
1368 try {
1369 std::string to = pick_text(this, "Goto", "Enter address to go to:", "");
1370 inst.iqueue->run_async([this, to]() {
1371 uint64_t addr;
1372 uint64_t base = 0;
1373 std::string vma;
1374 std::string offset;
1375 std::string _to = to;
1376 size_t sp = _to.find_first_of("+");
1377 if(sp >= _to.length()) {
1378 offset = _to;
1379 } else {
1380 vma = _to.substr(0, sp);
1381 offset = _to.substr(sp + 1);
1383 if(vma != "") {
1384 bool found = false;
1385 for(auto i : CORE().memory->get_regions()) {
1386 if(i->name == vma) {
1387 base = i->base;
1388 found = true;
1391 if(!found) {
1392 runuifun([this] {
1393 show_message_ok(this, "Error in address",
1394 "No such memory area known",
1395 wxICON_EXCLAMATION);
1397 return;
1400 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1401 " <name>+<hexdigits>", [&addr, offset]() {
1402 addr = hex::from<uint64_t>(offset); }))
1403 return;
1404 addr += base;
1405 runuifun([this, addr]() {
1406 uint64_t nrow = 0;
1407 uint64_t low = 0;
1408 uint64_t high = this->panel->rows.size();
1409 while(low < high && low < high - 1) {
1410 nrow = (low + high) / 2;
1411 if(this->panel->rows[nrow] > addr)
1412 high = nrow;
1413 else if(this->panel->rows[nrow] < addr)
1414 low = nrow;
1415 else
1416 break;
1418 this->scroll_pane(nrow);
1420 }, [](std::exception& e) {});
1421 } catch(canceled_exception& e) {
1426 void remove_from_array(std::vector<uint64_t>& v, uint64_t e)
1428 //Binary search for the element to remove.
1429 size_t low = 0;
1430 size_t high = v.size();
1431 size_t mid = 0;
1432 while(low < high) {
1433 mid = (low + high) / 2;
1434 if(v[mid] < e)
1435 low = mid;
1436 else if(v[mid] > e)
1437 high = mid;
1438 else
1439 break;
1441 if(v[mid] == e)
1442 v.erase(v.begin() + mid);
1445 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent& e)
1447 CHECK_UI_THREAD;
1448 if(e.GetId() == wxID_DISASM_MORE)
1450 if(current_row >= rows.size())
1451 return;
1452 uint64_t base = rows[current_row];
1453 uint64_t rbase = base;
1454 if(!row_map.count(base))
1455 return;
1456 auto& r = row_map[base];
1457 base = base + r.cover;
1458 std::string disasm = r.language;
1459 dialog_disassemble* d = new dialog_disassemble(this, inst, base, disasm);
1460 if(d->ShowModal() != wxID_OK) {
1461 d->Destroy();
1462 return;
1464 disasm = d->get_disassembler();
1465 uint64_t addr = d->get_address();
1466 uint64_t count = d->get_count();
1467 d->Destroy();
1468 auto pp = p;
1469 inst.iqueue->run_async([pp, disasm, addr, count]() {
1470 pp->run_disassembler(disasm, addr, count);
1471 }, [](std::exception& e) {});
1472 //Delete entries in (rbase, addr) if addr = base.
1473 if(addr == base) {
1474 for(uint64_t i = rbase + 1; i < addr; i++)
1475 if(row_map.count(i)) {
1476 //This line needs to be removed from rows too.
1477 row_map.erase(i);
1478 remove_from_array(rows, i);
1482 std::string str;
1483 uint64_t m = min(min(pressed_row, current_row), (uint64_t)rows.size());
1484 uint64_t M = min(max(pressed_row, current_row) + 1, (uint64_t)rows.size());
1485 size_t lines = 0;
1487 for(uint64_t i = m; i < M; i++) {
1488 try {
1489 std::string mline = row_map[rows[i]].row;
1490 if(lines == 1) str += "\n";
1491 str += mline;
1492 if(lines >= 1) str += "\n";
1493 lines++;
1494 } catch(...) {
1498 switch(e.GetId()) {
1499 case wxID_COPY:
1500 if (wxTheClipboard->Open()) {
1501 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
1502 wxTheClipboard->Close();
1504 break;
1505 case wxID_SAVE:
1506 try {
1507 std::string filename = choose_file_save(this, "Save disassembly fragment to",
1508 UI_get_project_otherpath(inst), filetype_disassembly);
1509 std::ofstream s(filename, std::ios::app);
1510 if(!s) throw std::runtime_error("Error opening output file");
1511 if(lines == 1) str += "\n";
1512 s << str;
1513 if(!s) throw std::runtime_error("Error writing output file");
1514 } catch(canceled_exception& e) {
1515 } catch(std::exception& e) {
1516 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1517 wxOK, this);
1519 break;
1520 case wxID_DELETE:
1521 for(uint64_t i = m; i < M; i++)
1522 row_map.erase(rows[i]);
1523 rows.erase(rows.begin() + m, rows.begin() + M);
1524 if(m != M)
1525 p->dirty = true;
1526 request_paint();
1527 break;
1531 std::string format_vma_offset(memory_space::region& region, uint64_t offset)
1533 std::ostringstream y;
1534 y << region.name;
1535 size_t sizedigits = 0;
1536 uint64_t tmp = region.size - 1;
1537 while(tmp > 0) {
1538 tmp >>= 4;
1539 sizedigits++;
1541 y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << offset;
1542 return y.str();
1545 std::string lookup_address(emulator_instance& inst, uint64_t raw)
1547 auto g = inst.memory->lookup(raw);
1548 if(!g.first)
1549 return hex::to<uint64_t>(raw);
1550 else
1551 return format_vma_offset(*g.first, g.second);
1554 inline int sign_compare(uint64_t a, uint64_t b)
1556 if(a < b) return -1;
1557 if(b < a) return 1;
1558 return 0;
1561 void insert_into_array(std::vector<uint64_t>& v, uint64_t e)
1563 //Binary search for the gap to insert to.
1564 size_t low = 0;
1565 size_t high = v.size();
1566 size_t mid = 0;
1567 while(low < high) {
1568 mid = (low + high) / 2;
1569 int s1 = sign_compare(v[mid], e);
1570 int s2 = ((mid + 1) < v.size()) ? sign_compare(v[mid + 1], e) : 1;
1571 if(s1 < 0 && s2 > 0)
1572 break;
1573 else if(s1 == 0 || s2 == 0)
1574 return;
1575 else if(s1 > 0)
1576 high = mid;
1577 else if(s2 < 0)
1578 low = mid;
1580 if(mid < v.size() && v[mid] < e)
1581 mid++;
1582 v.insert(v.begin() + mid, e);
1585 void wxwin_disassembler::add_row(uint64_t addr, const disasm_row& row, bool last)
1587 auto& rows = panel->rows;
1588 auto& row_map = panel->row_map;
1589 if(row_map.count(addr)) {
1590 row_map[addr] = row;
1591 } else {
1592 //We need to insert the row into rows.
1593 row_map[addr] = row;
1594 insert_into_array(rows, addr);
1596 dirty = true;
1597 if(!last)
1598 for(uint64_t i = addr + 1; i < addr + row.cover; i++)
1599 if(row_map.count(i)) {
1600 //This line needs to be removed from rows too.
1601 row_map.erase(i);
1602 remove_from_array(rows, i);
1606 void wxwin_disassembler::add_rows(const std::map<uint64_t, disasm_row>& rowdata)
1608 for(auto i = rowdata.begin(); i != rowdata.end(); i++) {
1609 auto j = i;
1610 j++;
1611 bool last = (j == rowdata.end());
1612 add_row(i->first, i->second, last);
1614 panel->request_paint();
1617 void wxwin_disassembler::add_rows_main(const std::map<uint64_t, disasm_row>& rowdata)
1619 std::map<uint64_t, disasm_row> _rowdata;
1620 for(auto& i : rowdata) {
1621 _rowdata[i.first] = i.second;
1622 _rowdata[i.first].row = lookup_address(inst, i.first) + " " + i.second.row;
1624 runuifun([this, _rowdata]() { this->add_rows(_rowdata); });
1627 template<typename T, bool hex> disasm_row _disassemble_data_item(emulator_instance& inst, uint64_t& addrbase,
1628 int endian, const std::string& disasm)
1630 char buf[sizeof(T)];
1631 for(size_t i = 0; i < sizeof(T); i++)
1632 buf[i] = inst.memory->read<uint8_t>(addrbase + i);
1633 disasm_row r;
1634 if(hex)
1635 r.row = (stringfmt() << "DATA 0x" << hex::to<T>(serialization::read_endian<T>(buf, endian))).
1636 str();
1637 else if(sizeof(T) > 1)
1638 r.row = (stringfmt() << "DATA " << serialization::read_endian<T>(buf, endian)).str();
1639 else
1640 r.row = (stringfmt() << "DATA " << (int)serialization::read_endian<T>(buf, endian)).str();
1641 r.cover = sizeof(T);
1642 r.language = disasm;
1643 addrbase += sizeof(T);
1644 return r;
1647 disasm_row disassemble_data_item(emulator_instance& inst, uint64_t& addrbase, const std::string& disasm)
1649 int endian;
1650 if(disasm[7] == 'l') endian = -1;
1651 if(disasm[7] == 'h') endian = 0;
1652 if(disasm[7] == 'b') endian = 1;
1653 switch(disasm[6]) {
1654 case 'b': return _disassemble_data_item<int8_t, false>(inst, addrbase, endian, disasm);
1655 case 'B': return _disassemble_data_item<uint8_t, false>(inst, addrbase, endian, disasm);
1656 case 'c': return _disassemble_data_item<uint8_t, true>(inst, addrbase, endian, disasm);
1657 case 'C': return _disassemble_data_item<uint16_t, true>(inst, addrbase, endian, disasm);
1658 case 'd': return _disassemble_data_item<int32_t, false>(inst, addrbase, endian, disasm);
1659 case 'D': return _disassemble_data_item<uint32_t, false>(inst, addrbase, endian, disasm);
1660 case 'f': return _disassemble_data_item<float, false>(inst, addrbase, endian, disasm);
1661 case 'F': return _disassemble_data_item<double, false>(inst, addrbase, endian, disasm);
1662 case 'h': return _disassemble_data_item<ss_int24_t, false>(inst, addrbase, endian, disasm);
1663 case 'H': return _disassemble_data_item<ss_uint24_t, false>(inst, addrbase, endian, disasm);
1664 case 'i': return _disassemble_data_item<ss_uint24_t, true>(inst, addrbase, endian, disasm);
1665 case 'I': return _disassemble_data_item<uint32_t, true>(inst, addrbase, endian, disasm);
1666 case 'q': return _disassemble_data_item<int64_t, false>(inst, addrbase, endian, disasm);
1667 case 'Q': return _disassemble_data_item<uint64_t, false>(inst, addrbase, endian, disasm);
1668 case 'r': return _disassemble_data_item<uint64_t, true>(inst, addrbase, endian, disasm);
1669 case 'w': return _disassemble_data_item<int16_t, false>(inst, addrbase, endian, disasm);
1670 case 'W': return _disassemble_data_item<uint16_t, false>(inst, addrbase, endian, disasm);
1672 throw std::runtime_error("Invalid kind of data");
1675 void wxwin_disassembler::scroll_pane(uint64_t line)
1677 unsigned r = panel->get_characters().second;
1678 unsigned offset = r / 2;
1679 if(offset > line)
1680 scroll->set_position(panel->pos = 0);
1681 else if(line + r < panel->rows.size())
1682 scroll->set_position(panel->pos = line - offset);
1683 else
1684 scroll->set_position(panel->pos = panel->rows.size() - r);
1685 panel->request_paint();
1688 void wxwin_disassembler::on_wclose(wxCloseEvent& e)
1690 CHECK_UI_THREAD;
1691 if(dirty && !wxwidgets_exiting) {
1692 int r = prompt_for_save(this, "Disassembly");
1693 if(r < 0 || (r > 0 && !do_exit_save()))
1694 return;
1696 if(!closing)
1697 Destroy();
1698 closing = true;
1701 void wxwin_disassembler::run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count)
1703 std::map<uint64_t, disasm_row> rowdata;
1704 if(regex_match("\\$data:.*", disasm)) {
1705 if(run_show_error(this, "Error in disassember", "Error in disassember",
1706 [this, disasm, &rowdata, &addrbase, count]() {
1707 for(uint64_t i = 0; i < count; i++) {
1708 uint64_t base = addrbase;
1709 disasm_row r = disassemble_data_item(this->inst, addrbase, disasm);
1710 rowdata[base] = r;
1713 return;
1714 add_rows_main(rowdata);
1715 return;
1717 disassembler* d;
1718 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm + "' found",
1719 [&d, disasm]() {
1720 d = &disassembler::byname(disasm);
1722 return;
1723 for(uint64_t i = 0; i < count; i++) {
1724 uint64_t base = addrbase;
1725 disasm_row r;
1726 r.row = d->disassemble(addrbase, [this, &addrbase]() -> unsigned char {
1727 return this->inst.memory->read<uint8_t>(addrbase++);
1729 r.cover = addrbase - base;
1730 r.language = disasm;
1731 rowdata[base] = r;
1733 add_rows_main(rowdata);
1736 bool wxwin_disassembler::do_exit_save()
1738 CHECK_UI_THREAD;
1739 back:
1740 try {
1741 std::string filename = choose_file_save(this, "Save disassembly to",
1742 UI_get_project_otherpath(inst), filetype_disassembly);
1743 std::ofstream s(filename, std::ios::app);
1744 if(!s) throw std::runtime_error("Error opening output file");
1745 for(auto& i : panel->rows)
1746 s << panel->row_map[i].row << std::endl;
1747 if(!s) throw std::runtime_error("Error writing output file");
1748 dirty = false;
1749 } catch(canceled_exception& e) {
1750 return false;
1751 } catch(std::exception& e) {
1752 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1753 wxOK, this);
1754 goto back;
1756 return true;
1759 wxwin_disassembler::wxwin_disassembler(wxWindow* parent, emulator_instance& _inst)
1760 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Disassembler"), wxDefaultPosition,
1761 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1762 wxCLIP_CHILDREN), inst(_inst)
1764 CHECK_UI_THREAD;
1765 closing = false;
1766 dirty = false;
1767 wxBoxSizer* top_s = new wxBoxSizer(wxHORIZONTAL);
1768 SetSizer(top_s);
1769 top_s->Add(panel = new _panel(this, inst), 1, wxGROW);
1770 top_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1771 scroll->set_page_size(panel->get_characters().second);
1772 scroll->set_handler([this](scroll_bar& s) {
1773 this->panel->pos = s.get_position();
1774 this->panel->request_paint();
1776 wxMenuBar* mb;
1777 wxStatusBar* sb;
1778 wxMenu* menu;
1780 SetMenuBar(mb = new wxMenuBar);
1781 SetStatusBar(sb = new wxStatusBar(this));
1782 mb->Append(menu = new wxMenu(), wxT("File"));
1783 menu->Append(wxID_DISASM, wxT("Disassemble..."));
1784 menu->AppendSeparator();
1785 menu->Append(wxID_SAVE, wxT("Save"));
1786 menu->AppendSeparator();
1787 menu->Append(wxID_EXIT, wxT("Close"));
1788 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_disassembler::on_menu),
1789 NULL, this);
1790 mb->Append(menu = new wxMenu(), wxT("Edit"));
1791 menu->Append(wxID_GOTO, wxT("Goto"));
1793 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_disassembler::on_wclose),
1794 NULL, this);
1795 //Very nasty hack.
1796 wxSize tmp = panel->GetMinSize();
1797 panel->SetMinSize(panel->DoGetBestSize());
1798 top_s->SetSizeHints(this);
1799 wxSize tmp2 = GetClientSize();
1800 panel->SetMinSize(tmp);
1801 top_s->SetSizeHints(this);
1802 SetClientSize(tmp2);
1805 dialog_breakpoints::dialog_breakpoints(wxwin_tracelog* parent, emulator_instance& _inst)
1806 : wxDialog(parent, wxID_ANY, wxT("Breakpoints")), inst(_inst)
1808 CHECK_UI_THREAD;
1809 pwin = parent;
1810 regions = inst.memory->get_regions();
1811 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1812 SetSizer(top_s);
1813 top_s->Add(brklist = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400)), 1, wxGROW);
1814 brklist->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
1815 wxCommandEventHandler(dialog_breakpoints::on_selchange), NULL, this);
1816 populate_breakpoints();
1817 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1818 pbutton_s->Add(addb = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
1819 pbutton_s->Add(delb = new wxButton(this, wxID_ANY, wxT("Remove")), 0, wxGROW);
1820 pbutton_s->AddStretchSpacer();
1821 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
1822 delb->Enable(false);
1823 addb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_add), NULL,
1824 this);
1825 delb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_delete),
1826 NULL, this);
1827 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_ok), NULL,
1828 this);
1829 top_s->Add(pbutton_s, 0, wxGROW);
1830 top_s->SetSizeHints(this);
1831 Fit();
1834 void dialog_breakpoints::populate_breakpoints()
1836 CHECK_UI_THREAD;
1837 auto t = pwin->get_breakpoints();
1838 for(auto i : t) {
1839 std::string line = format_line(i);
1840 unsigned insert_pos = get_insert_pos(i);
1841 brklist->Insert(towxstring(line), insert_pos);
1842 listsyms.insert(listsyms.begin() + insert_pos, i);
1846 void dialog_breakpoints::on_add(wxCommandEvent& e)
1848 CHECK_UI_THREAD;
1849 uint64_t addr;
1850 debug_context::etype dtype;
1851 dialog_breakpoint_add* d = new dialog_breakpoint_add(this, regions);
1852 if(d->ShowModal() != wxID_OK) {
1853 d->Destroy();
1854 return;
1856 rpair(addr, dtype) = d->get_result();
1857 d->Destroy();
1858 inst.iqueue->run_async([this, addr, dtype]() {
1859 pwin->add_breakpoint(addr, dtype);
1860 }, [](std::exception& e) {});
1861 auto ent = std::make_pair(addr, dtype);
1862 std::string line = format_line(ent);
1863 unsigned insert_pos = get_insert_pos(ent);
1864 brklist->Insert(towxstring(line), insert_pos);
1865 listsyms.insert(listsyms.begin() + insert_pos, ent);
1868 void dialog_breakpoints::on_delete(wxCommandEvent& e)
1870 CHECK_UI_THREAD;
1871 int idx = brklist->GetSelection();
1872 if(idx == wxNOT_FOUND)
1873 return;
1874 uint64_t addr;
1875 debug_context::etype dtype;
1876 addr = listsyms[idx].first;
1877 dtype = listsyms[idx].second;
1878 inst.iqueue->run_async([this, addr, dtype]() {
1879 pwin->remove_breakpoint(addr, dtype);
1880 }, [](std::exception& e) {});
1881 brklist->Delete(idx);
1882 listsyms.erase(listsyms.begin() + idx);
1885 size_t dialog_breakpoints::get_insert_pos(std::pair<uint64_t, debug_context::etype> entry)
1887 size_t i = 0;
1888 for(i = 0; i < listsyms.size(); i++)
1889 if(entry < listsyms[i])
1890 return i;
1891 return i;
1894 std::string dialog_breakpoints::format_line(std::pair<uint64_t, debug_context::etype> entry)
1896 std::string base = "";
1897 for(auto i : regions) {
1898 if(entry.first >= i->base && entry.first < i->base + i->size) {
1899 base = format_vma_offset(*i, entry.first - i->base);
1900 break;
1903 if(base == "")
1904 base = hex::to<uint64_t>(entry.first);
1905 if(entry.second == debug_context::DEBUG_READ)
1906 return base + ": Read";
1907 if(entry.second == debug_context::DEBUG_WRITE)
1908 return base + ": Write";
1909 if(entry.second == debug_context::DEBUG_EXEC)
1910 return base + ": Execute";
1911 return base + ": Unknown";
1914 void dialog_breakpoints::on_selchange(wxCommandEvent& e)
1916 CHECK_UI_THREAD;
1917 delb->Enable(brklist->GetSelection() != wxNOT_FOUND);
1921 void wxeditor_tracelog_display(wxWindow* parent, emulator_instance& inst, int cpuid, const std::string& cpuname)
1923 CHECK_UI_THREAD;
1924 try {
1925 wxwin_tracelog* d = new wxwin_tracelog(parent, inst, cpuid, cpuname);
1926 d->Show();
1927 } catch(std::exception& e) {
1928 show_message_ok(parent, "Error opening trace logger", e.what(), wxICON_EXCLAMATION);
1932 void wxeditor_disassembler_display(wxWindow* parent, emulator_instance& inst)
1934 CHECK_UI_THREAD;
1935 wxwin_disassembler* d = new wxwin_disassembler(parent, inst);
1936 d->Show();