Make various instance stuff to take references to other instance objs
[lsnes.git] / src / platform / wxwidgets / tracelogger.cpp
blob523a24c8e754e92e0d530472316f489427cf2489
1 #include "platform/wxwidgets/platform.hpp"
2 #include "platform/wxwidgets/textrender.hpp"
3 #include "platform/wxwidgets/scrollbar.hpp"
4 #include "platform/wxwidgets/loadsave.hpp"
5 #include "core/command.hpp"
6 #include "core/debug.hpp"
7 #include "core/instance.hpp"
8 #include "core/mainloop.hpp"
9 #include "core/memorymanip.hpp"
10 #include "core/project.hpp"
11 #include "interface/disassembler.hpp"
12 #include "library/minmax.hpp"
13 #include "library/hex.hpp"
14 #include "library/serialization.hpp"
15 #include <wx/frame.h>
16 #include <wx/clipbrd.h>
17 #include <wx/msgdlg.h>
18 #include <wx/menu.h>
19 #include <wx/button.h>
20 #include <wx/checkbox.h>
21 #include <wx/listbox.h>
22 #include <wx/stattext.h>
23 #include <wx/combobox.h>
24 #include <wx/textctrl.h>
25 #include <wx/spinctrl.h>
26 #include <wx/statusbr.h>
27 #include <wx/dataobj.h>
28 #include <wx/sizer.h>
29 #include <boost/regex.hpp>
30 #include <set>
32 namespace
34 enum
36 wxID_FIND_NEXT = wxID_HIGHEST + 1,
37 wxID_FIND_PREV,
38 wxID_GOTO,
39 wxID_DISASM,
40 wxID_DISASM_MORE,
41 wxID_SINGLESTEP,
42 wxID_BREAKPOINTS,
43 wxID_CONTINUE,
44 wxID_FRAMEADVANCE,
45 wxID_CLEAR,
48 int prompt_for_save(wxWindow* parent, const std::string& what)
50 wxMessageDialog* d = new wxMessageDialog(parent, towxstring(what + " has unsaved changes, "
51 "save before closing?"), towxstring("Save on exit?"), wxCENTER | wxYES_NO | wxCANCEL |
52 wxYES_DEFAULT);
53 d->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
54 int r = d->ShowModal();
55 d->Destroy();
56 if(r == wxID_YES) return 1;
57 if(r == wxID_NO) return 0;
58 if(r == wxID_CANCEL) return -1;
59 return -1;
62 class dialog_find : public wxDialog
64 public:
65 dialog_find(wxWindow* parent);
66 std::string get_pattern();
67 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
68 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
69 private:
70 wxTextCtrl* text;
71 wxComboBox* type;
72 wxButton* ok;
73 wxButton* cancel;
76 dialog_find::dialog_find(wxWindow* parent)
77 : wxDialog(parent, wxID_ANY, wxT("Find"))
79 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
80 SetSizer(top_s);
81 wxBoxSizer* t_s = new wxBoxSizer(wxHORIZONTAL);
82 t_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
83 0, NULL, wxCB_READONLY), 1, wxGROW);
84 type->Append(towxstring("Literal"));
85 type->Append(towxstring("Wildcards"));
86 type->Append(towxstring("Regexp"));
87 type->SetSelection(0);
88 t_s->Add(text = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(350, -1),
89 wxTE_PROCESS_ENTER), 0, wxGROW);
90 top_s->Add(t_s, 1, wxGROW);
91 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
92 pbutton_s->AddStretchSpacer();
93 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
94 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
95 text->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
96 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
97 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_cancel), NULL,
98 this);
99 top_s->Add(pbutton_s, 0, wxGROW);
100 top_s->SetSizeHints(this);
101 Fit();
104 std::string dialog_find::get_pattern()
106 if(tostdstring(text->GetValue()) == "")
107 return "";
108 if(type->GetSelection() == 2)
109 return "R" + tostdstring(text->GetValue());
110 else if(type->GetSelection() == 1)
111 return "W" + tostdstring(text->GetValue());
112 else
113 return "F" + tostdstring(text->GetValue());
116 class dialog_disassemble : public wxDialog
118 public:
119 dialog_disassemble(wxWindow* parent);
120 dialog_disassemble(wxWindow* parent, uint64_t dflt_base, const std::string& dflt_lang);
121 std::string get_disassembler();
122 uint64_t get_address();
123 uint64_t get_count();
124 void on_change(wxCommandEvent& e);
125 void on_ok(wxCommandEvent& e);
126 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
127 private:
128 void init(bool spec, uint64_t dflt_base, std::string dflt_lang);
129 wxComboBox* type;
130 wxComboBox* endian;
131 wxComboBox* vma;
132 wxTextCtrl* address;
133 wxSpinCtrl* count;
134 wxButton* ok;
135 wxButton* cancel;
136 bool has_default;
137 unsigned code_types;
138 static std::string old_dflt_lang;
139 static uint64_t old_dflt_base;
142 std::string dialog_disassemble::old_dflt_lang;
143 uint64_t dialog_disassemble::old_dflt_base;
145 dialog_disassemble::dialog_disassemble(wxWindow* parent)
146 : wxDialog(parent, wxID_ANY, wxT("Disassemble region"))
148 init(false, 0, "");
151 dialog_disassemble::dialog_disassemble(wxWindow* parent, uint64_t dflt_base, const std::string& dflt_lang)
152 : wxDialog(parent, wxID_ANY, wxT("Disassemble region"))
154 init(true, dflt_base, dflt_lang);
157 void dialog_disassemble::init(bool spec, uint64_t dflt_base, std::string dflt_lang)
159 std::map<std::string, std::pair<uint64_t, uint64_t>> regions;
160 std::set<std::string> disasms;
161 lsnes_instance.iqueue.run([&regions, &disasms]() {
162 for(auto i : lsnes_instance.memory.get_regions())
163 regions[i->name] = std::make_pair(i->base, i->size);
164 disasms = disassembler::list();
167 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
168 SetSizer(top_s);
170 wxBoxSizer* type_s = new wxBoxSizer(wxHORIZONTAL);
171 type_s->Add(new wxStaticText(this, wxID_ANY, wxT("Language:")), 0, wxGROW);
172 type_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
173 0, NULL, wxCB_READONLY), 0, wxGROW);
174 for(auto& i : disasms)
175 type->Append(towxstring(i));
176 code_types = type->GetCount();
177 type->Append(towxstring("Data (signed byte)"));
178 type->Append(towxstring("Data (unsigned byte)"));
179 type->Append(towxstring("Data (hex byte)"));
180 type->Append(towxstring("Data (signed word)"));
181 type->Append(towxstring("Data (unsigned word)"));
182 type->Append(towxstring("Data (hex word)"));
183 type->Append(towxstring("Data (signed onehalfword)"));
184 type->Append(towxstring("Data (unsigned onehalfword)"));
185 type->Append(towxstring("Data (hex onehalfword)"));
186 type->Append(towxstring("Data (signed doubleword)"));
187 type->Append(towxstring("Data (unsigned doubleword)"));
188 type->Append(towxstring("Data (hex doubleword)"));
189 type->Append(towxstring("Data (signed quadword)"));
190 type->Append(towxstring("Data (unsigned quadword)"));
191 type->Append(towxstring("Data (hex quadword)"));
192 type->Append(towxstring("Data (float)"));
193 type->Append(towxstring("Data (double)"));
194 type->SetSelection(0);
195 type->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
196 NULL, this);
197 top_s->Add(type_s, 0, wxGROW);
199 wxBoxSizer* endian_s = new wxBoxSizer(wxHORIZONTAL);
200 endian_s->Add(new wxStaticText(this, wxID_ANY, wxT("Endian:")), 0, wxGROW);
201 endian_s->Add(endian = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
202 0, NULL, wxCB_READONLY), 0, wxGROW);
203 endian->Append(towxstring("(Memory area default)"));
204 endian->Append(towxstring("Little-endian"));
205 endian->Append(towxstring("Host-endian"));
206 endian->Append(towxstring("Big-endian"));
207 endian->SetSelection(0);
208 endian->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
209 NULL, this);
210 top_s->Add(endian_s, 0, wxGROW);
212 wxBoxSizer* vma_s = new wxBoxSizer(wxHORIZONTAL);
213 vma_s->Add(new wxStaticText(this, wxID_ANY, wxT("Area:")), 0, wxGROW);
214 vma_s->Add(vma = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
215 0, NULL, wxCB_READONLY), 0, wxGROW);
216 vma->Append(towxstring("(Any)"));
217 for(auto& i : regions)
218 vma->Append(towxstring(i.first));
219 vma->SetSelection(0);
220 vma->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
221 NULL, this);
222 top_s->Add(vma_s, 0, wxGROW);
224 wxBoxSizer* addr_s = new wxBoxSizer(wxHORIZONTAL);
225 addr_s->Add(new wxStaticText(this, wxID_ANY, wxT("Address:")), 0, wxGROW);
226 addr_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)),
227 0, wxGROW);
228 address->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(dialog_disassemble::on_change),
229 NULL, this);
230 top_s->Add(addr_s, 0, wxGROW);
232 wxBoxSizer* cnt_s = new wxBoxSizer(wxHORIZONTAL);
233 cnt_s->Add(new wxStaticText(this, wxID_ANY, wxT("Count:")), 0, wxGROW);
234 cnt_s->Add(count = new wxSpinCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
235 wxSP_ARROW_KEYS, 1, 1000000000, 10), 0, wxGROW);
236 count->Connect(wxEVT_SPINCTRL, wxCommandEventHandler(dialog_disassemble::on_change), NULL,
237 this);
238 top_s->Add(cnt_s, 0, wxGROW);
240 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
241 pbutton_s->AddStretchSpacer();
242 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
243 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
244 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_ok), NULL,
245 this);
246 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_cancel),
247 NULL, this);
248 top_s->Add(pbutton_s, 0, wxGROW);
249 top_s->SetSizeHints(this);
250 Fit();
252 has_default = spec;
253 if(!spec) {
254 dflt_lang = old_dflt_lang;
255 dflt_base = old_dflt_base;
257 //Set default language.
258 if(regex_match("\\$data:.*", dflt_lang)) {
259 switch(dflt_lang[6]) {
260 case 'b': type->SetSelection(code_types + 0); break;
261 case 'B': type->SetSelection(code_types + 1); break;
262 case 'c': type->SetSelection(code_types + 2); break;
263 case 'w': type->SetSelection(code_types + 3); break;
264 case 'W': type->SetSelection(code_types + 4); break;
265 case 'C': type->SetSelection(code_types + 5); break;
266 case 'h': type->SetSelection(code_types + 6); break;
267 case 'H': type->SetSelection(code_types + 7); break;
268 case 'i': type->SetSelection(code_types + 8); break;
269 case 'd': type->SetSelection(code_types + 9); break;
270 case 'D': type->SetSelection(code_types + 10); break;
271 case 'I': type->SetSelection(code_types + 11); break;
272 case 'q': type->SetSelection(code_types + 12); break;
273 case 'Q': type->SetSelection(code_types + 13); break;
274 case 'r': type->SetSelection(code_types + 14); break;
275 case 'f': type->SetSelection(code_types + 15); break;
276 case 'F': type->SetSelection(code_types + 16); break;
278 switch(dflt_lang[7]) {
279 case 'l': endian->SetSelection(1); break;
280 case 'h': endian->SetSelection(2); break;
281 case 'b': endian->SetSelection(3); break;
283 } else {
284 unsigned j = 0;
285 //Set default disasm.
286 for(auto& i : disasms) {
287 if(i == dflt_lang)
288 break;
289 j++;
291 if(j < disasms.size())
292 type->SetSelection(j);
294 //Set default address.
295 int k = 0;
296 for(auto& i : regions) {
297 if(dflt_base >= i.second.first && dflt_base < i.second.first + i.second.second) {
298 vma->SetSelection(k + 1);
299 dflt_base -= i.second.first;
300 break;
302 k++;
304 address->SetValue(towxstring((stringfmt() << std::hex << dflt_base).str()));
306 wxCommandEvent e;
307 on_change(e);
310 void dialog_disassemble::on_ok(wxCommandEvent& e)
312 EndModal(wxID_OK);
315 std::string dialog_disassemble::get_disassembler()
317 if(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() < (ssize_t)type->GetCount()) {
318 int _endian = endian->GetSelection();
319 int dtsel = type->GetSelection() - code_types;
320 std::string _vma = tostdstring(vma->GetStringSelection());
321 if(_endian <= 0 || _endian > 3) {
322 _endian = 1;
323 lsnes_instance.iqueue.run([&_endian, _vma]() {
324 for(auto i : lsnes_instance.memory.get_regions()) {
325 if(i->name == _vma) {
326 _endian = i->endian + 2;
331 if(dtsel < 0) dtsel = 0;
332 if(dtsel > 16) dtsel = 16;
333 static const char* typechars = "bBcwWChHidDIqQrfF";
334 static const char* endianchars = " lhb";
335 std::string res = std::string("$data:") + std::string(1, typechars[dtsel]) +
336 std::string(1, endianchars[_endian]);
337 if(!has_default)
338 old_dflt_lang = res;
339 return res;
340 } else {
341 std::string res = tostdstring(type->GetStringSelection());
342 if(!has_default)
343 old_dflt_lang = res;
344 return res;
348 uint64_t dialog_disassemble::get_address()
350 uint64_t base = 0;
351 if(vma->GetSelection() && vma->GetSelection() != wxNOT_FOUND) {
352 std::string _vma = tostdstring(vma->GetStringSelection());
353 lsnes_instance.iqueue.run([&base, _vma]() {
354 for(auto i : lsnes_instance.memory.get_regions()) {
355 if(i->name == _vma) {
356 base = i->base;
361 uint64_t off = hex::from<uint64_t>(tostdstring(address->GetValue()));
362 uint64_t res = base + off;
363 if(!has_default)
364 old_dflt_base = res;
365 return res;
368 uint64_t dialog_disassemble::get_count()
370 return count->GetValue();
373 void dialog_disassemble::on_change(wxCommandEvent& e)
375 bool is_ok = true;
376 try {
377 hex::from<uint64_t>(tostdstring(address->GetValue()));
378 } catch(std::exception& e) {
379 is_ok = false;
381 is_ok = is_ok && (type->GetSelection() != wxNOT_FOUND);
382 is_ok = is_ok && (vma->GetSelection() != wxNOT_FOUND);
383 endian->Enable(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() <
384 (ssize_t)type->GetCount());
385 is_ok = is_ok && (!endian->IsEnabled() || endian->GetSelection() != wxNOT_FOUND);
386 //If VMA is global, ensure there is valid endian.
387 is_ok = is_ok && (vma->GetSelection() != 0 || !endian->IsEnabled() || endian->GetSelection() != 0);
388 ok->Enable(is_ok);
391 class wxwin_tracelog;
393 class dialog_breakpoint_add : public wxDialog
395 public:
396 dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> regions);
397 std::pair<uint64_t, debug_context::etype> get_result();
398 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
399 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
400 void on_address_change(wxCommandEvent& e);
401 private:
402 std::list<memory_region*> regions;
403 wxComboBox* vmasel;
404 wxTextCtrl* address;
405 wxComboBox* typesel;
406 wxButton* ok;
407 wxButton* cancel;
410 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow* parent, std::list<memory_region*> _regions)
411 : wxDialog(parent, wxID_ANY, wxT("Add breakpoint"))
413 regions = _regions;
414 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
415 SetSizer(top_s);
417 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Memory region:")), 0, wxGROW);
418 top_s->Add(vmasel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
419 0, NULL, wxCB_READONLY), 1, wxGROW);
420 vmasel->Append(towxstring(""));
421 for(auto i : regions)
422 vmasel->Append(towxstring(i->name));
423 vmasel->SetSelection(0);
425 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Offset (hexadecimal):")), 0, wxGROW);
426 top_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(350, -1)), 0,
427 wxGROW);
429 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Breakpoint type:")), 0, wxGROW);
430 top_s->Add(typesel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
431 0, NULL, wxCB_READONLY), 1, wxGROW);
432 typesel->Append(towxstring("Read"));
433 typesel->Append(towxstring("Write"));
434 typesel->Append(towxstring("Execute"));
435 typesel->SetSelection(0);
437 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
438 pbutton_s->AddStretchSpacer();
439 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
440 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
441 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_ok), NULL,
442 this);
443 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_cancel),
444 NULL, this);
445 top_s->Add(pbutton_s, 0, wxGROW);
446 top_s->SetSizeHints(this);
447 Fit();
450 void dialog_breakpoint_add::on_address_change(wxCommandEvent& e)
452 try {
453 hex::from<uint64_t>(tostdstring(address->GetValue()));
454 ok->Enable(true);
455 } catch(...) {
456 ok->Enable(false);
460 std::pair<uint64_t, debug_context::etype> dialog_breakpoint_add::get_result()
462 std::string vmaname = tostdstring(vmasel->GetStringSelection());
463 std::string addrtext = tostdstring(address->GetValue());
464 uint64_t base = 0;
465 if(vmaname != "") {
466 for(auto i : regions)
467 if(i->name == vmaname)
468 base = i->base;
470 uint64_t addr;
471 try {
472 addr = base + hex::from<uint64_t>(addrtext);
473 } catch(std::exception& e) {
474 addr = base;
476 debug_context::etype dtype = debug_context::DEBUG_EXEC;
477 if(typesel->GetSelection() == 0)
478 dtype = debug_context::DEBUG_READ;
479 if(typesel->GetSelection() == 1)
480 dtype = debug_context::DEBUG_WRITE;
481 if(typesel->GetSelection() == 2)
482 dtype = debug_context::DEBUG_EXEC;
483 return std::make_pair(addr, dtype);
487 class dialog_breakpoints : public wxDialog
489 public:
490 dialog_breakpoints(wxwin_tracelog* parent);
491 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
492 void on_add(wxCommandEvent& e);
493 void on_delete(wxCommandEvent& e);
494 void on_selchange(wxCommandEvent& e);
495 private:
496 std::string format_line(std::pair<uint64_t, debug_context::etype> entry);
497 size_t get_insert_pos(std::pair<uint64_t, debug_context::etype> entry);
498 void populate_breakpoints();
499 std::list<memory_region*> regions;
500 wxButton* ok;
501 wxButton* addb;
502 wxButton* delb;
503 wxListBox* brklist;
504 wxwin_tracelog* pwin;
505 std::vector<std::pair<uint64_t, debug_context::etype>> listsyms;
508 class wxwin_tracelog : public wxFrame, public debug_context::callback_base
510 public:
511 wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname);
512 ~wxwin_tracelog();
513 bool ShouldPreventAppExit() const { return false; }
514 scroll_bar* get_scroll() { return scroll; }
515 void on_wclose(wxCloseEvent& e);
516 void on_enabled(wxCommandEvent& e);
517 void on_menu(wxCommandEvent& e);
518 void process_lines();
519 uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
520 std::set<std::pair<uint64_t, debug_context::etype>> get_breakpoints();
521 void add_breakpoint(uint64_t addr, debug_context::etype dtype);
522 void remove_breakpoint(uint64_t addr, debug_context::etype dtype);
523 private:
524 class _panel : public text_framebuffer_panel
526 public:
527 _panel(wxwin_tracelog* parent);
528 void on_size(wxSizeEvent& e);
529 void on_mouse(wxMouseEvent& e);
530 wxSize DoGetBestSize() const;
531 uint64_t pos;
532 std::vector<std::string> rows;
533 void on_popup_menu(wxCommandEvent& e);
534 bool scroll_to_end_on_repaint;
535 protected:
536 void prepare_paint();
537 private:
538 uint64_t pressed_row;
539 uint64_t current_row;
540 bool holding;
541 wxwin_tracelog* p;
543 bool do_exit_save();
544 void scroll_pane(uint64_t line);
545 int cpuid;
546 volatile bool trace_active;
547 void callback(const debug_context::params& params);
548 void killed(uint64_t addr, debug_context::etype type);
549 void do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type);
550 void kill_debug_hooks();
551 scroll_bar* scroll;
552 _panel* panel;
553 bool broken;
554 bool broken2;
555 wxCheckBox* enabled;
556 threads::lock buffer_mutex;
557 std::list<std::string> lines_waiting;
558 bool unprocessed_lines;
559 bool closing;
560 bool find_active;
561 uint64_t find_line;
562 std::string find_string;
563 bool dirty;
564 bool singlestepping;
565 std::map<std::pair<uint64_t, debug_context::etype>, bool> rwx_breakpoints;
566 wxMenuItem* m_singlestep;
569 wxwin_tracelog::~wxwin_tracelog()
573 void wxwin_tracelog::on_wclose(wxCloseEvent& e)
575 if(dirty && !wxwidgets_exiting) {
576 int r = prompt_for_save(this, "Trace log");
577 if(r < 0 || (r > 0 && !do_exit_save()))
578 return;
580 if(trace_active)
581 lsnes_instance.iqueue.run([this]() { kill_debug_hooks(); });
582 trace_active = false;
583 if(!closing)
584 Destroy();
585 closing = true;
588 void wxwin_tracelog::kill_debug_hooks()
590 CORE().dbg.remove_callback(cpuid, debug_context::DEBUG_TRACE, *this);
591 CORE().dbg.remove_callback(cpuid, debug_context::DEBUG_FRAME, *this);
592 threads::alock h(buffer_mutex);
593 for(auto& i : rwx_breakpoints) {
594 if(!i.second)
595 continue;
596 CORE().dbg.remove_callback(i.first.first, i.first.second, *this);
597 i.second = false;
599 trace_active = false;
600 convert_break_to_pause();
603 wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent)
604 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL)
606 p = parent;
607 pos = 0;
608 pressed_row = 0;
609 current_row = 0;
610 holding = false;
611 scroll_to_end_on_repaint = false;
612 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
613 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
614 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
615 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
616 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
619 void wxwin_tracelog::_panel::on_size(wxSizeEvent& e)
621 wxSize newsize = e.GetSize();
622 auto tcell = get_cell();
623 size_t lines = newsize.y / tcell.second;
624 size_t linelen = newsize.x / tcell.first;
625 if(lines < 1) lines = 1;
626 if(linelen < 1) linelen = 1;
627 set_size(linelen, lines);
628 p->get_scroll()->set_page_size(lines);
629 request_paint();
630 e.Skip();
633 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent& e)
635 uint64_t local_line = pos + e.GetY() / get_cell().second;
636 if(e.RightDown()) {
637 if(local_line < rows.size()) {
638 holding = true;
639 pressed_row = local_line;
641 }else if(e.RightUp()) {
642 holding = false;
643 wxMenu menu;
644 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
645 wxCommandEventHandler(wxwin_tracelog::_panel::on_popup_menu), NULL, this);
646 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
647 menu.Append(wxID_SAVE, wxT("Save to file"));
648 menu.AppendSeparator();
649 menu.Append(wxID_DELETE, wxT("Delete"));
650 PopupMenu(&menu);
651 } else {
652 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
653 request_paint();
655 unsigned speed = 1;
656 if(e.ShiftDown())
657 speed = 10;
658 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
661 wxSize wxwin_tracelog::_panel::DoGetBestSize() const
663 return wxSize(120 * 8, 25 * 16);
666 void wxwin_tracelog::_panel::prepare_paint()
668 p->get_scroll()->set_range(rows.size());
669 if(scroll_to_end_on_repaint) {
670 scroll_to_end_on_repaint = false;
671 p->get_scroll()->set_position(rows.size());
672 pos = p->get_scroll()->get_position();
674 uint64_t m = min(pressed_row, current_row);
675 uint64_t M = max(pressed_row, current_row);
676 auto s = get_characters();
677 uint64_t fline = p->get_find_line();
678 for(uint64_t i = pos; i < pos + s.second && i < rows.size(); i++) {
679 bool selected = holding && (i >= m) && (i <= M);
680 bool isfl = (i == fline);
681 uint32_t fg = selected ? 0x0000FF : 0x000000;
682 uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
683 write(rows[i], s.first, 0, i - pos, fg, bg);
685 for(uint64_t i = rows.size(); i < pos + s.second; i++)
686 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
689 void wxwin_tracelog::process_lines()
691 threads::alock h(this->buffer_mutex);
692 size_t osize = panel->rows.size();
693 if(broken) {
694 panel->rows.push_back(std::string(120, '-'));
695 broken = false;
697 for(auto& i : lines_waiting)
698 panel->rows.push_back(i);
699 lines_waiting.clear();
700 unprocessed_lines = false;
701 if(panel->rows.size() != osize) {
702 panel->scroll_to_end_on_repaint = true;
703 dirty = true;
705 panel->request_paint();
708 void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type)
710 lsnes_instance.dbg.request_break();
713 void wxwin_tracelog::callback(const debug_context::params& p)
715 switch(p.type) {
716 case debug_context::DEBUG_READ:
717 case debug_context::DEBUG_WRITE:
718 case debug_context::DEBUG_EXEC:
719 do_rwx_break(p.rwx.addr, p.rwx.value, p.type);
720 break;
721 case debug_context::DEBUG_TRACE: {
722 if(!trace_active)
723 return;
724 //Got tracelog line, send it.
725 threads::alock h(buffer_mutex);
726 lines_waiting.push_back(p.trace.decoded_insn);
727 if(!unprocessed_lines) {
728 unprocessed_lines = true;
729 runuifun([this]() { this->process_lines(); });
731 if(singlestepping && p.trace.true_insn) {
732 lsnes_instance.dbg.request_break();
733 singlestepping = false;
735 break;
737 case debug_context::DEBUG_FRAME: {
738 std::ostringstream xstr;
739 xstr << "------------ ";
740 xstr << "Frame " << p.frame.frame;
741 if(p.frame.loadstated) xstr << " (loadstated)";
742 xstr << " ------------";
743 std::string str = xstr.str();
744 threads::alock h(buffer_mutex);
745 lines_waiting.push_back(str);
746 if(!unprocessed_lines) {
747 unprocessed_lines = true;
748 runuifun([this]() { this->process_lines(); });
750 break;
755 void wxwin_tracelog::killed(uint64_t addr, debug_context::etype type)
757 switch(type) {
758 case debug_context::DEBUG_READ:
759 case debug_context::DEBUG_WRITE:
760 case debug_context::DEBUG_EXEC: {
761 //We need to kill this hook if still active.
762 auto i2 = std::make_pair(addr, type);
763 auto& h = rwx_breakpoints[i2];
764 if(h)
765 lsnes_instance.dbg.remove_callback(addr, type, *this);
766 h = false;
767 break;
769 case debug_context::DEBUG_TRACE:
770 //Dtor!
771 if(!trace_active)
772 return;
773 kill_debug_hooks();
774 runuifun([this]() {
775 this->enabled->SetValue(false);
776 this->enabled->Enable(false);
777 this->m_singlestep->Enable(false);
779 break;
780 case debug_context::DEBUG_FRAME:
781 //Do nothing.
782 break;
786 void wxwin_tracelog::on_enabled(wxCommandEvent& e)
788 bool enable = enabled->GetValue();
789 lsnes_instance.iqueue.run([this, enable]() {
790 if(enable) {
791 threads::alock h(buffer_mutex);
792 broken = broken2;
793 broken2 = true;
794 for(auto& i : rwx_breakpoints) {
795 auto i2 = i.first;
796 lsnes_instance.dbg.add_callback(i2.first, i2.second, *this);
797 i.second = true;
799 lsnes_instance.dbg.add_callback(cpuid, debug_context::DEBUG_TRACE, *this);
800 lsnes_instance.dbg.add_callback(0, debug_context::DEBUG_FRAME, *this);
801 this->trace_active = true;
802 } else if(trace_active) {
803 this->trace_active = false;
804 this->kill_debug_hooks();
807 m_singlestep->Enable(enable);
810 bool find_match(const std::string& pattern, const std::string& candidate)
812 static std::string last_find;
813 static boost::regex regex;
814 if(pattern == "")
815 return false;
816 if(pattern[0] == 'F') {
817 //Substring find.
818 if(pattern != last_find) {
819 std::string tmp = pattern;
820 tmp = tmp.substr(1);
821 regex = boost::regex(tmp, boost::regex_constants::literal |
822 boost::regex_constants::icase);
823 last_find = pattern;
826 if(pattern[0] == 'W') {
827 //wildcard find.
828 if(pattern != last_find) {
829 std::ostringstream y;
830 for(size_t i = 1; i < pattern.length(); i++)
831 if(pattern[i] == '?')
832 y << ".";
833 else if(pattern[i] == '*')
834 y << ".*";
835 else if(pattern[i] >= 'A' && pattern[i] <= 'Z')
836 y << pattern[i];
837 else if(pattern[i] >= 'a' && pattern[i] <= 'z')
838 y << pattern[i];
839 else if(pattern[i] >= '0' && pattern[i] <= '9')
840 y << pattern[i];
841 else
842 y << "\\" << pattern[i];
843 std::string tmp = y.str();
844 regex = boost::regex(tmp, boost::regex_constants::extended);
845 last_find = pattern;
848 if(pattern[0] == 'R') {
849 //regexp find.
850 if(pattern != last_find) {
851 std::string tmp = pattern;
852 tmp = tmp.substr(1);
853 regex = boost::regex(tmp, boost::regex_constants::extended |
854 boost::regex_constants::icase);
855 last_find = pattern;
858 return regex_search(candidate, regex);
861 void wxwin_tracelog::on_menu(wxCommandEvent& e)
863 if(e.GetId() == wxID_EXIT) {
864 if(dirty) {
865 int r = prompt_for_save(this, "Trace log");
866 if(r < 0 || (r > 0 && !do_exit_save()))
867 return;
869 if(trace_active) {
870 lsnes_instance.iqueue.run([this]() { this->kill_debug_hooks(); });
872 trace_active = false;
873 Destroy();
874 return;
875 } else if(e.GetId() == wxID_SAVE) {
876 try {
877 std::string filename = choose_file_save(this, "Save tracelog to",
878 lsnes_instance.project.otherpath(), filetype_trace);
879 std::ofstream s(filename, std::ios::app);
880 if(!s) throw std::runtime_error("Error opening output file");
881 for(auto& i : panel->rows)
882 s << i << std::endl;
883 if(!s) throw std::runtime_error("Error writing output file");
884 dirty = false;
885 } catch(canceled_exception& e) {
886 } catch(std::exception& e) {
887 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
888 wxOK, this);
890 } else if(e.GetId() == wxID_FIND) {
891 std::string tmp;
892 dialog_find* d = new dialog_find(this);
893 if(d->ShowModal() != wxID_OK) {
894 d->Destroy();
895 return;
897 tmp = d->get_pattern();
898 d->Destroy();
899 if(tmp == "") {
900 find_active = false;
901 return;
903 find_string = tmp;
904 find_active = true;
905 find_line = 0;
906 while(find_line < panel->rows.size()) {
907 if(find_match(find_string, panel->rows[find_line]))
908 break;
909 find_line++;
911 if(find_line == panel->rows.size()) {
912 //Not found.
913 find_active = false;
914 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
915 wxICON_EXCLAMATION | wxOK, this);
916 } else
917 scroll_pane(find_line);
918 } else if(e.GetId() == wxID_FIND_NEXT) {
919 if(!find_active)
920 return;
921 uint64_t old_find_line = find_line;
922 find_line++;
923 while(!panel->rows.empty() && find_line != old_find_line) {
924 if(find_line >= panel->rows.size())
925 find_line = 0;
926 if(find_match(find_string, panel->rows[find_line]))
927 break;
928 find_line++;
930 scroll_pane(find_line);
931 } else if(e.GetId() == wxID_FIND_PREV) {
932 if(!find_active)
933 return;
934 uint64_t old_find_line = find_line;
935 find_line--;
936 while(!panel->rows.empty() && find_line != old_find_line) {
937 if(find_line >= panel->rows.size())
938 find_line = panel->rows.size() - 1;
939 if(find_match(find_string, panel->rows[find_line]))
940 break;
941 find_line--;
943 scroll_pane(find_line);
944 } else if(e.GetId() == wxID_SINGLESTEP) {
945 lsnes_instance.iqueue.run_async([this]() {
946 this->singlestepping = true;
947 lsnes_instance.command.invoke("unpause-emulator");
948 }, [](std::exception& e) {});
949 } else if(e.GetId() == wxID_FRAMEADVANCE) {
950 lsnes_instance.iqueue.run_async([this]() {
951 lsnes_instance.command.invoke("+advance-frame");
952 lsnes_instance.command.invoke("-advance-frame");
953 }, [](std::exception& e) {});
954 } else if(e.GetId() == wxID_CONTINUE) {
955 lsnes_instance.iqueue.run_async([this]() {
956 lsnes_instance.command.invoke("unpause-emulator");
957 }, [](std::exception& e) {});
958 } else if(e.GetId() == wxID_BREAKPOINTS) {
959 dialog_breakpoints* d = new dialog_breakpoints(this);
960 d->ShowModal();
961 d->Destroy();
962 } else if(e.GetId() == wxID_CLEAR) {
963 int r = prompt_for_save(this, "Trace log");
964 if(r < 0 || (r > 0 && !do_exit_save()))
965 return;
966 panel->rows.clear();
967 panel->request_paint();
968 find_active = false;
972 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent& e)
974 std::string str;
975 uint64_t m = min(pressed_row, current_row);
976 uint64_t M = max(pressed_row, current_row) + 1;
977 m = min(m, (uint64_t)rows.size());
978 M = min(M, (uint64_t)rows.size());
979 size_t lines = 0;
981 for(uint64_t i = m; i < M && i < rows.size(); i++) {
982 try {
983 std::string mline = rows[i];
984 if(lines == 1) str += "\n";
985 str += mline;
986 if(lines >= 1) str += "\n";
987 lines++;
988 } catch(...) {
992 switch(e.GetId()) {
993 case wxID_COPY:
994 if (wxTheClipboard->Open()) {
995 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
996 wxTheClipboard->Close();
998 break;
999 case wxID_SAVE:
1000 try {
1001 std::string filename = choose_file_save(this, "Save tracelog fragment to",
1002 lsnes_instance.project.otherpath(), filetype_trace);
1003 std::ofstream s(filename, std::ios::app);
1004 if(!s) throw std::runtime_error("Error opening output file");
1005 if(lines == 1) str += "\n";
1006 s << str;
1007 if(!s) throw std::runtime_error("Error writing output file");
1008 } catch(canceled_exception& e) {
1009 } catch(std::exception& e) {
1010 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1011 wxOK, this);
1013 break;
1014 case wxID_DELETE:
1015 rows.erase(rows.begin() + m, rows.begin() + M);
1016 if(m != M)
1017 p->dirty = true;
1018 request_paint();
1019 break;
1023 void wxwin_tracelog::scroll_pane(uint64_t line)
1025 unsigned r = panel->get_characters().second;
1026 unsigned offset = r / 2;
1027 if(offset > line)
1028 scroll->set_position(panel->pos = 0);
1029 else if(line + r <= panel->rows.size())
1030 scroll->set_position(panel->pos = line - offset);
1031 else
1032 scroll->set_position(panel->pos = panel->rows.size() - r);
1033 panel->request_paint();
1036 bool wxwin_tracelog::do_exit_save()
1038 back:
1039 try {
1040 std::string filename = choose_file_save(this, "Save tracelog to",
1041 lsnes_instance.project.otherpath(), filetype_trace);
1042 std::ofstream s(filename, std::ios::app);
1043 if(!s) throw std::runtime_error("Error opening output file");
1044 for(auto& i : panel->rows)
1045 s << i << std::endl;
1046 if(!s) throw std::runtime_error("Error writing output file");
1047 dirty = false;
1048 } catch(canceled_exception& e) {
1049 return false;
1050 } catch(std::exception& e) {
1051 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1052 wxOK, this);
1053 goto back;
1055 return true;
1058 std::set<std::pair<uint64_t, debug_context::etype>> wxwin_tracelog::get_breakpoints()
1060 std::set<std::pair<uint64_t, debug_context::etype>> ret;
1061 lsnes_instance.iqueue.run([this, &ret]() {
1062 for(auto i : rwx_breakpoints)
1063 ret.insert(i.first);
1065 return ret;
1068 void wxwin_tracelog::add_breakpoint(uint64_t addr, debug_context::etype dtype)
1070 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1071 if(!trace_active) {
1072 //We'll register this later.
1073 rwx_breakpoints[i2] = false;
1074 return;
1076 lsnes_instance.dbg.add_callback(i2.first, i2.second, *this);
1077 rwx_breakpoints[i2] = true;
1080 void wxwin_tracelog::remove_breakpoint(uint64_t addr, debug_context::etype dtype)
1082 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1083 auto& h = rwx_breakpoints[i2];
1084 if(h)
1085 lsnes_instance.dbg.remove_callback(i2.first, i2.second, *this);
1086 rwx_breakpoints.erase(i2);
1089 wxwin_tracelog::wxwin_tracelog(wxWindow* parent, int _cpuid, const std::string& cpuname)
1090 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
1091 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1092 wxCLIP_CHILDREN)
1094 cpuid = _cpuid;
1095 singlestepping = false;
1096 find_active = false;
1097 find_line = 0;
1098 closing = false;
1099 trace_active = false;
1100 unprocessed_lines = false;
1101 broken = false;
1102 broken2 = false;
1103 dirty = false;
1104 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1105 SetSizer(top_s);
1106 wxBoxSizer* bottom_s = new wxBoxSizer(wxHORIZONTAL);
1107 top_s->Add(enabled = new wxCheckBox(this, wxID_ANY, wxT("Enabled")), 0, wxGROW);
1108 bottom_s->Add(panel = new _panel(this), 1, wxGROW);
1109 bottom_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1110 top_s->Add(bottom_s, 1, wxGROW);
1111 enabled->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxwin_tracelog::on_enabled),
1112 NULL, this);
1113 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_tracelog::on_wclose),
1114 NULL, this);
1115 scroll->set_page_size(panel->get_characters().second);
1116 scroll->set_handler([this](scroll_bar& s) {
1117 this->panel->pos = s.get_position();
1118 this->panel->request_paint();
1120 wxMenuBar* mb;
1121 wxStatusBar* sb;
1122 wxMenu* menu;
1124 SetMenuBar(mb = new wxMenuBar);
1125 SetStatusBar(sb = new wxStatusBar(this));
1126 mb->Append(menu = new wxMenu(), wxT("File"));
1127 menu->Append(wxID_SAVE, wxT("Save"));
1128 menu->AppendSeparator();
1129 menu->Append(wxID_EXIT, wxT("Close"));
1130 mb->Append(menu = new wxMenu(), wxT("Edit"));
1131 menu->Append(wxID_FIND, wxT("Find..."));
1132 menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
1133 menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
1134 menu->AppendSeparator();
1135 menu->Append(wxID_CLEAR, towxstring("Clear"));
1136 mb->Append(menu = new wxMenu(), wxT("Debug"));
1137 m_singlestep = menu->Append(wxID_SINGLESTEP, towxstring("Singlestep\tF2"));
1138 menu->Append(wxID_FRAMEADVANCE, towxstring("Frame advance\tF4"));
1139 menu->Append(wxID_CONTINUE, towxstring("Continue\tF5"));
1140 menu->AppendSeparator();
1141 menu->Append(wxID_BREAKPOINTS, towxstring("Breakpoints"));
1142 m_singlestep->Enable(false);
1143 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
1144 NULL, this);
1145 //Very nasty hack.
1146 wxSize tmp = panel->GetMinSize();
1147 panel->SetMinSize(panel->DoGetBestSize());
1148 top_s->SetSizeHints(this);
1149 wxSize tmp2 = GetClientSize();
1150 panel->SetMinSize(tmp);
1151 top_s->SetSizeHints(this);
1152 SetClientSize(tmp2);
1155 struct disasm_row
1157 uint64_t cover;
1158 std::string language;
1159 std::string row;
1162 class wxwin_disassembler : public wxFrame
1164 public:
1165 wxwin_disassembler(wxWindow* parent);
1166 bool ShouldPreventAppExit() const { return false; }
1167 scroll_bar* get_scroll() { return scroll; }
1168 void on_menu(wxCommandEvent& e);
1169 void on_wclose(wxCloseEvent& e);
1170 private:
1171 class _panel : public text_framebuffer_panel
1173 public:
1174 _panel(wxwin_disassembler* parent);
1175 void on_size(wxSizeEvent& e);
1176 void on_mouse(wxMouseEvent& e);
1177 wxSize DoGetBestSize() const;
1178 uint64_t pos;
1179 std::vector<uint64_t> rows;
1180 std::map<uint64_t, disasm_row> row_map;
1181 void on_popup_menu(wxCommandEvent& e);
1182 protected:
1183 void prepare_paint();
1184 private:
1185 uint64_t pressed_row;
1186 uint64_t current_row;
1187 bool holding;
1188 wxwin_disassembler* p;
1190 bool do_exit_save();
1191 void add_row(uint64_t addr, const disasm_row& row, bool last);
1192 void add_rows(const std::map<uint64_t, disasm_row>& rowdata);
1193 void add_rows_main(const std::map<uint64_t, disasm_row>& rowdata);
1194 void run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count);
1195 void scroll_pane(uint64_t line);
1196 scroll_bar* scroll;
1197 _panel* panel;
1198 bool dirty;
1199 bool closing;
1202 wxwin_disassembler::_panel::_panel(wxwin_disassembler* parent)
1203 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL)
1205 p = parent;
1206 pos = 0;
1207 pressed_row = 0;
1208 current_row = 0;
1209 holding = false;
1210 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_disassembler::_panel::on_size), NULL, this);
1211 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1212 this);
1213 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1214 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1215 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1216 this);
1219 void wxwin_disassembler::_panel::on_size(wxSizeEvent& e)
1221 wxSize newsize = e.GetSize();
1222 auto tcell = get_cell();
1223 size_t lines = newsize.y / tcell.second;
1224 size_t linelen = newsize.x / tcell.first;
1225 if(lines < 1) lines = 1;
1226 if(linelen < 1) linelen = 1;
1227 set_size(linelen, lines);
1228 p->get_scroll()->set_page_size(lines);
1229 request_paint();
1230 e.Skip();
1233 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent& e)
1235 uint64_t local_line = pos + e.GetY() / get_cell().second;
1236 if(e.RightDown()) {
1237 if(local_line < rows.size()) {
1238 holding = true;
1239 pressed_row = local_line;
1241 }else if(e.RightUp()) {
1242 holding = false;
1243 wxMenu menu;
1244 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
1245 wxCommandEventHandler(wxwin_disassembler::_panel::on_popup_menu), NULL, this);
1246 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
1247 menu.Append(wxID_SAVE, wxT("Save to file"));
1248 menu.AppendSeparator();
1249 menu.Append(wxID_DISASM_MORE, wxT("Disassemble more"));
1250 menu.AppendSeparator();
1251 menu.Append(wxID_DELETE, wxT("Delete"));
1252 PopupMenu(&menu);
1253 } else {
1254 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
1255 request_paint();
1257 unsigned speed = 1;
1258 if(e.ShiftDown())
1259 speed = 10;
1260 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
1263 wxSize wxwin_disassembler::_panel::DoGetBestSize() const
1265 return wxSize(40 * 8, 25 * 16);
1268 void wxwin_disassembler::_panel::prepare_paint()
1270 p->get_scroll()->set_range(rows.size());
1271 uint64_t m = min(pressed_row, current_row);
1272 uint64_t M = max(pressed_row, current_row);
1273 auto s = get_characters();
1274 uint64_t i;
1275 for(i = pos; i < pos + s.second && i < rows.size(); i++) {
1276 bool selected = holding && (i >= m) && (i <= M);
1277 uint32_t fg = selected ? 0x0000FF : 0x000000;
1278 uint32_t bg = selected ? 0x000000 : 0xFFFFFF;
1279 write(row_map[rows[i]].row, s.first, 0, i - pos, fg, bg);
1281 for(; i < pos + s.second; i++) {
1282 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
1286 void wxwin_disassembler::on_menu(wxCommandEvent& e)
1288 if(e.GetId() == wxID_EXIT) {
1289 if(dirty) {
1290 int r = prompt_for_save(this, "Disassembly");
1291 if(r < 0 || (r > 0 && !do_exit_save()))
1292 return;
1294 Destroy();
1295 return;
1296 } else if(e.GetId() == wxID_SAVE) {
1297 try {
1298 std::string filename = choose_file_save(this, "Save disassembly to",
1299 lsnes_instance.project.otherpath(), filetype_disassembly);
1300 std::ofstream s(filename, std::ios::app);
1301 if(!s) throw std::runtime_error("Error opening output file");
1302 for(auto& i : panel->rows)
1303 s << panel->row_map[i].row << std::endl;
1304 if(!s) throw std::runtime_error("Error writing output file");
1305 dirty = false;
1306 } catch(canceled_exception& e) {
1307 } catch(std::exception& e) {
1308 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1309 wxOK, this);
1311 } else if(e.GetId() == wxID_DISASM) {
1312 std::string tmp;
1313 dialog_disassemble* d = new dialog_disassemble(this);
1314 if(d->ShowModal() != wxID_OK) {
1315 d->Destroy();
1316 return;
1318 std::string disasm = d->get_disassembler();
1319 uint64_t addr = d->get_address();
1320 uint64_t count = d->get_count();
1321 d->Destroy();
1322 lsnes_instance.iqueue.run_async([this, disasm, addr, count]() {
1323 this->run_disassembler(disasm, addr, count);
1324 }, [](std::exception& e) {});
1325 } else if(e.GetId() == wxID_GOTO) {
1326 try {
1327 std::string to = pick_text(this, "Goto", "Enter address to go to:", "");
1328 lsnes_instance.iqueue.run_async([this, to]() {
1329 uint64_t addr;
1330 uint64_t base = 0;
1331 std::string vma;
1332 std::string offset;
1333 std::string _to = to;
1334 size_t sp = _to.find_first_of("+");
1335 if(sp >= _to.length()) {
1336 offset = _to;
1337 } else {
1338 vma = _to.substr(0, sp);
1339 offset = _to.substr(sp + 1);
1341 if(vma != "") {
1342 bool found = false;
1343 for(auto i : lsnes_instance.memory.get_regions()) {
1344 if(i->name == vma) {
1345 base = i->base;
1346 found = true;
1349 if(!found) {
1350 runuifun([this] {
1351 show_message_ok(this, "Error in address",
1352 "No such memory area known",
1353 wxICON_EXCLAMATION);
1355 return;
1358 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1359 " <name>+<hexdigits>", [&addr, offset]() {
1360 addr = hex::from<uint64_t>(offset); }))
1361 return;
1362 addr += base;
1363 runuifun([this, addr]() {
1364 uint64_t nrow = 0;
1365 uint64_t low = 0;
1366 uint64_t high = this->panel->rows.size();
1367 while(low < high && low < high - 1) {
1368 nrow = (low + high) / 2;
1369 if(this->panel->rows[nrow] > addr)
1370 high = nrow;
1371 else if(this->panel->rows[nrow] < addr)
1372 low = nrow;
1373 else
1374 break;
1376 this->scroll_pane(nrow);
1378 }, [](std::exception& e) {});
1379 } catch(canceled_exception& e) {
1384 void remove_from_array(std::vector<uint64_t>& v, uint64_t e)
1386 //Binary search for the element to remove.
1387 size_t low = 0;
1388 size_t high = v.size();
1389 size_t mid = 0;
1390 while(low < high) {
1391 mid = (low + high) / 2;
1392 if(v[mid] < e)
1393 low = mid;
1394 else if(v[mid] > e)
1395 high = mid;
1396 else
1397 break;
1399 if(v[mid] == e)
1400 v.erase(v.begin() + mid);
1403 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent& e)
1405 if(e.GetId() == wxID_DISASM_MORE)
1407 if(current_row >= rows.size())
1408 return;
1409 uint64_t base = rows[current_row];
1410 uint64_t rbase = base;
1411 if(!row_map.count(base))
1412 return;
1413 auto& r = row_map[base];
1414 base = base + r.cover;
1415 std::string disasm = r.language;
1416 dialog_disassemble* d = new dialog_disassemble(this, base, disasm);
1417 if(d->ShowModal() != wxID_OK) {
1418 d->Destroy();
1419 return;
1421 disasm = d->get_disassembler();
1422 uint64_t addr = d->get_address();
1423 uint64_t count = d->get_count();
1424 d->Destroy();
1425 auto pp = p;
1426 lsnes_instance.iqueue.run_async([pp, disasm, addr, count]() {
1427 pp->run_disassembler(disasm, addr, count);
1428 }, [](std::exception& e) {});
1429 //Delete entries in (rbase, addr) if addr = base.
1430 if(addr == base) {
1431 for(uint64_t i = rbase + 1; i < addr; i++)
1432 if(row_map.count(i)) {
1433 //This line needs to be removed from rows too.
1434 row_map.erase(i);
1435 remove_from_array(rows, i);
1439 std::string str;
1440 uint64_t m = min(min(pressed_row, current_row), (uint64_t)rows.size());
1441 uint64_t M = min(max(pressed_row, current_row) + 1, (uint64_t)rows.size());
1442 size_t lines = 0;
1444 for(uint64_t i = m; i < M; i++) {
1445 try {
1446 std::string mline = row_map[rows[i]].row;
1447 if(lines == 1) str += "\n";
1448 str += mline;
1449 if(lines >= 1) str += "\n";
1450 lines++;
1451 } catch(...) {
1455 switch(e.GetId()) {
1456 case wxID_COPY:
1457 if (wxTheClipboard->Open()) {
1458 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
1459 wxTheClipboard->Close();
1461 break;
1462 case wxID_SAVE:
1463 try {
1464 std::string filename = choose_file_save(this, "Save disassembly fragment to",
1465 lsnes_instance.project.otherpath(), filetype_disassembly);
1466 std::ofstream s(filename, std::ios::app);
1467 if(!s) throw std::runtime_error("Error opening output file");
1468 if(lines == 1) str += "\n";
1469 s << str;
1470 if(!s) throw std::runtime_error("Error writing output file");
1471 } catch(canceled_exception& e) {
1472 } catch(std::exception& e) {
1473 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1474 wxOK, this);
1476 break;
1477 case wxID_DELETE:
1478 for(uint64_t i = m; i < M; i++)
1479 row_map.erase(rows[i]);
1480 rows.erase(rows.begin() + m, rows.begin() + M);
1481 if(m != M)
1482 p->dirty = true;
1483 request_paint();
1484 break;
1488 std::string format_vma_offset(memory_region& region, uint64_t offset)
1490 std::ostringstream y;
1491 y << region.name;
1492 size_t sizedigits = 0;
1493 uint64_t tmp = region.size - 1;
1494 while(tmp > 0) {
1495 tmp >>= 4;
1496 sizedigits++;
1498 y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << offset;
1499 return y.str();
1502 std::string lookup_address(uint64_t raw)
1504 auto g = lsnes_instance.memory.lookup(raw);
1505 if(!g.first)
1506 return hex::to<uint64_t>(raw);
1507 else
1508 return format_vma_offset(*g.first, g.second);
1511 inline int sign_compare(uint64_t a, uint64_t b)
1513 if(a < b) return -1;
1514 if(b < a) return 1;
1515 return 0;
1518 void insert_into_array(std::vector<uint64_t>& v, uint64_t e)
1520 //Binary search for the gap to insert to.
1521 size_t low = 0;
1522 size_t high = v.size();
1523 size_t mid = 0;
1524 while(low < high) {
1525 mid = (low + high) / 2;
1526 int s1 = sign_compare(v[mid], e);
1527 int s2 = ((mid + 1) < v.size()) ? sign_compare(v[mid + 1], e) : 1;
1528 if(s1 < 0 && s2 > 0)
1529 break;
1530 else if(s1 == 0 || s2 == 0)
1531 return;
1532 else if(s1 > 0)
1533 high = mid;
1534 else if(s2 < 0)
1535 low = mid;
1537 if(mid < v.size() && v[mid] < e)
1538 mid++;
1539 v.insert(v.begin() + mid, e);
1542 void wxwin_disassembler::add_row(uint64_t addr, const disasm_row& row, bool last)
1544 auto& rows = panel->rows;
1545 auto& row_map = panel->row_map;
1546 if(row_map.count(addr)) {
1547 row_map[addr] = row;
1548 } else {
1549 //We need to insert the row into rows.
1550 row_map[addr] = row;
1551 insert_into_array(rows, addr);
1553 dirty = true;
1554 if(!last)
1555 for(uint64_t i = addr + 1; i < addr + row.cover; i++)
1556 if(row_map.count(i)) {
1557 //This line needs to be removed from rows too.
1558 row_map.erase(i);
1559 remove_from_array(rows, i);
1563 void wxwin_disassembler::add_rows(const std::map<uint64_t, disasm_row>& rowdata)
1565 for(auto i = rowdata.begin(); i != rowdata.end(); i++) {
1566 auto j = i;
1567 j++;
1568 bool last = (j == rowdata.end());
1569 add_row(i->first, i->second, last);
1571 panel->request_paint();
1574 void wxwin_disassembler::add_rows_main(const std::map<uint64_t, disasm_row>& rowdata)
1576 std::map<uint64_t, disasm_row> _rowdata;
1577 for(auto& i : rowdata) {
1578 _rowdata[i.first] = i.second;
1579 _rowdata[i.first].row = lookup_address(i.first) + " " + i.second.row;
1581 runuifun([this, _rowdata]() { this->add_rows(_rowdata); });
1584 template<typename T, bool hex> disasm_row _disassemble_data_item(uint64_t& addrbase, int endian,
1585 const std::string& disasm)
1587 char buf[sizeof(T)];
1588 for(size_t i = 0; i < sizeof(T); i++)
1589 buf[i] = lsnes_instance.memory.read<uint8_t>(addrbase + i);
1590 disasm_row r;
1591 if(hex)
1592 r.row = (stringfmt() << "DATA 0x" << hex::to<T>(serialization::read_endian<T>(buf, endian))).
1593 str();
1594 else if(sizeof(T) > 1)
1595 r.row = (stringfmt() << "DATA " << serialization::read_endian<T>(buf, endian)).str();
1596 else
1597 r.row = (stringfmt() << "DATA " << (int)serialization::read_endian<T>(buf, endian)).str();
1598 r.cover = sizeof(T);
1599 r.language = disasm;
1600 addrbase += sizeof(T);
1601 return r;
1604 disasm_row disassemble_data_item(uint64_t& addrbase, const std::string& disasm)
1606 int endian;
1607 if(disasm[7] == 'l') endian = -1;
1608 if(disasm[7] == 'h') endian = 0;
1609 if(disasm[7] == 'b') endian = 1;
1610 switch(disasm[6]) {
1611 case 'b': return _disassemble_data_item<int8_t, false>(addrbase, endian, disasm);
1612 case 'B': return _disassemble_data_item<uint8_t, false>(addrbase, endian, disasm);
1613 case 'c': return _disassemble_data_item<uint8_t, true>(addrbase, endian, disasm);
1614 case 'C': return _disassemble_data_item<uint16_t, true>(addrbase, endian, disasm);
1615 case 'd': return _disassemble_data_item<int32_t, false>(addrbase, endian, disasm);
1616 case 'D': return _disassemble_data_item<uint32_t, false>(addrbase, endian, disasm);
1617 case 'f': return _disassemble_data_item<float, false>(addrbase, endian, disasm);
1618 case 'F': return _disassemble_data_item<double, false>(addrbase, endian, disasm);
1619 case 'h': return _disassemble_data_item<ss_int24_t, false>(addrbase, endian, disasm);
1620 case 'H': return _disassemble_data_item<ss_uint24_t, false>(addrbase, endian, disasm);
1621 case 'i': return _disassemble_data_item<ss_uint24_t, true>(addrbase, endian, disasm);
1622 case 'I': return _disassemble_data_item<uint32_t, true>(addrbase, endian, disasm);
1623 case 'q': return _disassemble_data_item<int64_t, false>(addrbase, endian, disasm);
1624 case 'Q': return _disassemble_data_item<uint64_t, false>(addrbase, endian, disasm);
1625 case 'r': return _disassemble_data_item<uint64_t, true>(addrbase, endian, disasm);
1626 case 'w': return _disassemble_data_item<int16_t, false>(addrbase, endian, disasm);
1627 case 'W': return _disassemble_data_item<uint16_t, false>(addrbase, endian, disasm);
1629 throw std::runtime_error("Invalid kind of data");
1632 void wxwin_disassembler::scroll_pane(uint64_t line)
1634 unsigned r = panel->get_characters().second;
1635 unsigned offset = r / 2;
1636 if(offset > line)
1637 scroll->set_position(panel->pos = 0);
1638 else if(line + r < panel->rows.size())
1639 scroll->set_position(panel->pos = line - offset);
1640 else
1641 scroll->set_position(panel->pos = panel->rows.size() - r);
1642 panel->request_paint();
1645 void wxwin_disassembler::on_wclose(wxCloseEvent& e)
1647 if(dirty && !wxwidgets_exiting) {
1648 int r = prompt_for_save(this, "Disassembly");
1649 if(r < 0 || (r > 0 && !do_exit_save()))
1650 return;
1652 if(!closing)
1653 Destroy();
1654 closing = true;
1657 void wxwin_disassembler::run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count)
1659 std::map<uint64_t, disasm_row> rowdata;
1660 if(regex_match("\\$data:.*", disasm)) {
1661 if(run_show_error(this, "Error in disassember", "Error in disassember",
1662 [disasm, &rowdata, &addrbase, count]() {
1663 for(uint64_t i = 0; i < count; i++) {
1664 uint64_t base = addrbase;
1665 disasm_row r = disassemble_data_item(addrbase, disasm);
1666 rowdata[base] = r;
1669 return;
1670 add_rows_main(rowdata);
1671 return;
1673 disassembler* d;
1674 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm + "' found",
1675 [&d, disasm]() {
1676 d = &disassembler::byname(disasm);
1678 return;
1679 for(uint64_t i = 0; i < count; i++) {
1680 uint64_t base = addrbase;
1681 disasm_row r;
1682 r.row = d->disassemble(addrbase, [&addrbase]() -> unsigned char {
1683 return lsnes_instance.memory.read<uint8_t>(addrbase++);
1685 r.cover = addrbase - base;
1686 r.language = disasm;
1687 rowdata[base] = r;
1689 add_rows_main(rowdata);
1692 bool wxwin_disassembler::do_exit_save()
1694 back:
1695 try {
1696 std::string filename = choose_file_save(this, "Save disassembly to",
1697 lsnes_instance.project.otherpath(), filetype_disassembly);
1698 std::ofstream s(filename, std::ios::app);
1699 if(!s) throw std::runtime_error("Error opening output file");
1700 for(auto& i : panel->rows)
1701 s << panel->row_map[i].row << std::endl;
1702 if(!s) throw std::runtime_error("Error writing output file");
1703 dirty = false;
1704 } catch(canceled_exception& e) {
1705 return false;
1706 } catch(std::exception& e) {
1707 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1708 wxOK, this);
1709 goto back;
1711 return true;
1714 wxwin_disassembler::wxwin_disassembler(wxWindow* parent)
1715 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Disassembler"), wxDefaultPosition,
1716 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1717 wxCLIP_CHILDREN)
1719 closing = false;
1720 dirty = false;
1721 wxBoxSizer* top_s = new wxBoxSizer(wxHORIZONTAL);
1722 SetSizer(top_s);
1723 top_s->Add(panel = new _panel(this), 1, wxGROW);
1724 top_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1725 scroll->set_page_size(panel->get_characters().second);
1726 scroll->set_handler([this](scroll_bar& s) {
1727 this->panel->pos = s.get_position();
1728 this->panel->request_paint();
1730 wxMenuBar* mb;
1731 wxStatusBar* sb;
1732 wxMenu* menu;
1734 SetMenuBar(mb = new wxMenuBar);
1735 SetStatusBar(sb = new wxStatusBar(this));
1736 mb->Append(menu = new wxMenu(), wxT("File"));
1737 menu->Append(wxID_DISASM, wxT("Disassemble..."));
1738 menu->AppendSeparator();
1739 menu->Append(wxID_SAVE, wxT("Save"));
1740 menu->AppendSeparator();
1741 menu->Append(wxID_EXIT, wxT("Close"));
1742 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_disassembler::on_menu),
1743 NULL, this);
1744 mb->Append(menu = new wxMenu(), wxT("Edit"));
1745 menu->Append(wxID_GOTO, wxT("Goto"));
1747 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_disassembler::on_wclose),
1748 NULL, this);
1749 //Very nasty hack.
1750 wxSize tmp = panel->GetMinSize();
1751 panel->SetMinSize(panel->DoGetBestSize());
1752 top_s->SetSizeHints(this);
1753 wxSize tmp2 = GetClientSize();
1754 panel->SetMinSize(tmp);
1755 top_s->SetSizeHints(this);
1756 SetClientSize(tmp2);
1759 dialog_breakpoints::dialog_breakpoints(wxwin_tracelog* parent)
1760 : wxDialog(parent, wxID_ANY, wxT("Breakpoints"))
1762 pwin = parent;
1763 regions = lsnes_instance.memory.get_regions();
1764 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1765 SetSizer(top_s);
1766 top_s->Add(brklist = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400)), 1, wxGROW);
1767 brklist->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
1768 wxCommandEventHandler(dialog_breakpoints::on_selchange), NULL, this);
1769 populate_breakpoints();
1770 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1771 pbutton_s->Add(addb = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
1772 pbutton_s->Add(delb = new wxButton(this, wxID_ANY, wxT("Remove")), 0, wxGROW);
1773 pbutton_s->AddStretchSpacer();
1774 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
1775 delb->Enable(false);
1776 addb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_add), NULL,
1777 this);
1778 delb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_delete),
1779 NULL, this);
1780 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_ok), NULL,
1781 this);
1782 top_s->Add(pbutton_s, 0, wxGROW);
1783 top_s->SetSizeHints(this);
1784 Fit();
1787 void dialog_breakpoints::populate_breakpoints()
1789 auto t = pwin->get_breakpoints();
1790 for(auto i : t) {
1791 std::string line = format_line(i);
1792 unsigned insert_pos = get_insert_pos(i);
1793 brklist->Insert(towxstring(line), insert_pos);
1794 listsyms.insert(listsyms.begin() + insert_pos, i);
1798 void dialog_breakpoints::on_add(wxCommandEvent& e)
1800 uint64_t addr;
1801 debug_context::etype dtype;
1802 dialog_breakpoint_add* d = new dialog_breakpoint_add(this, regions);
1803 if(d->ShowModal() != wxID_OK) {
1804 d->Destroy();
1805 return;
1807 rpair(addr, dtype) = d->get_result();
1808 d->Destroy();
1809 lsnes_instance.iqueue.run_async([this, addr, dtype]() {
1810 pwin->add_breakpoint(addr, dtype);
1811 }, [](std::exception& e) {});
1812 auto ent = std::make_pair(addr, dtype);
1813 std::string line = format_line(ent);
1814 unsigned insert_pos = get_insert_pos(ent);
1815 brklist->Insert(towxstring(line), insert_pos);
1816 listsyms.insert(listsyms.begin() + insert_pos, ent);
1819 void dialog_breakpoints::on_delete(wxCommandEvent& e)
1821 int idx = brklist->GetSelection();
1822 if(idx == wxNOT_FOUND)
1823 return;
1824 uint64_t addr;
1825 debug_context::etype dtype;
1826 addr = listsyms[idx].first;
1827 dtype = listsyms[idx].second;
1828 lsnes_instance.iqueue.run_async([this, addr, dtype]() {
1829 pwin->remove_breakpoint(addr, dtype);
1830 }, [](std::exception& e) {});
1831 brklist->Delete(idx);
1832 listsyms.erase(listsyms.begin() + idx);
1835 size_t dialog_breakpoints::get_insert_pos(std::pair<uint64_t, debug_context::etype> entry)
1837 size_t i = 0;
1838 for(i = 0; i < listsyms.size(); i++)
1839 if(entry < listsyms[i])
1840 return i;
1841 return i;
1844 std::string dialog_breakpoints::format_line(std::pair<uint64_t, debug_context::etype> entry)
1846 std::string base = "";
1847 for(auto i : regions) {
1848 if(entry.first >= i->base && entry.first < i->base + i->size) {
1849 base = format_vma_offset(*i, entry.first - i->base);
1850 break;
1853 if(base == "")
1854 base = hex::to<uint64_t>(entry.first);
1855 if(entry.second == debug_context::DEBUG_READ)
1856 return base + ": Read";
1857 if(entry.second == debug_context::DEBUG_WRITE)
1858 return base + ": Write";
1859 if(entry.second == debug_context::DEBUG_EXEC)
1860 return base + ": Execute";
1861 return base + ": Unknown";
1864 void dialog_breakpoints::on_selchange(wxCommandEvent& e)
1866 delb->Enable(brklist->GetSelection() != wxNOT_FOUND);
1870 void wxeditor_tracelog_display(wxWindow* parent, int cpuid, const std::string& cpuname)
1872 try {
1873 wxwin_tracelog* d = new wxwin_tracelog(parent, cpuid, cpuname);
1874 d->Show();
1875 } catch(std::exception& e) {
1876 show_message_ok(parent, "Error opening trace logger", e.what(), wxICON_EXCLAMATION);
1880 void wxeditor_disassembler_display(wxWindow* parent)
1882 wxwin_disassembler* d = new wxwin_disassembler(parent);
1883 d->Show();