Actually call on_reset callback
[lsnes.git] / src / platform / wxwidgets / tracelogger.cpp
bloba49738926b41fdcfd5615859a9336089eeaf6c69
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 <set>
39 namespace
41 enum
43 wxID_FIND_NEXT = wxID_HIGHEST + 1,
44 wxID_FIND_PREV,
45 wxID_GOTO,
46 wxID_DISASM,
47 wxID_DISASM_MORE,
48 wxID_SINGLESTEP,
49 wxID_BREAKPOINTS,
50 wxID_CONTINUE,
51 wxID_FRAMEADVANCE,
52 wxID_CLEAR,
55 int prompt_for_save(wxWindow* parent, const std::string& what)
57 CHECK_UI_THREAD;
58 wxMessageDialog* d = new wxMessageDialog(parent, towxstring(what + " has unsaved changes, "
59 "save before closing?"), towxstring("Save on exit?"), wxCENTER | wxYES_NO | wxCANCEL |
60 wxYES_DEFAULT);
61 d->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
62 int r = d->ShowModal();
63 d->Destroy();
64 if(r == wxID_YES) return 1;
65 if(r == wxID_NO) return 0;
66 if(r == wxID_CANCEL) return -1;
67 return -1;
70 class dialog_find : public wxDialog
72 public:
73 dialog_find(wxWindow* parent);
74 std::string get_pattern();
75 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
76 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
77 private:
78 wxTextCtrl* text;
79 wxComboBox* type;
80 wxButton* ok;
81 wxButton* cancel;
84 dialog_find::dialog_find(wxWindow* parent)
85 : wxDialog(parent, wxID_ANY, wxT("Find"))
87 CHECK_UI_THREAD;
88 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
89 SetSizer(top_s);
90 wxBoxSizer* t_s = new wxBoxSizer(wxHORIZONTAL);
91 t_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
92 0, NULL, wxCB_READONLY), 1, wxGROW);
93 type->Append(towxstring("Literal"));
94 type->Append(towxstring("Wildcards"));
95 type->Append(towxstring("Regexp"));
96 type->SetSelection(0);
97 t_s->Add(text = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(350, -1),
98 wxTE_PROCESS_ENTER), 0, wxGROW);
99 top_s->Add(t_s, 1, wxGROW);
100 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
101 pbutton_s->AddStretchSpacer();
102 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
103 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
104 text->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
105 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_ok), NULL, this);
106 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_find::on_cancel), NULL,
107 this);
108 top_s->Add(pbutton_s, 0, wxGROW);
109 top_s->SetSizeHints(this);
110 Fit();
113 std::string dialog_find::get_pattern()
115 CHECK_UI_THREAD;
116 if(tostdstring(text->GetValue()) == "")
117 return "";
118 if(type->GetSelection() == 2)
119 return "R" + tostdstring(text->GetValue());
120 else if(type->GetSelection() == 1)
121 return "W" + tostdstring(text->GetValue());
122 else
123 return "F" + tostdstring(text->GetValue());
126 class dialog_disassemble : public wxDialog
128 public:
129 dialog_disassemble(wxWindow* parent, emulator_instance& _inst);
130 dialog_disassemble(wxWindow* parent, emulator_instance& _inst, uint64_t dflt_base,
131 const std::string& dflt_lang);
132 std::string get_disassembler();
133 uint64_t get_address();
134 uint64_t get_count();
135 void on_change(wxCommandEvent& e);
136 void on_ok(wxCommandEvent& e);
137 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
138 private:
139 void init(bool spec, uint64_t dflt_base, std::string dflt_lang);
140 emulator_instance& inst;
141 wxComboBox* type;
142 wxComboBox* endian;
143 wxComboBox* vma;
144 wxTextCtrl* address;
145 wxSpinCtrl* count;
146 wxButton* ok;
147 wxButton* cancel;
148 bool has_default;
149 unsigned code_types;
150 static std::string old_dflt_lang;
151 static uint64_t old_dflt_base;
154 std::string dialog_disassemble::old_dflt_lang;
155 uint64_t dialog_disassemble::old_dflt_base;
157 dialog_disassemble::dialog_disassemble(wxWindow* parent, emulator_instance& _inst)
158 : wxDialog(parent, wxID_ANY, wxT("Disassemble region")), inst(_inst)
160 CHECK_UI_THREAD;
161 init(false, 0, "");
164 dialog_disassemble::dialog_disassemble(wxWindow* parent, emulator_instance& _inst, uint64_t dflt_base,
165 const std::string& dflt_lang)
166 : wxDialog(parent, wxID_ANY, wxT("Disassemble region")), inst(_inst)
168 CHECK_UI_THREAD;
169 init(true, dflt_base, dflt_lang);
172 void dialog_disassemble::init(bool spec, uint64_t dflt_base, std::string dflt_lang)
174 CHECK_UI_THREAD;
175 std::map<std::string, std::pair<uint64_t, uint64_t>> regions;
176 std::set<std::string> disasms;
177 inst.iqueue->run([&regions, &disasms]() {
178 for(auto i : CORE().memory->get_regions())
179 regions[i->name] = std::make_pair(i->base, i->size);
180 disasms = disassembler::list();
183 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
184 SetSizer(top_s);
186 wxBoxSizer* type_s = new wxBoxSizer(wxHORIZONTAL);
187 type_s->Add(new wxStaticText(this, wxID_ANY, wxT("Language:")), 0, wxGROW);
188 type_s->Add(type = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
189 0, NULL, wxCB_READONLY), 0, wxGROW);
190 for(auto& i : disasms)
191 type->Append(towxstring(i));
192 code_types = type->GetCount();
193 type->Append(towxstring("Data (signed byte)"));
194 type->Append(towxstring("Data (unsigned byte)"));
195 type->Append(towxstring("Data (hex byte)"));
196 type->Append(towxstring("Data (signed word)"));
197 type->Append(towxstring("Data (unsigned word)"));
198 type->Append(towxstring("Data (hex word)"));
199 type->Append(towxstring("Data (signed onehalfword)"));
200 type->Append(towxstring("Data (unsigned onehalfword)"));
201 type->Append(towxstring("Data (hex onehalfword)"));
202 type->Append(towxstring("Data (signed doubleword)"));
203 type->Append(towxstring("Data (unsigned doubleword)"));
204 type->Append(towxstring("Data (hex doubleword)"));
205 type->Append(towxstring("Data (signed quadword)"));
206 type->Append(towxstring("Data (unsigned quadword)"));
207 type->Append(towxstring("Data (hex quadword)"));
208 type->Append(towxstring("Data (float)"));
209 type->Append(towxstring("Data (double)"));
210 type->SetSelection(0);
211 type->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
212 NULL, this);
213 top_s->Add(type_s, 0, wxGROW);
215 wxBoxSizer* endian_s = new wxBoxSizer(wxHORIZONTAL);
216 endian_s->Add(new wxStaticText(this, wxID_ANY, wxT("Endian:")), 0, wxGROW);
217 endian_s->Add(endian = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
218 0, NULL, wxCB_READONLY), 0, wxGROW);
219 endian->Append(towxstring("(Memory area default)"));
220 endian->Append(towxstring("Little-endian"));
221 endian->Append(towxstring("Host-endian"));
222 endian->Append(towxstring("Big-endian"));
223 endian->SetSelection(0);
224 endian->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
225 NULL, this);
226 top_s->Add(endian_s, 0, wxGROW);
228 wxBoxSizer* vma_s = new wxBoxSizer(wxHORIZONTAL);
229 vma_s->Add(new wxStaticText(this, wxID_ANY, wxT("Area:")), 0, wxGROW);
230 vma_s->Add(vma = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
231 0, NULL, wxCB_READONLY), 0, wxGROW);
232 vma->Append(towxstring("(Any)"));
233 for(auto& i : regions)
234 vma->Append(towxstring(i.first));
235 vma->SetSelection(0);
236 vma->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(dialog_disassemble::on_change),
237 NULL, this);
238 top_s->Add(vma_s, 0, wxGROW);
240 wxBoxSizer* addr_s = new wxBoxSizer(wxHORIZONTAL);
241 addr_s->Add(new wxStaticText(this, wxID_ANY, wxT("Address:")), 0, wxGROW);
242 addr_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(200, -1)),
243 0, wxGROW);
244 address->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(dialog_disassemble::on_change),
245 NULL, this);
246 top_s->Add(addr_s, 0, wxGROW);
248 wxBoxSizer* cnt_s = new wxBoxSizer(wxHORIZONTAL);
249 cnt_s->Add(new wxStaticText(this, wxID_ANY, wxT("Count:")), 0, wxGROW);
250 cnt_s->Add(count = new wxSpinCtrl(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
251 wxSP_ARROW_KEYS, 1, 1000000000, 10), 0, wxGROW);
252 count->Connect(wxEVT_SPINCTRL, wxCommandEventHandler(dialog_disassemble::on_change), NULL,
253 this);
254 top_s->Add(cnt_s, 0, wxGROW);
256 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
257 pbutton_s->AddStretchSpacer();
258 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
259 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
260 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_ok), NULL,
261 this);
262 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_disassemble::on_cancel),
263 NULL, this);
264 top_s->Add(pbutton_s, 0, wxGROW);
265 top_s->SetSizeHints(this);
266 Fit();
268 has_default = spec;
269 if(!spec) {
270 dflt_lang = old_dflt_lang;
271 dflt_base = old_dflt_base;
273 //Set default language.
274 if(regex_match("\\$data:.*", dflt_lang)) {
275 switch(dflt_lang[6]) {
276 case 'b': type->SetSelection(code_types + 0); break;
277 case 'B': type->SetSelection(code_types + 1); break;
278 case 'c': type->SetSelection(code_types + 2); break;
279 case 'w': type->SetSelection(code_types + 3); break;
280 case 'W': type->SetSelection(code_types + 4); break;
281 case 'C': type->SetSelection(code_types + 5); break;
282 case 'h': type->SetSelection(code_types + 6); break;
283 case 'H': type->SetSelection(code_types + 7); break;
284 case 'i': type->SetSelection(code_types + 8); break;
285 case 'd': type->SetSelection(code_types + 9); break;
286 case 'D': type->SetSelection(code_types + 10); break;
287 case 'I': type->SetSelection(code_types + 11); break;
288 case 'q': type->SetSelection(code_types + 12); break;
289 case 'Q': type->SetSelection(code_types + 13); break;
290 case 'r': type->SetSelection(code_types + 14); break;
291 case 'f': type->SetSelection(code_types + 15); break;
292 case 'F': type->SetSelection(code_types + 16); break;
294 switch(dflt_lang[7]) {
295 case 'l': endian->SetSelection(1); break;
296 case 'h': endian->SetSelection(2); break;
297 case 'b': endian->SetSelection(3); break;
299 } else {
300 unsigned j = 0;
301 //Set default disasm.
302 for(auto& i : disasms) {
303 if(i == dflt_lang)
304 break;
305 j++;
307 if(j < disasms.size())
308 type->SetSelection(j);
310 //Set default address.
311 int k = 0;
312 for(auto& i : regions) {
313 if(dflt_base >= i.second.first && dflt_base < i.second.first + i.second.second) {
314 vma->SetSelection(k + 1);
315 dflt_base -= i.second.first;
316 break;
318 k++;
320 address->SetValue(towxstring((stringfmt() << std::hex << dflt_base).str()));
322 wxCommandEvent e;
323 on_change(e);
326 void dialog_disassemble::on_ok(wxCommandEvent& e)
328 CHECK_UI_THREAD;
329 EndModal(wxID_OK);
332 std::string dialog_disassemble::get_disassembler()
334 CHECK_UI_THREAD;
335 if(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() < (ssize_t)type->GetCount()) {
336 int _endian = endian->GetSelection();
337 int dtsel = type->GetSelection() - code_types;
338 std::string _vma = tostdstring(vma->GetStringSelection());
339 if(_endian <= 0 || _endian > 3) {
340 _endian = 1;
341 inst.iqueue->run([&_endian, _vma]() {
342 for(auto i : CORE().memory->get_regions()) {
343 if(i->name == _vma) {
344 _endian = i->endian + 2;
349 if(dtsel < 0) dtsel = 0;
350 if(dtsel > 16) dtsel = 16;
351 static const char* typechars = "bBcwWChHidDIqQrfF";
352 static const char* endianchars = " lhb";
353 std::string res = std::string("$data:") + std::string(1, typechars[dtsel]) +
354 std::string(1, endianchars[_endian]);
355 if(!has_default)
356 old_dflt_lang = res;
357 return res;
358 } else {
359 std::string res = tostdstring(type->GetStringSelection());
360 if(!has_default)
361 old_dflt_lang = res;
362 return res;
366 uint64_t dialog_disassemble::get_address()
368 CHECK_UI_THREAD;
369 uint64_t base = 0;
370 if(vma->GetSelection() && vma->GetSelection() != wxNOT_FOUND) {
371 std::string _vma = tostdstring(vma->GetStringSelection());
372 inst.iqueue->run([&base, _vma]() {
373 for(auto i : CORE().memory->get_regions()) {
374 if(i->name == _vma) {
375 base = i->base;
380 uint64_t off = hex::from<uint64_t>(tostdstring(address->GetValue()));
381 uint64_t res = base + off;
382 if(!has_default)
383 old_dflt_base = res;
384 return res;
387 uint64_t dialog_disassemble::get_count()
389 CHECK_UI_THREAD;
390 return count->GetValue();
393 void dialog_disassemble::on_change(wxCommandEvent& e)
395 CHECK_UI_THREAD;
396 bool is_ok = true;
397 try {
398 hex::from<uint64_t>(tostdstring(address->GetValue()));
399 } catch(std::exception& e) {
400 is_ok = false;
402 is_ok = is_ok && (type->GetSelection() != wxNOT_FOUND);
403 is_ok = is_ok && (vma->GetSelection() != wxNOT_FOUND);
404 endian->Enable(type->GetSelection() >= (ssize_t)code_types && type->GetSelection() <
405 (ssize_t)type->GetCount());
406 is_ok = is_ok && (!endian->IsEnabled() || endian->GetSelection() != wxNOT_FOUND);
407 //If VMA is global, ensure there is valid endian.
408 is_ok = is_ok && (vma->GetSelection() != 0 || !endian->IsEnabled() || endian->GetSelection() != 0);
409 ok->Enable(is_ok);
412 class wxwin_tracelog;
414 class dialog_breakpoint_add : public wxDialog
416 public:
417 dialog_breakpoint_add(wxWindow* parent, std::list<memory_space::region*> regions);
418 std::pair<uint64_t, debug_context::etype> get_result();
419 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
420 void on_cancel(wxCommandEvent& e) { EndModal(wxID_CANCEL); }
421 void on_address_change(wxCommandEvent& e);
422 private:
423 std::list<memory_space::region*> regions;
424 wxComboBox* vmasel;
425 wxTextCtrl* address;
426 wxComboBox* typesel;
427 wxButton* ok;
428 wxButton* cancel;
431 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow* parent, std::list<memory_space::region*> _regions)
432 : wxDialog(parent, wxID_ANY, wxT("Add breakpoint"))
434 CHECK_UI_THREAD;
435 regions = _regions;
436 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
437 SetSizer(top_s);
439 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Memory region:")), 0, wxGROW);
440 top_s->Add(vmasel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
441 0, NULL, wxCB_READONLY), 1, wxGROW);
442 vmasel->Append(towxstring(""));
443 for(auto i : regions)
444 vmasel->Append(towxstring(i->name));
445 vmasel->SetSelection(0);
447 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Offset (hexadecimal):")), 0, wxGROW);
448 top_s->Add(address = new wxTextCtrl(this, wxID_ANY, wxT("0"), wxDefaultPosition, wxSize(350, -1)), 0,
449 wxGROW);
451 top_s->Add(new wxStaticText(this, wxID_ANY, wxT("Breakpoint type:")), 0, wxGROW);
452 top_s->Add(typesel = new wxComboBox(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
453 0, NULL, wxCB_READONLY), 1, wxGROW);
454 typesel->Append(towxstring("Read"));
455 typesel->Append(towxstring("Write"));
456 typesel->Append(towxstring("Execute"));
457 typesel->SetSelection(0);
459 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
460 pbutton_s->AddStretchSpacer();
461 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("OK")));
462 pbutton_s->Add(cancel = new wxButton(this, wxID_ANY, wxT("Cancel")));
463 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_ok), NULL,
464 this);
465 cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoint_add::on_cancel),
466 NULL, this);
467 top_s->Add(pbutton_s, 0, wxGROW);
468 top_s->SetSizeHints(this);
469 Fit();
472 void dialog_breakpoint_add::on_address_change(wxCommandEvent& e)
474 CHECK_UI_THREAD;
475 try {
476 hex::from<uint64_t>(tostdstring(address->GetValue()));
477 ok->Enable(true);
478 } catch(...) {
479 ok->Enable(false);
483 std::pair<uint64_t, debug_context::etype> dialog_breakpoint_add::get_result()
485 CHECK_UI_THREAD;
486 std::string vmaname = tostdstring(vmasel->GetStringSelection());
487 std::string addrtext = tostdstring(address->GetValue());
488 uint64_t base = 0;
489 if(vmaname != "") {
490 for(auto i : regions)
491 if(i->name == vmaname)
492 base = i->base;
494 uint64_t addr;
495 try {
496 addr = base + hex::from<uint64_t>(addrtext);
497 } catch(std::exception& e) {
498 addr = base;
500 debug_context::etype dtype = debug_context::DEBUG_EXEC;
501 if(typesel->GetSelection() == 0)
502 dtype = debug_context::DEBUG_READ;
503 if(typesel->GetSelection() == 1)
504 dtype = debug_context::DEBUG_WRITE;
505 if(typesel->GetSelection() == 2)
506 dtype = debug_context::DEBUG_EXEC;
507 return std::make_pair(addr, dtype);
510 class dialog_breakpoints : public wxDialog
512 public:
513 dialog_breakpoints(wxwin_tracelog* parent, emulator_instance& _inst);
514 void on_ok(wxCommandEvent& e) { EndModal(wxID_OK); }
515 void on_add(wxCommandEvent& e);
516 void on_delete(wxCommandEvent& e);
517 void on_selchange(wxCommandEvent& e);
518 private:
519 std::string format_line(std::pair<uint64_t, debug_context::etype> entry);
520 size_t get_insert_pos(std::pair<uint64_t, debug_context::etype> entry);
521 void populate_breakpoints();
522 std::list<memory_space::region*> regions;
523 emulator_instance& inst;
524 wxButton* ok;
525 wxButton* addb;
526 wxButton* delb;
527 wxListBox* brklist;
528 wxwin_tracelog* pwin;
529 std::vector<std::pair<uint64_t, debug_context::etype>> listsyms;
532 class wxwin_tracelog : public wxFrame, public debug_context::callback_base
534 public:
535 wxwin_tracelog(wxWindow* parent, emulator_instance& _inst, int _cpuid, const std::string& cpuname);
536 ~wxwin_tracelog();
537 bool ShouldPreventAppExit() const { return false; }
538 scroll_bar* get_scroll() { return scroll; }
539 void on_wclose(wxCloseEvent& e);
540 void on_enabled(wxCommandEvent& e);
541 void on_menu(wxCommandEvent& e);
542 void process_lines();
543 uint64_t get_find_line() { return find_active ? find_line : 0xFFFFFFFFFFFFFFFFULL; }
544 std::set<std::pair<uint64_t, debug_context::etype>> get_breakpoints();
545 void add_breakpoint(uint64_t addr, debug_context::etype dtype);
546 void remove_breakpoint(uint64_t addr, debug_context::etype dtype);
547 private:
548 class _panel : public text_framebuffer_panel
550 public:
551 _panel(wxwin_tracelog* parent, emulator_instance& _inst);
552 void on_size(wxSizeEvent& e);
553 void on_mouse(wxMouseEvent& e);
554 wxSize DoGetBestSize() const;
555 uint64_t pos;
556 std::vector<std::string> rows;
557 void on_popup_menu(wxCommandEvent& e);
558 bool scroll_to_end_on_repaint;
559 protected:
560 void prepare_paint();
561 private:
562 emulator_instance& inst;
563 uint64_t pressed_row;
564 uint64_t current_row;
565 bool holding;
566 wxwin_tracelog* p;
568 emulator_instance& inst;
569 bool do_exit_save();
570 void scroll_pane(uint64_t line);
571 int cpuid;
572 volatile bool trace_active;
573 void callback(const debug_context::params& params);
574 void killed(uint64_t addr, debug_context::etype type);
575 void do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type);
576 void kill_debug_hooks(bool kill_hard = false);
577 scroll_bar* scroll;
578 _panel* panel;
579 bool broken;
580 bool broken2;
581 wxCheckBox* enabled;
582 threads::lock buffer_mutex;
583 std::list<std::string> lines_waiting;
584 bool unprocessed_lines;
585 bool closing;
586 bool find_active;
587 uint64_t find_line;
588 std::string find_string;
589 bool dirty;
590 bool singlestepping;
591 std::map<std::pair<uint64_t, debug_context::etype>, bool> rwx_breakpoints;
592 wxMenuItem* m_singlestep;
595 wxwin_tracelog::~wxwin_tracelog()
599 void wxwin_tracelog::on_wclose(wxCloseEvent& e)
601 CHECK_UI_THREAD;
602 if(dirty && !wxwidgets_exiting) {
603 int r = prompt_for_save(this, "Trace log");
604 if(r < 0 || (r > 0 && !do_exit_save()))
605 return;
607 if(trace_active)
608 inst.iqueue->run([this]() { kill_debug_hooks(); });
609 trace_active = false;
610 if(!closing)
611 Destroy();
612 closing = true;
615 void wxwin_tracelog::kill_debug_hooks(bool kill_hard)
617 CORE().dbg->remove_callback(cpuid, debug_context::DEBUG_FRAME, *this);
618 if(!kill_hard)
619 CORE().dbg->remove_callback(cpuid, debug_context::DEBUG_TRACE, *this);
620 threads::alock h(buffer_mutex);
621 for(auto& i : rwx_breakpoints) {
622 if(!i.second)
623 continue;
624 if(!kill_hard)
625 CORE().dbg->remove_callback(i.first.first, i.first.second, *this);
626 i.second = false;
628 trace_active = false;
629 convert_break_to_pause();
632 wxwin_tracelog::_panel::_panel(wxwin_tracelog* parent, emulator_instance& _inst)
633 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL), inst(_inst)
635 CHECK_UI_THREAD;
636 p = parent;
637 pos = 0;
638 pressed_row = 0;
639 current_row = 0;
640 holding = false;
641 scroll_to_end_on_repaint = false;
642 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_tracelog::_panel::on_size), NULL, this);
643 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
644 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
645 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
646 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse), NULL, this);
649 void wxwin_tracelog::_panel::on_size(wxSizeEvent& e)
651 wxSize newsize = e.GetSize();
652 auto tcell = get_cell();
653 size_t lines = newsize.y / tcell.second;
654 size_t linelen = newsize.x / tcell.first;
655 if(lines < 1) lines = 1;
656 if(linelen < 1) linelen = 1;
657 set_size(linelen, lines);
658 p->get_scroll()->set_page_size(lines);
659 request_paint();
660 e.Skip();
663 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent& e)
665 CHECK_UI_THREAD;
666 uint64_t local_line = pos + e.GetY() / get_cell().second;
667 if(e.RightDown()) {
668 if(local_line < rows.size()) {
669 holding = true;
670 pressed_row = local_line;
672 }else if(e.RightUp()) {
673 holding = false;
674 wxMenu menu;
675 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
676 wxCommandEventHandler(wxwin_tracelog::_panel::on_popup_menu), NULL, this);
677 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
678 menu.Append(wxID_SAVE, wxT("Save to file"));
679 menu.AppendSeparator();
680 menu.Append(wxID_DELETE, wxT("Delete"));
681 PopupMenu(&menu);
682 } else {
683 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
684 request_paint();
686 unsigned speed = 1;
687 if(e.ShiftDown())
688 speed = 10;
689 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
692 wxSize wxwin_tracelog::_panel::DoGetBestSize() const
694 return wxSize(120 * 8, 25 * 16);
697 void wxwin_tracelog::_panel::prepare_paint()
699 p->get_scroll()->set_range(rows.size());
700 if(scroll_to_end_on_repaint) {
701 scroll_to_end_on_repaint = false;
702 p->get_scroll()->set_position(rows.size());
703 pos = p->get_scroll()->get_position();
705 uint64_t m = min(pressed_row, current_row);
706 uint64_t M = max(pressed_row, current_row);
707 auto s = get_characters();
708 uint64_t fline = p->get_find_line();
709 for(uint64_t i = pos; i < pos + s.second && i < rows.size(); i++) {
710 bool selected = holding && (i >= m) && (i <= M);
711 bool isfl = (i == fline);
712 uint32_t fg = selected ? 0x0000FF : 0x000000;
713 uint32_t bg = selected ? 0x000000 : (isfl ? 0xC0FFC0 : 0xFFFFFF);
714 write(rows[i], s.first, 0, i - pos, fg, bg);
716 for(uint64_t i = rows.size(); i < pos + s.second; i++)
717 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
720 void wxwin_tracelog::process_lines()
722 CHECK_UI_THREAD;
723 threads::alock h(this->buffer_mutex);
724 size_t osize = panel->rows.size();
725 if(broken) {
726 panel->rows.push_back(std::string(120, '-'));
727 broken = false;
729 for(auto& i : lines_waiting)
730 panel->rows.push_back(i);
731 lines_waiting.clear();
732 unprocessed_lines = false;
733 if(panel->rows.size() != osize) {
734 panel->scroll_to_end_on_repaint = true;
735 dirty = true;
737 panel->request_paint();
740 void wxwin_tracelog::do_rwx_break(uint64_t addr, uint64_t value, debug_context::etype type)
742 inst.dbg->request_break();
745 void wxwin_tracelog::callback(const debug_context::params& p)
747 switch(p.type) {
748 case debug_context::DEBUG_READ:
749 case debug_context::DEBUG_WRITE:
750 case debug_context::DEBUG_EXEC:
751 do_rwx_break(p.rwx.addr, p.rwx.value, p.type);
752 break;
753 case debug_context::DEBUG_TRACE: {
754 if(!trace_active)
755 return;
756 //Got tracelog line, send it.
757 threads::alock h(buffer_mutex);
758 lines_waiting.push_back(p.trace.decoded_insn);
759 if(!unprocessed_lines) {
760 unprocessed_lines = true;
761 runuifun([this]() { this->process_lines(); });
763 if(singlestepping && p.trace.true_insn) {
764 inst.dbg->request_break();
765 singlestepping = false;
767 break;
769 case debug_context::DEBUG_FRAME: {
770 std::ostringstream xstr;
771 xstr << "------------ ";
772 xstr << "Frame " << p.frame.frame;
773 if(p.frame.loadstated) xstr << " (loadstated)";
774 xstr << " ------------";
775 std::string str = xstr.str();
776 threads::alock h(buffer_mutex);
777 lines_waiting.push_back(str);
778 if(!unprocessed_lines) {
779 unprocessed_lines = true;
780 runuifun([this]() { this->process_lines(); });
782 break;
787 void wxwin_tracelog::killed(uint64_t addr, debug_context::etype type)
789 switch(type) {
790 case debug_context::DEBUG_READ:
791 case debug_context::DEBUG_WRITE:
792 case debug_context::DEBUG_EXEC: {
793 //We need to kill this hook if still active.
794 auto i2 = std::make_pair(addr, type);
795 rwx_breakpoints[i2] = false;
796 break;
798 case debug_context::DEBUG_TRACE:
799 //Dtor!
800 if(!trace_active)
801 return;
802 kill_debug_hooks(true);
803 runuifun([this]() {
804 this->enabled->SetValue(false);
805 this->enabled->Enable(false);
806 this->m_singlestep->Enable(false);
808 break;
809 case debug_context::DEBUG_FRAME:
810 //Do nothing.
811 break;
815 void wxwin_tracelog::on_enabled(wxCommandEvent& e)
817 CHECK_UI_THREAD;
818 bool enable = enabled->GetValue();
819 inst.iqueue->run([this, enable]() {
820 if(enable) {
821 threads::alock h(buffer_mutex);
822 broken = broken2;
823 broken2 = true;
824 for(auto& i : rwx_breakpoints) {
825 auto i2 = i.first;
826 CORE().dbg->add_callback(i2.first, i2.second, *this);
827 i.second = true;
829 CORE().dbg->add_callback(cpuid, debug_context::DEBUG_TRACE, *this);
830 CORE().dbg->add_callback(0, debug_context::DEBUG_FRAME, *this);
831 this->trace_active = true;
832 } else if(trace_active) {
833 this->trace_active = false;
834 this->kill_debug_hooks();
837 m_singlestep->Enable(enable);
840 bool find_match(const std::string& pattern, const std::string& candidate)
842 static std::string last_find;
843 if(pattern == "")
844 return false;
845 std::string tmp = pattern;
846 tmp = tmp.substr(1);
847 if(pattern[0] == 'F')
848 return regex_match(tmp, candidate, REGEX_MATCH_LITERIAL);
849 if(pattern[0] == 'W')
850 return regex_match(tmp, candidate, REGEX_MATCH_IWILDCARDS);
851 if(pattern[0] == 'R')
852 return regex_match(tmp, candidate, REGEX_MATCH_IREGEX);
853 return false;
856 void wxwin_tracelog::on_menu(wxCommandEvent& e)
858 CHECK_UI_THREAD;
859 if(e.GetId() == wxID_EXIT) {
860 if(dirty) {
861 int r = prompt_for_save(this, "Trace log");
862 if(r < 0 || (r > 0 && !do_exit_save()))
863 return;
865 if(trace_active) {
866 inst.iqueue->run([this]() { this->kill_debug_hooks(); });
868 trace_active = false;
869 Destroy();
870 return;
871 } else if(e.GetId() == wxID_SAVE) {
872 try {
873 std::string filename = choose_file_save(this, "Save tracelog to",
874 UI_get_project_otherpath(inst), filetype_trace);
875 std::ofstream s(filename, std::ios::app);
876 if(!s) throw std::runtime_error("Error opening output file");
877 for(auto& i : panel->rows)
878 s << i << std::endl;
879 if(!s) throw std::runtime_error("Error writing output file");
880 dirty = false;
881 } catch(canceled_exception& e) {
882 } catch(std::exception& e) {
883 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
884 wxOK, this);
886 } else if(e.GetId() == wxID_FIND) {
887 std::string tmp;
888 dialog_find* d = new dialog_find(this);
889 if(d->ShowModal() != wxID_OK) {
890 d->Destroy();
891 return;
893 tmp = d->get_pattern();
894 d->Destroy();
895 if(tmp == "") {
896 find_active = false;
897 return;
899 //Do syntax check.
900 try {
901 find_match(tmp, "");
902 } catch(...) {
903 find_active = false;
904 wxMessageBox(towxstring("Invalid search pattern"), _T("Invalid pattern"),
905 wxICON_EXCLAMATION | wxOK, this);
906 return;
908 find_string = tmp;
909 find_active = true;
910 find_line = 0;
911 while(find_line < panel->rows.size()) {
912 if(find_match(find_string, panel->rows[find_line]))
913 break;
914 find_line++;
916 if(find_line == panel->rows.size()) {
917 //Not found.
918 find_active = false;
919 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
920 wxICON_EXCLAMATION | wxOK, this);
921 } else
922 scroll_pane(find_line);
923 } else if(e.GetId() == wxID_FIND_NEXT) {
924 if(!find_active)
925 return;
926 uint64_t old_find_line = find_line;
927 find_line++;
928 while(!panel->rows.empty() && find_line != old_find_line) {
929 if(find_line >= panel->rows.size())
930 find_line = 0;
931 if(find_match(find_string, panel->rows[find_line]))
932 break;
933 find_line++;
935 scroll_pane(find_line);
936 } else if(e.GetId() == wxID_FIND_PREV) {
937 if(!find_active)
938 return;
939 uint64_t old_find_line = find_line;
940 find_line--;
941 while(!panel->rows.empty() && find_line != old_find_line) {
942 if(find_line >= panel->rows.size())
943 find_line = panel->rows.size() - 1;
944 if(find_match(find_string, panel->rows[find_line]))
945 break;
946 find_line--;
948 scroll_pane(find_line);
949 } else if(e.GetId() == wxID_SINGLESTEP) {
950 inst.iqueue->run_async([this]() {
951 this->singlestepping = true;
952 CORE().command->invoke("unpause-emulator");
953 }, [](std::exception& e) {});
954 } else if(e.GetId() == wxID_FRAMEADVANCE) {
955 inst.iqueue->run_async([this]() {
956 CORE().command->invoke("+advance-frame");
957 CORE().command->invoke("-advance-frame");
958 }, [](std::exception& e) {});
959 } else if(e.GetId() == wxID_CONTINUE) {
960 inst.iqueue->run_async([this]() {
961 CORE().command->invoke("unpause-emulator");
962 }, [](std::exception& e) {});
963 } else if(e.GetId() == wxID_BREAKPOINTS) {
964 dialog_breakpoints* d = new dialog_breakpoints(this, inst);
965 d->ShowModal();
966 d->Destroy();
967 } else if(e.GetId() == wxID_CLEAR) {
968 int r = prompt_for_save(this, "Trace log");
969 if(r < 0 || (r > 0 && !do_exit_save()))
970 return;
971 panel->rows.clear();
972 panel->request_paint();
973 find_active = false;
977 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent& e)
979 CHECK_UI_THREAD;
980 std::string str;
981 uint64_t m = min(pressed_row, current_row);
982 uint64_t M = max(pressed_row, current_row) + 1;
983 m = min(m, (uint64_t)rows.size());
984 M = min(M, (uint64_t)rows.size());
985 size_t lines = 0;
987 for(uint64_t i = m; i < M && i < rows.size(); i++) {
988 try {
989 std::string mline = rows[i];
990 if(lines == 1) str += "\n";
991 str += mline;
992 if(lines >= 1) str += "\n";
993 lines++;
994 } catch(...) {
998 switch(e.GetId()) {
999 case wxID_COPY:
1000 if (wxTheClipboard->Open()) {
1001 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
1002 wxTheClipboard->Close();
1004 break;
1005 case wxID_SAVE:
1006 try {
1007 std::string filename = choose_file_save(this, "Save tracelog fragment to",
1008 UI_get_project_otherpath(inst), filetype_trace);
1009 std::ofstream s(filename, std::ios::app);
1010 if(!s) throw std::runtime_error("Error opening output file");
1011 if(lines == 1) str += "\n";
1012 s << str;
1013 if(!s) throw std::runtime_error("Error writing output file");
1014 } catch(canceled_exception& e) {
1015 } catch(std::exception& e) {
1016 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1017 wxOK, this);
1019 break;
1020 case wxID_DELETE:
1021 rows.erase(rows.begin() + m, rows.begin() + M);
1022 if(m != M)
1023 p->dirty = true;
1024 request_paint();
1025 break;
1029 void wxwin_tracelog::scroll_pane(uint64_t line)
1031 unsigned r = panel->get_characters().second;
1032 unsigned offset = r / 2;
1033 if(offset > line)
1034 scroll->set_position(panel->pos = 0);
1035 else if(line + r <= panel->rows.size())
1036 scroll->set_position(panel->pos = line - offset);
1037 else
1038 scroll->set_position(panel->pos = panel->rows.size() - r);
1039 panel->request_paint();
1042 bool wxwin_tracelog::do_exit_save()
1044 CHECK_UI_THREAD;
1045 back:
1046 try {
1047 std::string filename = choose_file_save(this, "Save tracelog to",
1048 UI_get_project_otherpath(inst), filetype_trace);
1049 std::ofstream s(filename, std::ios::app);
1050 if(!s) throw std::runtime_error("Error opening output file");
1051 for(auto& i : panel->rows)
1052 s << i << std::endl;
1053 if(!s) throw std::runtime_error("Error writing output file");
1054 dirty = false;
1055 } catch(canceled_exception& e) {
1056 return false;
1057 } catch(std::exception& e) {
1058 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1059 wxOK, this);
1060 goto back;
1062 return true;
1065 std::set<std::pair<uint64_t, debug_context::etype>> wxwin_tracelog::get_breakpoints()
1067 std::set<std::pair<uint64_t, debug_context::etype>> ret;
1068 inst.iqueue->run([this, &ret]() {
1069 for(auto i : rwx_breakpoints)
1070 ret.insert(i.first);
1072 return ret;
1075 void wxwin_tracelog::add_breakpoint(uint64_t addr, debug_context::etype dtype)
1077 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1078 if(!trace_active) {
1079 //We'll register this later.
1080 rwx_breakpoints[i2] = false;
1081 return;
1083 inst.dbg->add_callback(i2.first, i2.second, *this);
1084 rwx_breakpoints[i2] = true;
1087 void wxwin_tracelog::remove_breakpoint(uint64_t addr, debug_context::etype dtype)
1089 std::pair<uint64_t, debug_context::etype> i2 = std::make_pair(addr, dtype);
1090 auto& h = rwx_breakpoints[i2];
1091 if(h)
1092 inst.dbg->remove_callback(i2.first, i2.second, *this);
1093 rwx_breakpoints.erase(i2);
1096 wxwin_tracelog::wxwin_tracelog(wxWindow* parent, emulator_instance& _inst, int _cpuid,
1097 const std::string& cpuname)
1098 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Tracelog for " + cpuname), wxDefaultPosition,
1099 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1100 wxCLIP_CHILDREN), inst(_inst)
1102 CHECK_UI_THREAD;
1103 cpuid = _cpuid;
1104 singlestepping = false;
1105 find_active = false;
1106 find_line = 0;
1107 closing = false;
1108 trace_active = false;
1109 unprocessed_lines = false;
1110 broken = false;
1111 broken2 = false;
1112 dirty = false;
1113 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1114 SetSizer(top_s);
1115 wxBoxSizer* bottom_s = new wxBoxSizer(wxHORIZONTAL);
1116 top_s->Add(enabled = new wxCheckBox(this, wxID_ANY, wxT("Enabled")), 0, wxGROW);
1117 bottom_s->Add(panel = new _panel(this, inst), 1, wxGROW);
1118 bottom_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1119 top_s->Add(bottom_s, 1, wxGROW);
1120 enabled->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxwin_tracelog::on_enabled),
1121 NULL, this);
1122 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_tracelog::on_wclose),
1123 NULL, this);
1124 scroll->set_page_size(panel->get_characters().second);
1125 scroll->set_handler([this](scroll_bar& s) {
1126 this->panel->pos = s.get_position();
1127 this->panel->request_paint();
1129 wxMenuBar* mb;
1130 wxStatusBar* sb;
1131 wxMenu* menu;
1133 SetMenuBar(mb = new wxMenuBar);
1134 SetStatusBar(sb = new wxStatusBar(this));
1135 mb->Append(menu = new wxMenu(), wxT("File"));
1136 menu->Append(wxID_SAVE, wxT("Save"));
1137 menu->AppendSeparator();
1138 menu->Append(wxID_EXIT, wxT("Close"));
1139 mb->Append(menu = new wxMenu(), wxT("Edit"));
1140 menu->Append(wxID_FIND, wxT("Find..."));
1141 menu->Append(wxID_FIND_NEXT, wxT("Find next\tF3"));
1142 menu->Append(wxID_FIND_PREV, wxT("Find previous\tSHIFT+F3"));
1143 menu->AppendSeparator();
1144 menu->Append(wxID_CLEAR, towxstring("Clear"));
1145 mb->Append(menu = new wxMenu(), wxT("Debug"));
1146 m_singlestep = menu->Append(wxID_SINGLESTEP, towxstring("Singlestep\tF2"));
1147 menu->Append(wxID_FRAMEADVANCE, towxstring("Frame advance\tF4"));
1148 menu->Append(wxID_CONTINUE, towxstring("Continue\tF5"));
1149 menu->AppendSeparator();
1150 menu->Append(wxID_BREAKPOINTS, towxstring("Breakpoints"));
1151 m_singlestep->Enable(false);
1152 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_tracelog::on_menu),
1153 NULL, this);
1154 //Very nasty hack.
1155 wxSize tmp = panel->GetMinSize();
1156 panel->SetMinSize(panel->DoGetBestSize());
1157 top_s->SetSizeHints(this);
1158 wxSize tmp2 = GetClientSize();
1159 panel->SetMinSize(tmp);
1160 top_s->SetSizeHints(this);
1161 SetClientSize(tmp2);
1164 struct disasm_row
1166 uint64_t cover;
1167 std::string language;
1168 std::string row;
1171 class wxwin_disassembler : public wxFrame
1173 public:
1174 wxwin_disassembler(wxWindow* parent, emulator_instance& _inst);
1175 bool ShouldPreventAppExit() const { return false; }
1176 scroll_bar* get_scroll() { return scroll; }
1177 void on_menu(wxCommandEvent& e);
1178 void on_wclose(wxCloseEvent& e);
1179 private:
1180 class _panel : public text_framebuffer_panel
1182 public:
1183 _panel(wxwin_disassembler* parent, emulator_instance& _inst);
1184 void on_size(wxSizeEvent& e);
1185 void on_mouse(wxMouseEvent& e);
1186 wxSize DoGetBestSize() const;
1187 uint64_t pos;
1188 std::vector<uint64_t> rows;
1189 std::map<uint64_t, disasm_row> row_map;
1190 void on_popup_menu(wxCommandEvent& e);
1191 protected:
1192 void prepare_paint();
1193 private:
1194 emulator_instance& inst;
1195 uint64_t pressed_row;
1196 uint64_t current_row;
1197 bool holding;
1198 wxwin_disassembler* p;
1200 bool do_exit_save();
1201 void add_row(uint64_t addr, const disasm_row& row, bool last);
1202 void add_rows(const std::map<uint64_t, disasm_row>& rowdata);
1203 void add_rows_main(const std::map<uint64_t, disasm_row>& rowdata);
1204 void run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count);
1205 void scroll_pane(uint64_t line);
1206 emulator_instance& inst;
1207 scroll_bar* scroll;
1208 _panel* panel;
1209 bool dirty;
1210 bool closing;
1213 wxwin_disassembler::_panel::_panel(wxwin_disassembler* parent, emulator_instance& _inst)
1214 : text_framebuffer_panel(parent, 20, 5, wxID_ANY, NULL), inst(_inst)
1216 CHECK_UI_THREAD;
1217 p = parent;
1218 pos = 0;
1219 pressed_row = 0;
1220 current_row = 0;
1221 holding = false;
1222 this->Connect(wxEVT_SIZE, wxSizeEventHandler(wxwin_disassembler::_panel::on_size), NULL, this);
1223 this->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1224 this);
1225 this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1226 this->Connect(wxEVT_MOTION, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL, this);
1227 this->Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse), NULL,
1228 this);
1231 void wxwin_disassembler::_panel::on_size(wxSizeEvent& e)
1233 wxSize newsize = e.GetSize();
1234 auto tcell = get_cell();
1235 size_t lines = newsize.y / tcell.second;
1236 size_t linelen = newsize.x / tcell.first;
1237 if(lines < 1) lines = 1;
1238 if(linelen < 1) linelen = 1;
1239 set_size(linelen, lines);
1240 p->get_scroll()->set_page_size(lines);
1241 request_paint();
1242 e.Skip();
1245 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent& e)
1247 CHECK_UI_THREAD;
1248 uint64_t local_line = pos + e.GetY() / get_cell().second;
1249 if(e.RightDown()) {
1250 if(local_line < rows.size()) {
1251 holding = true;
1252 pressed_row = local_line;
1254 }else if(e.RightUp()) {
1255 holding = false;
1256 wxMenu menu;
1257 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
1258 wxCommandEventHandler(wxwin_disassembler::_panel::on_popup_menu), NULL, this);
1259 menu.Append(wxID_COPY, wxT("Copy to clipboard"));
1260 menu.Append(wxID_SAVE, wxT("Save to file"));
1261 menu.AppendSeparator();
1262 menu.Append(wxID_DISASM_MORE, wxT("Disassemble more"));
1263 menu.AppendSeparator();
1264 menu.Append(wxID_DELETE, wxT("Delete"));
1265 PopupMenu(&menu);
1266 } else {
1267 current_row = min(local_line, static_cast<uint64_t>(rows.size()));
1268 request_paint();
1270 unsigned speed = 1;
1271 if(e.ShiftDown())
1272 speed = 10;
1273 p->get_scroll()->apply_wheel(e.GetWheelRotation(), e.GetWheelDelta(), speed);
1276 wxSize wxwin_disassembler::_panel::DoGetBestSize() const
1278 return wxSize(40 * 8, 25 * 16);
1281 void wxwin_disassembler::_panel::prepare_paint()
1283 p->get_scroll()->set_range(rows.size());
1284 uint64_t m = min(pressed_row, current_row);
1285 uint64_t M = max(pressed_row, current_row);
1286 auto s = get_characters();
1287 uint64_t i;
1288 for(i = pos; i < pos + s.second && i < rows.size(); i++) {
1289 bool selected = holding && (i >= m) && (i <= M);
1290 uint32_t fg = selected ? 0x0000FF : 0x000000;
1291 uint32_t bg = selected ? 0x000000 : 0xFFFFFF;
1292 write(row_map[rows[i]].row, s.first, 0, i - pos, fg, bg);
1294 for(; i < pos + s.second; i++) {
1295 write("", s.first, 0, i - pos, 0xFFFFFF, 0xFFFFFF);
1299 void wxwin_disassembler::on_menu(wxCommandEvent& e)
1301 CHECK_UI_THREAD;
1302 if(e.GetId() == wxID_EXIT) {
1303 if(dirty) {
1304 int r = prompt_for_save(this, "Disassembly");
1305 if(r < 0 || (r > 0 && !do_exit_save()))
1306 return;
1308 Destroy();
1309 return;
1310 } else if(e.GetId() == wxID_SAVE) {
1311 try {
1312 std::string filename = choose_file_save(this, "Save disassembly to",
1313 UI_get_project_otherpath(inst), filetype_disassembly);
1314 std::ofstream s(filename, std::ios::app);
1315 if(!s) throw std::runtime_error("Error opening output file");
1316 for(auto& i : panel->rows)
1317 s << panel->row_map[i].row << std::endl;
1318 if(!s) throw std::runtime_error("Error writing output file");
1319 dirty = false;
1320 } catch(canceled_exception& e) {
1321 } catch(std::exception& e) {
1322 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1323 wxOK, this);
1325 } else if(e.GetId() == wxID_DISASM) {
1326 std::string tmp;
1327 dialog_disassemble* d = new dialog_disassemble(this, inst);
1328 if(d->ShowModal() != wxID_OK) {
1329 d->Destroy();
1330 return;
1332 std::string disasm = d->get_disassembler();
1333 uint64_t addr = d->get_address();
1334 uint64_t count = d->get_count();
1335 d->Destroy();
1336 inst.iqueue->run_async([this, disasm, addr, count]() {
1337 this->run_disassembler(disasm, addr, count);
1338 }, [](std::exception& e) {});
1339 } else if(e.GetId() == wxID_GOTO) {
1340 try {
1341 std::string to = pick_text(this, "Goto", "Enter address to go to:", "");
1342 inst.iqueue->run_async([this, to]() {
1343 uint64_t addr;
1344 uint64_t base = 0;
1345 std::string vma;
1346 std::string offset;
1347 std::string _to = to;
1348 size_t sp = _to.find_first_of("+");
1349 if(sp >= _to.length()) {
1350 offset = _to;
1351 } else {
1352 vma = _to.substr(0, sp);
1353 offset = _to.substr(sp + 1);
1355 if(vma != "") {
1356 bool found = false;
1357 for(auto i : CORE().memory->get_regions()) {
1358 if(i->name == vma) {
1359 base = i->base;
1360 found = true;
1363 if(!found) {
1364 runuifun([this] {
1365 show_message_ok(this, "Error in address",
1366 "No such memory area known",
1367 wxICON_EXCLAMATION);
1369 return;
1372 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1373 " <name>+<hexdigits>", [&addr, offset]() {
1374 addr = hex::from<uint64_t>(offset); }))
1375 return;
1376 addr += base;
1377 runuifun([this, addr]() {
1378 uint64_t nrow = 0;
1379 uint64_t low = 0;
1380 uint64_t high = this->panel->rows.size();
1381 while(low < high && low < high - 1) {
1382 nrow = (low + high) / 2;
1383 if(this->panel->rows[nrow] > addr)
1384 high = nrow;
1385 else if(this->panel->rows[nrow] < addr)
1386 low = nrow;
1387 else
1388 break;
1390 this->scroll_pane(nrow);
1392 }, [](std::exception& e) {});
1393 } catch(canceled_exception& e) {
1398 void remove_from_array(std::vector<uint64_t>& v, uint64_t e)
1400 //Binary search for the element to remove.
1401 size_t low = 0;
1402 size_t high = v.size();
1403 size_t mid = 0;
1404 while(low < high) {
1405 mid = (low + high) / 2;
1406 if(v[mid] < e)
1407 low = mid;
1408 else if(v[mid] > e)
1409 high = mid;
1410 else
1411 break;
1413 if(v[mid] == e)
1414 v.erase(v.begin() + mid);
1417 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent& e)
1419 CHECK_UI_THREAD;
1420 if(e.GetId() == wxID_DISASM_MORE)
1422 if(current_row >= rows.size())
1423 return;
1424 uint64_t base = rows[current_row];
1425 uint64_t rbase = base;
1426 if(!row_map.count(base))
1427 return;
1428 auto& r = row_map[base];
1429 base = base + r.cover;
1430 std::string disasm = r.language;
1431 dialog_disassemble* d = new dialog_disassemble(this, inst, base, disasm);
1432 if(d->ShowModal() != wxID_OK) {
1433 d->Destroy();
1434 return;
1436 disasm = d->get_disassembler();
1437 uint64_t addr = d->get_address();
1438 uint64_t count = d->get_count();
1439 d->Destroy();
1440 auto pp = p;
1441 inst.iqueue->run_async([pp, disasm, addr, count]() {
1442 pp->run_disassembler(disasm, addr, count);
1443 }, [](std::exception& e) {});
1444 //Delete entries in (rbase, addr) if addr = base.
1445 if(addr == base) {
1446 for(uint64_t i = rbase + 1; i < addr; i++)
1447 if(row_map.count(i)) {
1448 //This line needs to be removed from rows too.
1449 row_map.erase(i);
1450 remove_from_array(rows, i);
1454 std::string str;
1455 uint64_t m = min(min(pressed_row, current_row), (uint64_t)rows.size());
1456 uint64_t M = min(max(pressed_row, current_row) + 1, (uint64_t)rows.size());
1457 size_t lines = 0;
1459 for(uint64_t i = m; i < M; i++) {
1460 try {
1461 std::string mline = row_map[rows[i]].row;
1462 if(lines == 1) str += "\n";
1463 str += mline;
1464 if(lines >= 1) str += "\n";
1465 lines++;
1466 } catch(...) {
1470 switch(e.GetId()) {
1471 case wxID_COPY:
1472 if (wxTheClipboard->Open()) {
1473 wxTheClipboard->SetData(new wxTextDataObject(towxstring(str)));
1474 wxTheClipboard->Close();
1476 break;
1477 case wxID_SAVE:
1478 try {
1479 std::string filename = choose_file_save(this, "Save disassembly fragment to",
1480 UI_get_project_otherpath(inst), filetype_disassembly);
1481 std::ofstream s(filename, std::ios::app);
1482 if(!s) throw std::runtime_error("Error opening output file");
1483 if(lines == 1) str += "\n";
1484 s << str;
1485 if(!s) throw std::runtime_error("Error writing output file");
1486 } catch(canceled_exception& e) {
1487 } catch(std::exception& e) {
1488 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1489 wxOK, this);
1491 break;
1492 case wxID_DELETE:
1493 for(uint64_t i = m; i < M; i++)
1494 row_map.erase(rows[i]);
1495 rows.erase(rows.begin() + m, rows.begin() + M);
1496 if(m != M)
1497 p->dirty = true;
1498 request_paint();
1499 break;
1503 std::string format_vma_offset(memory_space::region& region, uint64_t offset)
1505 std::ostringstream y;
1506 y << region.name;
1507 size_t sizedigits = 0;
1508 uint64_t tmp = region.size - 1;
1509 while(tmp > 0) {
1510 tmp >>= 4;
1511 sizedigits++;
1513 y << "+" << std::hex << std::setfill('0') << std::setw(sizedigits) << offset;
1514 return y.str();
1517 std::string lookup_address(emulator_instance& inst, uint64_t raw)
1519 auto g = inst.memory->lookup(raw);
1520 if(!g.first)
1521 return hex::to<uint64_t>(raw);
1522 else
1523 return format_vma_offset(*g.first, g.second);
1526 inline int sign_compare(uint64_t a, uint64_t b)
1528 if(a < b) return -1;
1529 if(b < a) return 1;
1530 return 0;
1533 void insert_into_array(std::vector<uint64_t>& v, uint64_t e)
1535 //Binary search for the gap to insert to.
1536 size_t low = 0;
1537 size_t high = v.size();
1538 size_t mid = 0;
1539 while(low < high) {
1540 mid = (low + high) / 2;
1541 int s1 = sign_compare(v[mid], e);
1542 int s2 = ((mid + 1) < v.size()) ? sign_compare(v[mid + 1], e) : 1;
1543 if(s1 < 0 && s2 > 0)
1544 break;
1545 else if(s1 == 0 || s2 == 0)
1546 return;
1547 else if(s1 > 0)
1548 high = mid;
1549 else if(s2 < 0)
1550 low = mid;
1552 if(mid < v.size() && v[mid] < e)
1553 mid++;
1554 v.insert(v.begin() + mid, e);
1557 void wxwin_disassembler::add_row(uint64_t addr, const disasm_row& row, bool last)
1559 auto& rows = panel->rows;
1560 auto& row_map = panel->row_map;
1561 if(row_map.count(addr)) {
1562 row_map[addr] = row;
1563 } else {
1564 //We need to insert the row into rows.
1565 row_map[addr] = row;
1566 insert_into_array(rows, addr);
1568 dirty = true;
1569 if(!last)
1570 for(uint64_t i = addr + 1; i < addr + row.cover; i++)
1571 if(row_map.count(i)) {
1572 //This line needs to be removed from rows too.
1573 row_map.erase(i);
1574 remove_from_array(rows, i);
1578 void wxwin_disassembler::add_rows(const std::map<uint64_t, disasm_row>& rowdata)
1580 for(auto i = rowdata.begin(); i != rowdata.end(); i++) {
1581 auto j = i;
1582 j++;
1583 bool last = (j == rowdata.end());
1584 add_row(i->first, i->second, last);
1586 panel->request_paint();
1589 void wxwin_disassembler::add_rows_main(const std::map<uint64_t, disasm_row>& rowdata)
1591 std::map<uint64_t, disasm_row> _rowdata;
1592 for(auto& i : rowdata) {
1593 _rowdata[i.first] = i.second;
1594 _rowdata[i.first].row = lookup_address(inst, i.first) + " " + i.second.row;
1596 runuifun([this, _rowdata]() { this->add_rows(_rowdata); });
1599 template<typename T, bool hex> disasm_row _disassemble_data_item(emulator_instance& inst, uint64_t& addrbase,
1600 int endian, const std::string& disasm)
1602 char buf[sizeof(T)];
1603 for(size_t i = 0; i < sizeof(T); i++)
1604 buf[i] = inst.memory->read<uint8_t>(addrbase + i);
1605 disasm_row r;
1606 if(hex)
1607 r.row = (stringfmt() << "DATA 0x" << hex::to<T>(serialization::read_endian<T>(buf, endian))).
1608 str();
1609 else if(sizeof(T) > 1)
1610 r.row = (stringfmt() << "DATA " << serialization::read_endian<T>(buf, endian)).str();
1611 else
1612 r.row = (stringfmt() << "DATA " << (int)serialization::read_endian<T>(buf, endian)).str();
1613 r.cover = sizeof(T);
1614 r.language = disasm;
1615 addrbase += sizeof(T);
1616 return r;
1619 disasm_row disassemble_data_item(emulator_instance& inst, uint64_t& addrbase, const std::string& disasm)
1621 int endian;
1622 if(disasm[7] == 'l') endian = -1;
1623 if(disasm[7] == 'h') endian = 0;
1624 if(disasm[7] == 'b') endian = 1;
1625 switch(disasm[6]) {
1626 case 'b': return _disassemble_data_item<int8_t, false>(inst, addrbase, endian, disasm);
1627 case 'B': return _disassemble_data_item<uint8_t, false>(inst, addrbase, endian, disasm);
1628 case 'c': return _disassemble_data_item<uint8_t, true>(inst, addrbase, endian, disasm);
1629 case 'C': return _disassemble_data_item<uint16_t, true>(inst, addrbase, endian, disasm);
1630 case 'd': return _disassemble_data_item<int32_t, false>(inst, addrbase, endian, disasm);
1631 case 'D': return _disassemble_data_item<uint32_t, false>(inst, addrbase, endian, disasm);
1632 case 'f': return _disassemble_data_item<float, false>(inst, addrbase, endian, disasm);
1633 case 'F': return _disassemble_data_item<double, false>(inst, addrbase, endian, disasm);
1634 case 'h': return _disassemble_data_item<ss_int24_t, false>(inst, addrbase, endian, disasm);
1635 case 'H': return _disassemble_data_item<ss_uint24_t, false>(inst, addrbase, endian, disasm);
1636 case 'i': return _disassemble_data_item<ss_uint24_t, true>(inst, addrbase, endian, disasm);
1637 case 'I': return _disassemble_data_item<uint32_t, true>(inst, addrbase, endian, disasm);
1638 case 'q': return _disassemble_data_item<int64_t, false>(inst, addrbase, endian, disasm);
1639 case 'Q': return _disassemble_data_item<uint64_t, false>(inst, addrbase, endian, disasm);
1640 case 'r': return _disassemble_data_item<uint64_t, true>(inst, addrbase, endian, disasm);
1641 case 'w': return _disassemble_data_item<int16_t, false>(inst, addrbase, endian, disasm);
1642 case 'W': return _disassemble_data_item<uint16_t, false>(inst, addrbase, endian, disasm);
1644 throw std::runtime_error("Invalid kind of data");
1647 void wxwin_disassembler::scroll_pane(uint64_t line)
1649 unsigned r = panel->get_characters().second;
1650 unsigned offset = r / 2;
1651 if(offset > line)
1652 scroll->set_position(panel->pos = 0);
1653 else if(line + r < panel->rows.size())
1654 scroll->set_position(panel->pos = line - offset);
1655 else
1656 scroll->set_position(panel->pos = panel->rows.size() - r);
1657 panel->request_paint();
1660 void wxwin_disassembler::on_wclose(wxCloseEvent& e)
1662 CHECK_UI_THREAD;
1663 if(dirty && !wxwidgets_exiting) {
1664 int r = prompt_for_save(this, "Disassembly");
1665 if(r < 0 || (r > 0 && !do_exit_save()))
1666 return;
1668 if(!closing)
1669 Destroy();
1670 closing = true;
1673 void wxwin_disassembler::run_disassembler(const std::string& disasm, uint64_t addrbase, uint64_t count)
1675 std::map<uint64_t, disasm_row> rowdata;
1676 if(regex_match("\\$data:.*", disasm)) {
1677 if(run_show_error(this, "Error in disassember", "Error in disassember",
1678 [this, disasm, &rowdata, &addrbase, count]() {
1679 for(uint64_t i = 0; i < count; i++) {
1680 uint64_t base = addrbase;
1681 disasm_row r = disassemble_data_item(this->inst, addrbase, disasm);
1682 rowdata[base] = r;
1685 return;
1686 add_rows_main(rowdata);
1687 return;
1689 disassembler* d;
1690 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm + "' found",
1691 [&d, disasm]() {
1692 d = &disassembler::byname(disasm);
1694 return;
1695 for(uint64_t i = 0; i < count; i++) {
1696 uint64_t base = addrbase;
1697 disasm_row r;
1698 r.row = d->disassemble(addrbase, [this, &addrbase]() -> unsigned char {
1699 return this->inst.memory->read<uint8_t>(addrbase++);
1701 r.cover = addrbase - base;
1702 r.language = disasm;
1703 rowdata[base] = r;
1705 add_rows_main(rowdata);
1708 bool wxwin_disassembler::do_exit_save()
1710 CHECK_UI_THREAD;
1711 back:
1712 try {
1713 std::string filename = choose_file_save(this, "Save disassembly to",
1714 UI_get_project_otherpath(inst), filetype_disassembly);
1715 std::ofstream s(filename, std::ios::app);
1716 if(!s) throw std::runtime_error("Error opening output file");
1717 for(auto& i : panel->rows)
1718 s << panel->row_map[i].row << std::endl;
1719 if(!s) throw std::runtime_error("Error writing output file");
1720 dirty = false;
1721 } catch(canceled_exception& e) {
1722 return false;
1723 } catch(std::exception& e) {
1724 wxMessageBox(towxstring(e.what()), _T("Error creating file"), wxICON_EXCLAMATION |
1725 wxOK, this);
1726 goto back;
1728 return true;
1731 wxwin_disassembler::wxwin_disassembler(wxWindow* parent, emulator_instance& _inst)
1732 : wxFrame(parent, wxID_ANY, towxstring("lsnes: Disassembler"), wxDefaultPosition,
1733 wxDefaultSize, wxMINIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX |
1734 wxCLIP_CHILDREN), inst(_inst)
1736 CHECK_UI_THREAD;
1737 closing = false;
1738 dirty = false;
1739 wxBoxSizer* top_s = new wxBoxSizer(wxHORIZONTAL);
1740 SetSizer(top_s);
1741 top_s->Add(panel = new _panel(this, inst), 1, wxGROW);
1742 top_s->Add(scroll = new scroll_bar(this, wxID_ANY, true), 0, wxGROW);
1743 scroll->set_page_size(panel->get_characters().second);
1744 scroll->set_handler([this](scroll_bar& s) {
1745 this->panel->pos = s.get_position();
1746 this->panel->request_paint();
1748 wxMenuBar* mb;
1749 wxStatusBar* sb;
1750 wxMenu* menu;
1752 SetMenuBar(mb = new wxMenuBar);
1753 SetStatusBar(sb = new wxStatusBar(this));
1754 mb->Append(menu = new wxMenu(), wxT("File"));
1755 menu->Append(wxID_DISASM, wxT("Disassemble..."));
1756 menu->AppendSeparator();
1757 menu->Append(wxID_SAVE, wxT("Save"));
1758 menu->AppendSeparator();
1759 menu->Append(wxID_EXIT, wxT("Close"));
1760 Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(wxwin_disassembler::on_menu),
1761 NULL, this);
1762 mb->Append(menu = new wxMenu(), wxT("Edit"));
1763 menu->Append(wxID_GOTO, wxT("Goto"));
1765 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxwin_disassembler::on_wclose),
1766 NULL, this);
1767 //Very nasty hack.
1768 wxSize tmp = panel->GetMinSize();
1769 panel->SetMinSize(panel->DoGetBestSize());
1770 top_s->SetSizeHints(this);
1771 wxSize tmp2 = GetClientSize();
1772 panel->SetMinSize(tmp);
1773 top_s->SetSizeHints(this);
1774 SetClientSize(tmp2);
1777 dialog_breakpoints::dialog_breakpoints(wxwin_tracelog* parent, emulator_instance& _inst)
1778 : wxDialog(parent, wxID_ANY, wxT("Breakpoints")), inst(_inst)
1780 CHECK_UI_THREAD;
1781 pwin = parent;
1782 regions = inst.memory->get_regions();
1783 wxBoxSizer* top_s = new wxBoxSizer(wxVERTICAL);
1784 SetSizer(top_s);
1785 top_s->Add(brklist = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(300, 400)), 1, wxGROW);
1786 brklist->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
1787 wxCommandEventHandler(dialog_breakpoints::on_selchange), NULL, this);
1788 populate_breakpoints();
1789 wxBoxSizer* pbutton_s = new wxBoxSizer(wxHORIZONTAL);
1790 pbutton_s->Add(addb = new wxButton(this, wxID_ANY, wxT("Add")), 0, wxGROW);
1791 pbutton_s->Add(delb = new wxButton(this, wxID_ANY, wxT("Remove")), 0, wxGROW);
1792 pbutton_s->AddStretchSpacer();
1793 pbutton_s->Add(ok = new wxButton(this, wxID_ANY, wxT("Close")), 0, wxGROW);
1794 delb->Enable(false);
1795 addb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_add), NULL,
1796 this);
1797 delb->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_delete),
1798 NULL, this);
1799 ok->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(dialog_breakpoints::on_ok), NULL,
1800 this);
1801 top_s->Add(pbutton_s, 0, wxGROW);
1802 top_s->SetSizeHints(this);
1803 Fit();
1806 void dialog_breakpoints::populate_breakpoints()
1808 CHECK_UI_THREAD;
1809 auto t = pwin->get_breakpoints();
1810 for(auto i : t) {
1811 std::string line = format_line(i);
1812 unsigned insert_pos = get_insert_pos(i);
1813 brklist->Insert(towxstring(line), insert_pos);
1814 listsyms.insert(listsyms.begin() + insert_pos, i);
1818 void dialog_breakpoints::on_add(wxCommandEvent& e)
1820 CHECK_UI_THREAD;
1821 uint64_t addr;
1822 debug_context::etype dtype;
1823 dialog_breakpoint_add* d = new dialog_breakpoint_add(this, regions);
1824 if(d->ShowModal() != wxID_OK) {
1825 d->Destroy();
1826 return;
1828 rpair(addr, dtype) = d->get_result();
1829 d->Destroy();
1830 inst.iqueue->run_async([this, addr, dtype]() {
1831 pwin->add_breakpoint(addr, dtype);
1832 }, [](std::exception& e) {});
1833 auto ent = std::make_pair(addr, dtype);
1834 std::string line = format_line(ent);
1835 unsigned insert_pos = get_insert_pos(ent);
1836 brklist->Insert(towxstring(line), insert_pos);
1837 listsyms.insert(listsyms.begin() + insert_pos, ent);
1840 void dialog_breakpoints::on_delete(wxCommandEvent& e)
1842 CHECK_UI_THREAD;
1843 int idx = brklist->GetSelection();
1844 if(idx == wxNOT_FOUND)
1845 return;
1846 uint64_t addr;
1847 debug_context::etype dtype;
1848 addr = listsyms[idx].first;
1849 dtype = listsyms[idx].second;
1850 inst.iqueue->run_async([this, addr, dtype]() {
1851 pwin->remove_breakpoint(addr, dtype);
1852 }, [](std::exception& e) {});
1853 brklist->Delete(idx);
1854 listsyms.erase(listsyms.begin() + idx);
1857 size_t dialog_breakpoints::get_insert_pos(std::pair<uint64_t, debug_context::etype> entry)
1859 size_t i = 0;
1860 for(i = 0; i < listsyms.size(); i++)
1861 if(entry < listsyms[i])
1862 return i;
1863 return i;
1866 std::string dialog_breakpoints::format_line(std::pair<uint64_t, debug_context::etype> entry)
1868 std::string base = "";
1869 for(auto i : regions) {
1870 if(entry.first >= i->base && entry.first < i->base + i->size) {
1871 base = format_vma_offset(*i, entry.first - i->base);
1872 break;
1875 if(base == "")
1876 base = hex::to<uint64_t>(entry.first);
1877 if(entry.second == debug_context::DEBUG_READ)
1878 return base + ": Read";
1879 if(entry.second == debug_context::DEBUG_WRITE)
1880 return base + ": Write";
1881 if(entry.second == debug_context::DEBUG_EXEC)
1882 return base + ": Execute";
1883 return base + ": Unknown";
1886 void dialog_breakpoints::on_selchange(wxCommandEvent& e)
1888 CHECK_UI_THREAD;
1889 delb->Enable(brklist->GetSelection() != wxNOT_FOUND);
1893 void wxeditor_tracelog_display(wxWindow* parent, emulator_instance& inst, int cpuid, const std::string& cpuname)
1895 CHECK_UI_THREAD;
1896 try {
1897 wxwin_tracelog* d = new wxwin_tracelog(parent, inst, cpuid, cpuname);
1898 d->Show();
1899 } catch(std::exception& e) {
1900 show_message_ok(parent, "Error opening trace logger", e.what(), wxICON_EXCLAMATION);
1904 void wxeditor_disassembler_display(wxWindow* parent, emulator_instance& inst)
1906 CHECK_UI_THREAD;
1907 wxwin_disassembler* d = new wxwin_disassembler(parent, inst);
1908 d->Show();