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"
24 #include <wx/clipbrd.h>
25 #include <wx/msgdlg.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>
37 #include <boost/regex.hpp>
44 wxID_FIND_NEXT
= wxID_HIGHEST
+ 1,
56 int prompt_for_save(wxWindow
* parent
, const std::string
& what
)
59 wxMessageDialog
* d
= new wxMessageDialog(parent
, towxstring(what
+ " has unsaved changes, "
60 "save before closing?"), towxstring("Save on exit?"), wxCENTER
| wxYES_NO
| wxCANCEL
|
62 d
->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
63 int r
= d
->ShowModal();
65 if(r
== wxID_YES
) return 1;
66 if(r
== wxID_NO
) return 0;
67 if(r
== wxID_CANCEL
) return -1;
71 class dialog_find
: public wxDialog
74 dialog_find(wxWindow
* parent
);
75 std::string
get_pattern();
76 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
77 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
85 dialog_find::dialog_find(wxWindow
* parent
)
86 : wxDialog(parent
, wxID_ANY
, wxT("Find"))
89 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
91 wxBoxSizer
* t_s
= new wxBoxSizer(wxHORIZONTAL
);
92 t_s
->Add(type
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
93 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
94 type
->Append(towxstring("Literal"));
95 type
->Append(towxstring("Wildcards"));
96 type
->Append(towxstring("Regexp"));
97 type
->SetSelection(0);
98 t_s
->Add(text
= new wxTextCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxSize(350, -1),
99 wxTE_PROCESS_ENTER
), 0, wxGROW
);
100 top_s
->Add(t_s
, 1, wxGROW
);
101 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
102 pbutton_s
->AddStretchSpacer();
103 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
104 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
105 text
->Connect(wxEVT_COMMAND_TEXT_ENTER
, wxCommandEventHandler(dialog_find::on_ok
), NULL
, this);
106 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_find::on_ok
), NULL
, this);
107 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_find::on_cancel
), NULL
,
109 top_s
->Add(pbutton_s
, 0, wxGROW
);
110 top_s
->SetSizeHints(this);
114 std::string
dialog_find::get_pattern()
117 if(tostdstring(text
->GetValue()) == "")
119 if(type
->GetSelection() == 2)
120 return "R" + tostdstring(text
->GetValue());
121 else if(type
->GetSelection() == 1)
122 return "W" + tostdstring(text
->GetValue());
124 return "F" + tostdstring(text
->GetValue());
127 class dialog_disassemble
: public wxDialog
130 dialog_disassemble(wxWindow
* parent
, emulator_instance
& _inst
);
131 dialog_disassemble(wxWindow
* parent
, emulator_instance
& _inst
, uint64_t dflt_base
,
132 const std::string
& dflt_lang
);
133 std::string
get_disassembler();
134 uint64_t get_address();
135 uint64_t get_count();
136 void on_change(wxCommandEvent
& e
);
137 void on_ok(wxCommandEvent
& e
);
138 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
140 void init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
);
141 emulator_instance
& inst
;
151 static std::string old_dflt_lang
;
152 static uint64_t old_dflt_base
;
155 std::string
dialog_disassemble::old_dflt_lang
;
156 uint64_t dialog_disassemble::old_dflt_base
;
158 dialog_disassemble::dialog_disassemble(wxWindow
* parent
, emulator_instance
& _inst
)
159 : wxDialog(parent
, wxID_ANY
, wxT("Disassemble region")), inst(_inst
)
165 dialog_disassemble::dialog_disassemble(wxWindow
* parent
, emulator_instance
& _inst
, uint64_t dflt_base
,
166 const std::string
& dflt_lang
)
167 : wxDialog(parent
, wxID_ANY
, wxT("Disassemble region")), inst(_inst
)
170 init(true, dflt_base
, dflt_lang
);
173 void dialog_disassemble::init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
)
176 std::map
<std::string
, std::pair
<uint64_t, uint64_t>> regions
;
177 std::set
<std::string
> disasms
;
178 inst
.iqueue
->run([®ions
, &disasms
]() {
179 for(auto i
: CORE().memory
->get_regions())
180 regions
[i
->name
] = std::make_pair(i
->base
, i
->size
);
181 disasms
= disassembler::list();
184 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
187 wxBoxSizer
* type_s
= new wxBoxSizer(wxHORIZONTAL
);
188 type_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Language:")), 0, wxGROW
);
189 type_s
->Add(type
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
190 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
191 for(auto& i
: disasms
)
192 type
->Append(towxstring(i
));
193 code_types
= type
->GetCount();
194 type
->Append(towxstring("Data (signed byte)"));
195 type
->Append(towxstring("Data (unsigned byte)"));
196 type
->Append(towxstring("Data (hex byte)"));
197 type
->Append(towxstring("Data (signed word)"));
198 type
->Append(towxstring("Data (unsigned word)"));
199 type
->Append(towxstring("Data (hex word)"));
200 type
->Append(towxstring("Data (signed onehalfword)"));
201 type
->Append(towxstring("Data (unsigned onehalfword)"));
202 type
->Append(towxstring("Data (hex onehalfword)"));
203 type
->Append(towxstring("Data (signed doubleword)"));
204 type
->Append(towxstring("Data (unsigned doubleword)"));
205 type
->Append(towxstring("Data (hex doubleword)"));
206 type
->Append(towxstring("Data (signed quadword)"));
207 type
->Append(towxstring("Data (unsigned quadword)"));
208 type
->Append(towxstring("Data (hex quadword)"));
209 type
->Append(towxstring("Data (float)"));
210 type
->Append(towxstring("Data (double)"));
211 type
->SetSelection(0);
212 type
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
214 top_s
->Add(type_s
, 0, wxGROW
);
216 wxBoxSizer
* endian_s
= new wxBoxSizer(wxHORIZONTAL
);
217 endian_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Endian:")), 0, wxGROW
);
218 endian_s
->Add(endian
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
219 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
220 endian
->Append(towxstring("(Memory area default)"));
221 endian
->Append(towxstring("Little-endian"));
222 endian
->Append(towxstring("Host-endian"));
223 endian
->Append(towxstring("Big-endian"));
224 endian
->SetSelection(0);
225 endian
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
227 top_s
->Add(endian_s
, 0, wxGROW
);
229 wxBoxSizer
* vma_s
= new wxBoxSizer(wxHORIZONTAL
);
230 vma_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Area:")), 0, wxGROW
);
231 vma_s
->Add(vma
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
232 0, NULL
, wxCB_READONLY
), 0, wxGROW
);
233 vma
->Append(towxstring("(Any)"));
234 for(auto& i
: regions
)
235 vma
->Append(towxstring(i
.first
));
236 vma
->SetSelection(0);
237 vma
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, wxCommandEventHandler(dialog_disassemble::on_change
),
239 top_s
->Add(vma_s
, 0, wxGROW
);
241 wxBoxSizer
* addr_s
= new wxBoxSizer(wxHORIZONTAL
);
242 addr_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Address:")), 0, wxGROW
);
243 addr_s
->Add(address
= new wxTextCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxSize(200, -1)),
245 address
->Connect(wxEVT_COMMAND_TEXT_UPDATED
, wxCommandEventHandler(dialog_disassemble::on_change
),
247 top_s
->Add(addr_s
, 0, wxGROW
);
249 wxBoxSizer
* cnt_s
= new wxBoxSizer(wxHORIZONTAL
);
250 cnt_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Count:")), 0, wxGROW
);
251 cnt_s
->Add(count
= new wxSpinCtrl(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
252 wxSP_ARROW_KEYS
, 1, 1000000000, 10), 0, wxGROW
);
253 count
->Connect(wxEVT_SPINCTRL
, wxCommandEventHandler(dialog_disassemble::on_change
), NULL
,
255 top_s
->Add(cnt_s
, 0, wxGROW
);
257 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
258 pbutton_s
->AddStretchSpacer();
259 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
260 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
261 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_disassemble::on_ok
), NULL
,
263 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_disassemble::on_cancel
),
265 top_s
->Add(pbutton_s
, 0, wxGROW
);
266 top_s
->SetSizeHints(this);
271 dflt_lang
= old_dflt_lang
;
272 dflt_base
= old_dflt_base
;
274 //Set default language.
275 if(regex_match("\\$data:.*", dflt_lang
)) {
276 switch(dflt_lang
[6]) {
277 case 'b': type
->SetSelection(code_types
+ 0); break;
278 case 'B': type
->SetSelection(code_types
+ 1); break;
279 case 'c': type
->SetSelection(code_types
+ 2); break;
280 case 'w': type
->SetSelection(code_types
+ 3); break;
281 case 'W': type
->SetSelection(code_types
+ 4); break;
282 case 'C': type
->SetSelection(code_types
+ 5); break;
283 case 'h': type
->SetSelection(code_types
+ 6); break;
284 case 'H': type
->SetSelection(code_types
+ 7); break;
285 case 'i': type
->SetSelection(code_types
+ 8); break;
286 case 'd': type
->SetSelection(code_types
+ 9); break;
287 case 'D': type
->SetSelection(code_types
+ 10); break;
288 case 'I': type
->SetSelection(code_types
+ 11); break;
289 case 'q': type
->SetSelection(code_types
+ 12); break;
290 case 'Q': type
->SetSelection(code_types
+ 13); break;
291 case 'r': type
->SetSelection(code_types
+ 14); break;
292 case 'f': type
->SetSelection(code_types
+ 15); break;
293 case 'F': type
->SetSelection(code_types
+ 16); break;
295 switch(dflt_lang
[7]) {
296 case 'l': endian
->SetSelection(1); break;
297 case 'h': endian
->SetSelection(2); break;
298 case 'b': endian
->SetSelection(3); break;
302 //Set default disasm.
303 for(auto& i
: disasms
) {
308 if(j
< disasms
.size())
309 type
->SetSelection(j
);
311 //Set default address.
313 for(auto& i
: regions
) {
314 if(dflt_base
>= i
.second
.first
&& dflt_base
< i
.second
.first
+ i
.second
.second
) {
315 vma
->SetSelection(k
+ 1);
316 dflt_base
-= i
.second
.first
;
321 address
->SetValue(towxstring((stringfmt() << std::hex
<< dflt_base
).str()));
327 void dialog_disassemble::on_ok(wxCommandEvent
& e
)
333 std::string
dialog_disassemble::get_disassembler()
336 if(type
->GetSelection() >= (ssize_t
)code_types
&& type
->GetSelection() < (ssize_t
)type
->GetCount()) {
337 int _endian
= endian
->GetSelection();
338 int dtsel
= type
->GetSelection() - code_types
;
339 std::string _vma
= tostdstring(vma
->GetStringSelection());
340 if(_endian
<= 0 || _endian
> 3) {
342 inst
.iqueue
->run([&_endian
, _vma
]() {
343 for(auto i
: CORE().memory
->get_regions()) {
344 if(i
->name
== _vma
) {
345 _endian
= i
->endian
+ 2;
350 if(dtsel
< 0) dtsel
= 0;
351 if(dtsel
> 16) dtsel
= 16;
352 static const char* typechars
= "bBcwWChHidDIqQrfF";
353 static const char* endianchars
= " lhb";
354 std::string res
= std::string("$data:") + std::string(1, typechars
[dtsel
]) +
355 std::string(1, endianchars
[_endian
]);
360 std::string res
= tostdstring(type
->GetStringSelection());
367 uint64_t dialog_disassemble::get_address()
371 if(vma
->GetSelection() && vma
->GetSelection() != wxNOT_FOUND
) {
372 std::string _vma
= tostdstring(vma
->GetStringSelection());
373 inst
.iqueue
->run([&base
, _vma
]() {
374 for(auto i
: CORE().memory
->get_regions()) {
375 if(i
->name
== _vma
) {
381 uint64_t off
= hex::from
<uint64_t>(tostdstring(address
->GetValue()));
382 uint64_t res
= base
+ off
;
388 uint64_t dialog_disassemble::get_count()
391 return count
->GetValue();
394 void dialog_disassemble::on_change(wxCommandEvent
& e
)
399 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
400 } catch(std::exception
& e
) {
403 is_ok
= is_ok
&& (type
->GetSelection() != wxNOT_FOUND
);
404 is_ok
= is_ok
&& (vma
->GetSelection() != wxNOT_FOUND
);
405 endian
->Enable(type
->GetSelection() >= (ssize_t
)code_types
&& type
->GetSelection() <
406 (ssize_t
)type
->GetCount());
407 is_ok
= is_ok
&& (!endian
->IsEnabled() || endian
->GetSelection() != wxNOT_FOUND
);
408 //If VMA is global, ensure there is valid endian.
409 is_ok
= is_ok
&& (vma
->GetSelection() != 0 || !endian
->IsEnabled() || endian
->GetSelection() != 0);
413 class wxwin_tracelog
;
415 class dialog_breakpoint_add
: public wxDialog
418 dialog_breakpoint_add(wxWindow
* parent
, std::list
<memory_space::region
*> regions
);
419 std::pair
<uint64_t, debug_context::etype
> get_result();
420 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
421 void on_cancel(wxCommandEvent
& e
) { EndModal(wxID_CANCEL
); }
422 void on_address_change(wxCommandEvent
& e
);
424 std::list
<memory_space::region
*> regions
;
432 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow
* parent
, std::list
<memory_space::region
*> _regions
)
433 : wxDialog(parent
, wxID_ANY
, wxT("Add breakpoint"))
437 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
440 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Memory region:")), 0, wxGROW
);
441 top_s
->Add(vmasel
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
442 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
443 vmasel
->Append(towxstring(""));
444 for(auto i
: regions
)
445 vmasel
->Append(towxstring(i
->name
));
446 vmasel
->SetSelection(0);
448 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Offset (hexadecimal):")), 0, wxGROW
);
449 top_s
->Add(address
= new wxTextCtrl(this, wxID_ANY
, wxT("0"), wxDefaultPosition
, wxSize(350, -1)), 0,
452 top_s
->Add(new wxStaticText(this, wxID_ANY
, wxT("Breakpoint type:")), 0, wxGROW
);
453 top_s
->Add(typesel
= new wxComboBox(this, wxID_ANY
, wxT(""), wxDefaultPosition
, wxDefaultSize
,
454 0, NULL
, wxCB_READONLY
), 1, wxGROW
);
455 typesel
->Append(towxstring("Read"));
456 typesel
->Append(towxstring("Write"));
457 typesel
->Append(towxstring("Execute"));
458 typesel
->SetSelection(0);
460 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
461 pbutton_s
->AddStretchSpacer();
462 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("OK")));
463 pbutton_s
->Add(cancel
= new wxButton(this, wxID_ANY
, wxT("Cancel")));
464 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoint_add::on_ok
), NULL
,
466 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoint_add::on_cancel
),
468 top_s
->Add(pbutton_s
, 0, wxGROW
);
469 top_s
->SetSizeHints(this);
473 void dialog_breakpoint_add::on_address_change(wxCommandEvent
& e
)
477 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
484 std::pair
<uint64_t, debug_context::etype
> dialog_breakpoint_add::get_result()
487 std::string vmaname
= tostdstring(vmasel
->GetStringSelection());
488 std::string addrtext
= tostdstring(address
->GetValue());
491 for(auto i
: regions
)
492 if(i
->name
== vmaname
)
497 addr
= base
+ hex::from
<uint64_t>(addrtext
);
498 } catch(std::exception
& e
) {
501 debug_context::etype dtype
= debug_context::DEBUG_EXEC
;
502 if(typesel
->GetSelection() == 0)
503 dtype
= debug_context::DEBUG_READ
;
504 if(typesel
->GetSelection() == 1)
505 dtype
= debug_context::DEBUG_WRITE
;
506 if(typesel
->GetSelection() == 2)
507 dtype
= debug_context::DEBUG_EXEC
;
508 return std::make_pair(addr
, dtype
);
512 class dialog_breakpoints
: public wxDialog
515 dialog_breakpoints(wxwin_tracelog
* parent
, emulator_instance
& _inst
);
516 void on_ok(wxCommandEvent
& e
) { EndModal(wxID_OK
); }
517 void on_add(wxCommandEvent
& e
);
518 void on_delete(wxCommandEvent
& e
);
519 void on_selchange(wxCommandEvent
& e
);
521 std::string
format_line(std::pair
<uint64_t, debug_context::etype
> entry
);
522 size_t get_insert_pos(std::pair
<uint64_t, debug_context::etype
> entry
);
523 void populate_breakpoints();
524 std::list
<memory_space::region
*> regions
;
525 emulator_instance
& inst
;
530 wxwin_tracelog
* pwin
;
531 std::vector
<std::pair
<uint64_t, debug_context::etype
>> listsyms
;
534 class wxwin_tracelog
: public wxFrame
, public debug_context::callback_base
537 wxwin_tracelog(wxWindow
* parent
, emulator_instance
& _inst
, int _cpuid
, const std::string
& cpuname
);
539 bool ShouldPreventAppExit() const { return false; }
540 scroll_bar
* get_scroll() { return scroll
; }
541 void on_wclose(wxCloseEvent
& e
);
542 void on_enabled(wxCommandEvent
& e
);
543 void on_menu(wxCommandEvent
& e
);
544 void process_lines();
545 uint64_t get_find_line() { return find_active
? find_line
: 0xFFFFFFFFFFFFFFFFULL
; }
546 std::set
<std::pair
<uint64_t, debug_context::etype
>> get_breakpoints();
547 void add_breakpoint(uint64_t addr
, debug_context::etype dtype
);
548 void remove_breakpoint(uint64_t addr
, debug_context::etype dtype
);
550 class _panel
: public text_framebuffer_panel
553 _panel(wxwin_tracelog
* parent
, emulator_instance
& _inst
);
554 void on_size(wxSizeEvent
& e
);
555 void on_mouse(wxMouseEvent
& e
);
556 wxSize
DoGetBestSize() const;
558 std::vector
<std::string
> rows
;
559 void on_popup_menu(wxCommandEvent
& e
);
560 bool scroll_to_end_on_repaint
;
562 void prepare_paint();
564 emulator_instance
& inst
;
565 uint64_t pressed_row
;
566 uint64_t current_row
;
570 emulator_instance
& inst
;
572 void scroll_pane(uint64_t line
);
574 volatile bool trace_active
;
575 void callback(const debug_context::params
& params
);
576 void killed(uint64_t addr
, debug_context::etype type
);
577 void do_rwx_break(uint64_t addr
, uint64_t value
, debug_context::etype type
);
578 void kill_debug_hooks(bool kill_hard
= false);
584 threads::lock buffer_mutex
;
585 std::list
<std::string
> lines_waiting
;
586 bool unprocessed_lines
;
590 std::string find_string
;
593 std::map
<std::pair
<uint64_t, debug_context::etype
>, bool> rwx_breakpoints
;
594 wxMenuItem
* m_singlestep
;
597 wxwin_tracelog::~wxwin_tracelog()
601 void wxwin_tracelog::on_wclose(wxCloseEvent
& e
)
604 if(dirty
&& !wxwidgets_exiting
) {
605 int r
= prompt_for_save(this, "Trace log");
606 if(r
< 0 || (r
> 0 && !do_exit_save()))
610 inst
.iqueue
->run([this]() { kill_debug_hooks(); });
611 trace_active
= false;
617 void wxwin_tracelog::kill_debug_hooks(bool kill_hard
)
619 CORE().dbg
->remove_callback(cpuid
, debug_context::DEBUG_FRAME
, *this);
621 CORE().dbg
->remove_callback(cpuid
, debug_context::DEBUG_TRACE
, *this);
622 threads::alock
h(buffer_mutex
);
623 for(auto& i
: rwx_breakpoints
) {
627 CORE().dbg
->remove_callback(i
.first
.first
, i
.first
.second
, *this);
630 trace_active
= false;
631 convert_break_to_pause();
634 wxwin_tracelog::_panel::_panel(wxwin_tracelog
* parent
, emulator_instance
& _inst
)
635 : text_framebuffer_panel(parent
, 20, 5, wxID_ANY
, NULL
), inst(_inst
)
643 scroll_to_end_on_repaint
= false;
644 this->Connect(wxEVT_SIZE
, wxSizeEventHandler(wxwin_tracelog::_panel::on_size
), NULL
, this);
645 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
646 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
647 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
648 this->Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(wxwin_tracelog::_panel::on_mouse
), NULL
, this);
651 void wxwin_tracelog::_panel::on_size(wxSizeEvent
& e
)
653 wxSize newsize
= e
.GetSize();
654 auto tcell
= get_cell();
655 size_t lines
= newsize
.y
/ tcell
.second
;
656 size_t linelen
= newsize
.x
/ tcell
.first
;
657 if(lines
< 1) lines
= 1;
658 if(linelen
< 1) linelen
= 1;
659 set_size(linelen
, lines
);
660 p
->get_scroll()->set_page_size(lines
);
665 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent
& e
)
668 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
670 if(local_line
< rows
.size()) {
672 pressed_row
= local_line
;
674 }else if(e
.RightUp()) {
677 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
,
678 wxCommandEventHandler(wxwin_tracelog::_panel::on_popup_menu
), NULL
, this);
679 menu
.Append(wxID_COPY
, wxT("Copy to clipboard"));
680 menu
.Append(wxID_SAVE
, wxT("Save to file"));
681 menu
.AppendSeparator();
682 menu
.Append(wxID_DELETE
, wxT("Delete"));
685 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
691 p
->get_scroll()->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
694 wxSize
wxwin_tracelog::_panel::DoGetBestSize() const
696 return wxSize(120 * 8, 25 * 16);
699 void wxwin_tracelog::_panel::prepare_paint()
701 p
->get_scroll()->set_range(rows
.size());
702 if(scroll_to_end_on_repaint
) {
703 scroll_to_end_on_repaint
= false;
704 p
->get_scroll()->set_position(rows
.size());
705 pos
= p
->get_scroll()->get_position();
707 uint64_t m
= min(pressed_row
, current_row
);
708 uint64_t M
= max(pressed_row
, current_row
);
709 auto s
= get_characters();
710 uint64_t fline
= p
->get_find_line();
711 for(uint64_t i
= pos
; i
< pos
+ s
.second
&& i
< rows
.size(); i
++) {
712 bool selected
= holding
&& (i
>= m
) && (i
<= M
);
713 bool isfl
= (i
== fline
);
714 uint32_t fg
= selected
? 0x0000FF : 0x000000;
715 uint32_t bg
= selected
? 0x000000 : (isfl
? 0xC0FFC0 : 0xFFFFFF);
716 write(rows
[i
], s
.first
, 0, i
- pos
, fg
, bg
);
718 for(uint64_t i
= rows
.size(); i
< pos
+ s
.second
; i
++)
719 write("", s
.first
, 0, i
- pos
, 0xFFFFFF, 0xFFFFFF);
722 void wxwin_tracelog::process_lines()
725 threads::alock
h(this->buffer_mutex
);
726 size_t osize
= panel
->rows
.size();
728 panel
->rows
.push_back(std::string(120, '-'));
731 for(auto& i
: lines_waiting
)
732 panel
->rows
.push_back(i
);
733 lines_waiting
.clear();
734 unprocessed_lines
= false;
735 if(panel
->rows
.size() != osize
) {
736 panel
->scroll_to_end_on_repaint
= true;
739 panel
->request_paint();
742 void wxwin_tracelog::do_rwx_break(uint64_t addr
, uint64_t value
, debug_context::etype type
)
744 inst
.dbg
->request_break();
747 void wxwin_tracelog::callback(const debug_context::params
& p
)
750 case debug_context::DEBUG_READ
:
751 case debug_context::DEBUG_WRITE
:
752 case debug_context::DEBUG_EXEC
:
753 do_rwx_break(p
.rwx
.addr
, p
.rwx
.value
, p
.type
);
755 case debug_context::DEBUG_TRACE
: {
758 //Got tracelog line, send it.
759 threads::alock
h(buffer_mutex
);
760 lines_waiting
.push_back(p
.trace
.decoded_insn
);
761 if(!unprocessed_lines
) {
762 unprocessed_lines
= true;
763 runuifun([this]() { this->process_lines(); });
765 if(singlestepping
&& p
.trace
.true_insn
) {
766 inst
.dbg
->request_break();
767 singlestepping
= false;
771 case debug_context::DEBUG_FRAME
: {
772 std::ostringstream xstr
;
773 xstr
<< "------------ ";
774 xstr
<< "Frame " << p
.frame
.frame
;
775 if(p
.frame
.loadstated
) xstr
<< " (loadstated)";
776 xstr
<< " ------------";
777 std::string str
= xstr
.str();
778 threads::alock
h(buffer_mutex
);
779 lines_waiting
.push_back(str
);
780 if(!unprocessed_lines
) {
781 unprocessed_lines
= true;
782 runuifun([this]() { this->process_lines(); });
789 void wxwin_tracelog::killed(uint64_t addr
, debug_context::etype type
)
792 case debug_context::DEBUG_READ
:
793 case debug_context::DEBUG_WRITE
:
794 case debug_context::DEBUG_EXEC
: {
795 //We need to kill this hook if still active.
796 auto i2
= std::make_pair(addr
, type
);
797 rwx_breakpoints
[i2
] = false;
800 case debug_context::DEBUG_TRACE
:
804 kill_debug_hooks(true);
806 this->enabled
->SetValue(false);
807 this->enabled
->Enable(false);
808 this->m_singlestep
->Enable(false);
811 case debug_context::DEBUG_FRAME
:
817 void wxwin_tracelog::on_enabled(wxCommandEvent
& e
)
820 bool enable
= enabled
->GetValue();
821 inst
.iqueue
->run([this, enable
]() {
823 threads::alock
h(buffer_mutex
);
826 for(auto& i
: rwx_breakpoints
) {
828 CORE().dbg
->add_callback(i2
.first
, i2
.second
, *this);
831 CORE().dbg
->add_callback(cpuid
, debug_context::DEBUG_TRACE
, *this);
832 CORE().dbg
->add_callback(0, debug_context::DEBUG_FRAME
, *this);
833 this->trace_active
= true;
834 } else if(trace_active
) {
835 this->trace_active
= false;
836 this->kill_debug_hooks();
839 m_singlestep
->Enable(enable
);
842 bool find_match(const std::string
& pattern
, const std::string
& candidate
)
844 static std::string last_find
;
845 static boost::regex regex
;
848 std::string tmp
= pattern
;
850 if(pattern
[0] == 'F')
851 return regex_match(tmp
, candidate
, REGEX_MATCH_LITERIAL
);
852 if(pattern
[0] == 'W')
853 return regex_match(tmp
, candidate
, REGEX_MATCH_IWILDCARDS
);
854 if(pattern
[0] == 'R')
855 return regex_match(tmp
, candidate
, REGEX_MATCH_IREGEX
);
859 void wxwin_tracelog::on_menu(wxCommandEvent
& e
)
862 if(e
.GetId() == wxID_EXIT
) {
864 int r
= prompt_for_save(this, "Trace log");
865 if(r
< 0 || (r
> 0 && !do_exit_save()))
869 inst
.iqueue
->run([this]() { this->kill_debug_hooks(); });
871 trace_active
= false;
874 } else if(e
.GetId() == wxID_SAVE
) {
876 std::string filename
= choose_file_save(this, "Save tracelog to",
877 UI_get_project_otherpath(inst
), filetype_trace
);
878 std::ofstream
s(filename
, std::ios::app
);
879 if(!s
) throw std::runtime_error("Error opening output file");
880 for(auto& i
: panel
->rows
)
882 if(!s
) throw std::runtime_error("Error writing output file");
884 } catch(canceled_exception
& e
) {
885 } catch(std::exception
& e
) {
886 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
889 } else if(e
.GetId() == wxID_FIND
) {
891 dialog_find
* d
= new dialog_find(this);
892 if(d
->ShowModal() != wxID_OK
) {
896 tmp
= d
->get_pattern();
907 wxMessageBox(towxstring("Invalid search pattern"), _T("Invalid pattern"),
908 wxICON_EXCLAMATION
| wxOK
, this);
914 while(find_line
< panel
->rows
.size()) {
915 if(find_match(find_string
, panel
->rows
[find_line
]))
919 if(find_line
== panel
->rows
.size()) {
922 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
923 wxICON_EXCLAMATION
| wxOK
, this);
925 scroll_pane(find_line
);
926 } else if(e
.GetId() == wxID_FIND_NEXT
) {
929 uint64_t old_find_line
= find_line
;
931 while(!panel
->rows
.empty() && find_line
!= old_find_line
) {
932 if(find_line
>= panel
->rows
.size())
934 if(find_match(find_string
, panel
->rows
[find_line
]))
938 scroll_pane(find_line
);
939 } else if(e
.GetId() == wxID_FIND_PREV
) {
942 uint64_t old_find_line
= find_line
;
944 while(!panel
->rows
.empty() && find_line
!= old_find_line
) {
945 if(find_line
>= panel
->rows
.size())
946 find_line
= panel
->rows
.size() - 1;
947 if(find_match(find_string
, panel
->rows
[find_line
]))
951 scroll_pane(find_line
);
952 } else if(e
.GetId() == wxID_SINGLESTEP
) {
953 inst
.iqueue
->run_async([this]() {
954 this->singlestepping
= true;
955 CORE().command
->invoke("unpause-emulator");
956 }, [](std::exception
& e
) {});
957 } else if(e
.GetId() == wxID_FRAMEADVANCE
) {
958 inst
.iqueue
->run_async([this]() {
959 CORE().command
->invoke("+advance-frame");
960 CORE().command
->invoke("-advance-frame");
961 }, [](std::exception
& e
) {});
962 } else if(e
.GetId() == wxID_CONTINUE
) {
963 inst
.iqueue
->run_async([this]() {
964 CORE().command
->invoke("unpause-emulator");
965 }, [](std::exception
& e
) {});
966 } else if(e
.GetId() == wxID_BREAKPOINTS
) {
967 dialog_breakpoints
* d
= new dialog_breakpoints(this, inst
);
970 } else if(e
.GetId() == wxID_CLEAR
) {
971 int r
= prompt_for_save(this, "Trace log");
972 if(r
< 0 || (r
> 0 && !do_exit_save()))
975 panel
->request_paint();
980 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent
& e
)
984 uint64_t m
= min(pressed_row
, current_row
);
985 uint64_t M
= max(pressed_row
, current_row
) + 1;
986 m
= min(m
, (uint64_t)rows
.size());
987 M
= min(M
, (uint64_t)rows
.size());
990 for(uint64_t i
= m
; i
< M
&& i
< rows
.size(); i
++) {
992 std::string mline
= rows
[i
];
993 if(lines
== 1) str
+= "\n";
995 if(lines
>= 1) str
+= "\n";
1003 if (wxTheClipboard
->Open()) {
1004 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
1005 wxTheClipboard
->Close();
1010 std::string filename
= choose_file_save(this, "Save tracelog fragment to",
1011 UI_get_project_otherpath(inst
), filetype_trace
);
1012 std::ofstream
s(filename
, std::ios::app
);
1013 if(!s
) throw std::runtime_error("Error opening output file");
1014 if(lines
== 1) str
+= "\n";
1016 if(!s
) throw std::runtime_error("Error writing output file");
1017 } catch(canceled_exception
& e
) {
1018 } catch(std::exception
& e
) {
1019 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1024 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1032 void wxwin_tracelog::scroll_pane(uint64_t line
)
1034 unsigned r
= panel
->get_characters().second
;
1035 unsigned offset
= r
/ 2;
1037 scroll
->set_position(panel
->pos
= 0);
1038 else if(line
+ r
<= panel
->rows
.size())
1039 scroll
->set_position(panel
->pos
= line
- offset
);
1041 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1042 panel
->request_paint();
1045 bool wxwin_tracelog::do_exit_save()
1050 std::string filename
= choose_file_save(this, "Save tracelog to",
1051 UI_get_project_otherpath(inst
), filetype_trace
);
1052 std::ofstream
s(filename
, std::ios::app
);
1053 if(!s
) throw std::runtime_error("Error opening output file");
1054 for(auto& i
: panel
->rows
)
1055 s
<< i
<< std::endl
;
1056 if(!s
) throw std::runtime_error("Error writing output file");
1058 } catch(canceled_exception
& e
) {
1060 } catch(std::exception
& e
) {
1061 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1068 std::set
<std::pair
<uint64_t, debug_context::etype
>> wxwin_tracelog::get_breakpoints()
1070 std::set
<std::pair
<uint64_t, debug_context::etype
>> ret
;
1071 inst
.iqueue
->run([this, &ret
]() {
1072 for(auto i
: rwx_breakpoints
)
1073 ret
.insert(i
.first
);
1078 void wxwin_tracelog::add_breakpoint(uint64_t addr
, debug_context::etype dtype
)
1080 std::pair
<uint64_t, debug_context::etype
> i2
= std::make_pair(addr
, dtype
);
1082 //We'll register this later.
1083 rwx_breakpoints
[i2
] = false;
1086 inst
.dbg
->add_callback(i2
.first
, i2
.second
, *this);
1087 rwx_breakpoints
[i2
] = true;
1090 void wxwin_tracelog::remove_breakpoint(uint64_t addr
, debug_context::etype dtype
)
1092 std::pair
<uint64_t, debug_context::etype
> i2
= std::make_pair(addr
, dtype
);
1093 auto& h
= rwx_breakpoints
[i2
];
1095 inst
.dbg
->remove_callback(i2
.first
, i2
.second
, *this);
1096 rwx_breakpoints
.erase(i2
);
1099 wxwin_tracelog::wxwin_tracelog(wxWindow
* parent
, emulator_instance
& _inst
, int _cpuid
,
1100 const std::string
& cpuname
)
1101 : wxFrame(parent
, wxID_ANY
, towxstring("lsnes: Tracelog for " + cpuname
), wxDefaultPosition
,
1102 wxDefaultSize
, wxMINIMIZE_BOX
| wxRESIZE_BORDER
| wxSYSTEM_MENU
| wxCAPTION
| wxCLOSE_BOX
|
1103 wxCLIP_CHILDREN
), inst(_inst
)
1107 singlestepping
= false;
1108 find_active
= false;
1111 trace_active
= false;
1112 unprocessed_lines
= false;
1116 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
1118 wxBoxSizer
* bottom_s
= new wxBoxSizer(wxHORIZONTAL
);
1119 top_s
->Add(enabled
= new wxCheckBox(this, wxID_ANY
, wxT("Enabled")), 0, wxGROW
);
1120 bottom_s
->Add(panel
= new _panel(this, inst
), 1, wxGROW
);
1121 bottom_s
->Add(scroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
1122 top_s
->Add(bottom_s
, 1, wxGROW
);
1123 enabled
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
, wxCommandEventHandler(wxwin_tracelog::on_enabled
),
1125 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_tracelog::on_wclose
),
1127 scroll
->set_page_size(panel
->get_characters().second
);
1128 scroll
->set_handler([this](scroll_bar
& s
) {
1129 this->panel
->pos
= s
.get_position();
1130 this->panel
->request_paint();
1136 SetMenuBar(mb
= new wxMenuBar
);
1137 SetStatusBar(sb
= new wxStatusBar(this));
1138 mb
->Append(menu
= new wxMenu(), wxT("File"));
1139 menu
->Append(wxID_SAVE
, wxT("Save"));
1140 menu
->AppendSeparator();
1141 menu
->Append(wxID_EXIT
, wxT("Close"));
1142 mb
->Append(menu
= new wxMenu(), wxT("Edit"));
1143 menu
->Append(wxID_FIND
, wxT("Find..."));
1144 menu
->Append(wxID_FIND_NEXT
, wxT("Find next\tF3"));
1145 menu
->Append(wxID_FIND_PREV
, wxT("Find previous\tSHIFT+F3"));
1146 menu
->AppendSeparator();
1147 menu
->Append(wxID_CLEAR
, towxstring("Clear"));
1148 mb
->Append(menu
= new wxMenu(), wxT("Debug"));
1149 m_singlestep
= menu
->Append(wxID_SINGLESTEP
, towxstring("Singlestep\tF2"));
1150 menu
->Append(wxID_FRAMEADVANCE
, towxstring("Frame advance\tF4"));
1151 menu
->Append(wxID_CONTINUE
, towxstring("Continue\tF5"));
1152 menu
->AppendSeparator();
1153 menu
->Append(wxID_BREAKPOINTS
, towxstring("Breakpoints"));
1154 m_singlestep
->Enable(false);
1155 Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxwin_tracelog::on_menu
),
1158 wxSize tmp
= panel
->GetMinSize();
1159 panel
->SetMinSize(panel
->DoGetBestSize());
1160 top_s
->SetSizeHints(this);
1161 wxSize tmp2
= GetClientSize();
1162 panel
->SetMinSize(tmp
);
1163 top_s
->SetSizeHints(this);
1164 SetClientSize(tmp2
);
1170 std::string language
;
1174 class wxwin_disassembler
: public wxFrame
1177 wxwin_disassembler(wxWindow
* parent
, emulator_instance
& _inst
);
1178 bool ShouldPreventAppExit() const { return false; }
1179 scroll_bar
* get_scroll() { return scroll
; }
1180 void on_menu(wxCommandEvent
& e
);
1181 void on_wclose(wxCloseEvent
& e
);
1183 class _panel
: public text_framebuffer_panel
1186 _panel(wxwin_disassembler
* parent
, emulator_instance
& _inst
);
1187 void on_size(wxSizeEvent
& e
);
1188 void on_mouse(wxMouseEvent
& e
);
1189 wxSize
DoGetBestSize() const;
1191 std::vector
<uint64_t> rows
;
1192 std::map
<uint64_t, disasm_row
> row_map
;
1193 void on_popup_menu(wxCommandEvent
& e
);
1195 void prepare_paint();
1197 emulator_instance
& inst
;
1198 uint64_t pressed_row
;
1199 uint64_t current_row
;
1201 wxwin_disassembler
* p
;
1203 bool do_exit_save();
1204 void add_row(uint64_t addr
, const disasm_row
& row
, bool last
);
1205 void add_rows(const std::map
<uint64_t, disasm_row
>& rowdata
);
1206 void add_rows_main(const std::map
<uint64_t, disasm_row
>& rowdata
);
1207 void run_disassembler(const std::string
& disasm
, uint64_t addrbase
, uint64_t count
);
1208 void scroll_pane(uint64_t line
);
1209 emulator_instance
& inst
;
1216 wxwin_disassembler::_panel::_panel(wxwin_disassembler
* parent
, emulator_instance
& _inst
)
1217 : text_framebuffer_panel(parent
, 20, 5, wxID_ANY
, NULL
), inst(_inst
)
1225 this->Connect(wxEVT_SIZE
, wxSizeEventHandler(wxwin_disassembler::_panel::on_size
), NULL
, this);
1226 this->Connect(wxEVT_RIGHT_DOWN
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
,
1228 this->Connect(wxEVT_RIGHT_UP
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
, this);
1229 this->Connect(wxEVT_MOTION
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
, this);
1230 this->Connect(wxEVT_MOUSEWHEEL
, wxMouseEventHandler(wxwin_disassembler::_panel::on_mouse
), NULL
,
1234 void wxwin_disassembler::_panel::on_size(wxSizeEvent
& e
)
1236 wxSize newsize
= e
.GetSize();
1237 auto tcell
= get_cell();
1238 size_t lines
= newsize
.y
/ tcell
.second
;
1239 size_t linelen
= newsize
.x
/ tcell
.first
;
1240 if(lines
< 1) lines
= 1;
1241 if(linelen
< 1) linelen
= 1;
1242 set_size(linelen
, lines
);
1243 p
->get_scroll()->set_page_size(lines
);
1248 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent
& e
)
1251 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
1253 if(local_line
< rows
.size()) {
1255 pressed_row
= local_line
;
1257 }else if(e
.RightUp()) {
1260 menu
.Connect(wxEVT_COMMAND_MENU_SELECTED
,
1261 wxCommandEventHandler(wxwin_disassembler::_panel::on_popup_menu
), NULL
, this);
1262 menu
.Append(wxID_COPY
, wxT("Copy to clipboard"));
1263 menu
.Append(wxID_SAVE
, wxT("Save to file"));
1264 menu
.AppendSeparator();
1265 menu
.Append(wxID_DISASM_MORE
, wxT("Disassemble more"));
1266 menu
.AppendSeparator();
1267 menu
.Append(wxID_DELETE
, wxT("Delete"));
1270 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
1276 p
->get_scroll()->apply_wheel(e
.GetWheelRotation(), e
.GetWheelDelta(), speed
);
1279 wxSize
wxwin_disassembler::_panel::DoGetBestSize() const
1281 return wxSize(40 * 8, 25 * 16);
1284 void wxwin_disassembler::_panel::prepare_paint()
1286 p
->get_scroll()->set_range(rows
.size());
1287 uint64_t m
= min(pressed_row
, current_row
);
1288 uint64_t M
= max(pressed_row
, current_row
);
1289 auto s
= get_characters();
1291 for(i
= pos
; i
< pos
+ s
.second
&& i
< rows
.size(); i
++) {
1292 bool selected
= holding
&& (i
>= m
) && (i
<= M
);
1293 uint32_t fg
= selected
? 0x0000FF : 0x000000;
1294 uint32_t bg
= selected
? 0x000000 : 0xFFFFFF;
1295 write(row_map
[rows
[i
]].row
, s
.first
, 0, i
- pos
, fg
, bg
);
1297 for(; i
< pos
+ s
.second
; i
++) {
1298 write("", s
.first
, 0, i
- pos
, 0xFFFFFF, 0xFFFFFF);
1302 void wxwin_disassembler::on_menu(wxCommandEvent
& e
)
1305 if(e
.GetId() == wxID_EXIT
) {
1307 int r
= prompt_for_save(this, "Disassembly");
1308 if(r
< 0 || (r
> 0 && !do_exit_save()))
1313 } else if(e
.GetId() == wxID_SAVE
) {
1315 std::string filename
= choose_file_save(this, "Save disassembly to",
1316 UI_get_project_otherpath(inst
), filetype_disassembly
);
1317 std::ofstream
s(filename
, std::ios::app
);
1318 if(!s
) throw std::runtime_error("Error opening output file");
1319 for(auto& i
: panel
->rows
)
1320 s
<< panel
->row_map
[i
].row
<< std::endl
;
1321 if(!s
) throw std::runtime_error("Error writing output file");
1323 } catch(canceled_exception
& e
) {
1324 } catch(std::exception
& e
) {
1325 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1328 } else if(e
.GetId() == wxID_DISASM
) {
1330 dialog_disassemble
* d
= new dialog_disassemble(this, inst
);
1331 if(d
->ShowModal() != wxID_OK
) {
1335 std::string disasm
= d
->get_disassembler();
1336 uint64_t addr
= d
->get_address();
1337 uint64_t count
= d
->get_count();
1339 inst
.iqueue
->run_async([this, disasm
, addr
, count
]() {
1340 this->run_disassembler(disasm
, addr
, count
);
1341 }, [](std::exception
& e
) {});
1342 } else if(e
.GetId() == wxID_GOTO
) {
1344 std::string to
= pick_text(this, "Goto", "Enter address to go to:", "");
1345 inst
.iqueue
->run_async([this, to
]() {
1350 std::string _to
= to
;
1351 size_t sp
= _to
.find_first_of("+");
1352 if(sp
>= _to
.length()) {
1355 vma
= _to
.substr(0, sp
);
1356 offset
= _to
.substr(sp
+ 1);
1360 for(auto i
: CORE().memory
->get_regions()) {
1361 if(i
->name
== vma
) {
1368 show_message_ok(this, "Error in address",
1369 "No such memory area known",
1370 wxICON_EXCLAMATION
);
1375 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1376 " <name>+<hexdigits>", [&addr
, offset
]() {
1377 addr
= hex::from
<uint64_t>(offset
); }))
1380 runuifun([this, addr
]() {
1383 uint64_t high
= this->panel
->rows
.size();
1384 while(low
< high
&& low
< high
- 1) {
1385 nrow
= (low
+ high
) / 2;
1386 if(this->panel
->rows
[nrow
] > addr
)
1388 else if(this->panel
->rows
[nrow
] < addr
)
1393 this->scroll_pane(nrow
);
1395 }, [](std::exception
& e
) {});
1396 } catch(canceled_exception
& e
) {
1401 void remove_from_array(std::vector
<uint64_t>& v
, uint64_t e
)
1403 //Binary search for the element to remove.
1405 size_t high
= v
.size();
1408 mid
= (low
+ high
) / 2;
1417 v
.erase(v
.begin() + mid
);
1420 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent
& e
)
1423 if(e
.GetId() == wxID_DISASM_MORE
)
1425 if(current_row
>= rows
.size())
1427 uint64_t base
= rows
[current_row
];
1428 uint64_t rbase
= base
;
1429 if(!row_map
.count(base
))
1431 auto& r
= row_map
[base
];
1432 base
= base
+ r
.cover
;
1433 std::string disasm
= r
.language
;
1434 dialog_disassemble
* d
= new dialog_disassemble(this, inst
, base
, disasm
);
1435 if(d
->ShowModal() != wxID_OK
) {
1439 disasm
= d
->get_disassembler();
1440 uint64_t addr
= d
->get_address();
1441 uint64_t count
= d
->get_count();
1444 inst
.iqueue
->run_async([pp
, disasm
, addr
, count
]() {
1445 pp
->run_disassembler(disasm
, addr
, count
);
1446 }, [](std::exception
& e
) {});
1447 //Delete entries in (rbase, addr) if addr = base.
1449 for(uint64_t i
= rbase
+ 1; i
< addr
; i
++)
1450 if(row_map
.count(i
)) {
1451 //This line needs to be removed from rows too.
1453 remove_from_array(rows
, i
);
1458 uint64_t m
= min(min(pressed_row
, current_row
), (uint64_t)rows
.size());
1459 uint64_t M
= min(max(pressed_row
, current_row
) + 1, (uint64_t)rows
.size());
1462 for(uint64_t i
= m
; i
< M
; i
++) {
1464 std::string mline
= row_map
[rows
[i
]].row
;
1465 if(lines
== 1) str
+= "\n";
1467 if(lines
>= 1) str
+= "\n";
1475 if (wxTheClipboard
->Open()) {
1476 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
1477 wxTheClipboard
->Close();
1482 std::string filename
= choose_file_save(this, "Save disassembly fragment to",
1483 UI_get_project_otherpath(inst
), filetype_disassembly
);
1484 std::ofstream
s(filename
, std::ios::app
);
1485 if(!s
) throw std::runtime_error("Error opening output file");
1486 if(lines
== 1) str
+= "\n";
1488 if(!s
) throw std::runtime_error("Error writing output file");
1489 } catch(canceled_exception
& e
) {
1490 } catch(std::exception
& e
) {
1491 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1496 for(uint64_t i
= m
; i
< M
; i
++)
1497 row_map
.erase(rows
[i
]);
1498 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1506 std::string
format_vma_offset(memory_space::region
& region
, uint64_t offset
)
1508 std::ostringstream y
;
1510 size_t sizedigits
= 0;
1511 uint64_t tmp
= region
.size
- 1;
1516 y
<< "+" << std::hex
<< std::setfill('0') << std::setw(sizedigits
) << offset
;
1520 std::string
lookup_address(emulator_instance
& inst
, uint64_t raw
)
1522 auto g
= inst
.memory
->lookup(raw
);
1524 return hex::to
<uint64_t>(raw
);
1526 return format_vma_offset(*g
.first
, g
.second
);
1529 inline int sign_compare(uint64_t a
, uint64_t b
)
1531 if(a
< b
) return -1;
1536 void insert_into_array(std::vector
<uint64_t>& v
, uint64_t e
)
1538 //Binary search for the gap to insert to.
1540 size_t high
= v
.size();
1543 mid
= (low
+ high
) / 2;
1544 int s1
= sign_compare(v
[mid
], e
);
1545 int s2
= ((mid
+ 1) < v
.size()) ? sign_compare(v
[mid
+ 1], e
) : 1;
1546 if(s1
< 0 && s2
> 0)
1548 else if(s1
== 0 || s2
== 0)
1555 if(mid
< v
.size() && v
[mid
] < e
)
1557 v
.insert(v
.begin() + mid
, e
);
1560 void wxwin_disassembler::add_row(uint64_t addr
, const disasm_row
& row
, bool last
)
1562 auto& rows
= panel
->rows
;
1563 auto& row_map
= panel
->row_map
;
1564 if(row_map
.count(addr
)) {
1565 row_map
[addr
] = row
;
1567 //We need to insert the row into rows.
1568 row_map
[addr
] = row
;
1569 insert_into_array(rows
, addr
);
1573 for(uint64_t i
= addr
+ 1; i
< addr
+ row
.cover
; i
++)
1574 if(row_map
.count(i
)) {
1575 //This line needs to be removed from rows too.
1577 remove_from_array(rows
, i
);
1581 void wxwin_disassembler::add_rows(const std::map
<uint64_t, disasm_row
>& rowdata
)
1583 for(auto i
= rowdata
.begin(); i
!= rowdata
.end(); i
++) {
1586 bool last
= (j
== rowdata
.end());
1587 add_row(i
->first
, i
->second
, last
);
1589 panel
->request_paint();
1592 void wxwin_disassembler::add_rows_main(const std::map
<uint64_t, disasm_row
>& rowdata
)
1594 std::map
<uint64_t, disasm_row
> _rowdata
;
1595 for(auto& i
: rowdata
) {
1596 _rowdata
[i
.first
] = i
.second
;
1597 _rowdata
[i
.first
].row
= lookup_address(inst
, i
.first
) + " " + i
.second
.row
;
1599 runuifun([this, _rowdata
]() { this->add_rows(_rowdata
); });
1602 template<typename T
, bool hex
> disasm_row
_disassemble_data_item(emulator_instance
& inst
, uint64_t& addrbase
,
1603 int endian
, const std::string
& disasm
)
1605 char buf
[sizeof(T
)];
1606 for(size_t i
= 0; i
< sizeof(T
); i
++)
1607 buf
[i
] = inst
.memory
->read
<uint8_t>(addrbase
+ i
);
1610 r
.row
= (stringfmt() << "DATA 0x" << hex::to
<T
>(serialization::read_endian
<T
>(buf
, endian
))).
1612 else if(sizeof(T
) > 1)
1613 r
.row
= (stringfmt() << "DATA " << serialization::read_endian
<T
>(buf
, endian
)).str();
1615 r
.row
= (stringfmt() << "DATA " << (int)serialization::read_endian
<T
>(buf
, endian
)).str();
1616 r
.cover
= sizeof(T
);
1617 r
.language
= disasm
;
1618 addrbase
+= sizeof(T
);
1622 disasm_row
disassemble_data_item(emulator_instance
& inst
, uint64_t& addrbase
, const std::string
& disasm
)
1625 if(disasm
[7] == 'l') endian
= -1;
1626 if(disasm
[7] == 'h') endian
= 0;
1627 if(disasm
[7] == 'b') endian
= 1;
1629 case 'b': return _disassemble_data_item
<int8_t, false>(inst
, addrbase
, endian
, disasm
);
1630 case 'B': return _disassemble_data_item
<uint8_t, false>(inst
, addrbase
, endian
, disasm
);
1631 case 'c': return _disassemble_data_item
<uint8_t, true>(inst
, addrbase
, endian
, disasm
);
1632 case 'C': return _disassemble_data_item
<uint16_t, true>(inst
, addrbase
, endian
, disasm
);
1633 case 'd': return _disassemble_data_item
<int32_t, false>(inst
, addrbase
, endian
, disasm
);
1634 case 'D': return _disassemble_data_item
<uint32_t, false>(inst
, addrbase
, endian
, disasm
);
1635 case 'f': return _disassemble_data_item
<float, false>(inst
, addrbase
, endian
, disasm
);
1636 case 'F': return _disassemble_data_item
<double, false>(inst
, addrbase
, endian
, disasm
);
1637 case 'h': return _disassemble_data_item
<ss_int24_t
, false>(inst
, addrbase
, endian
, disasm
);
1638 case 'H': return _disassemble_data_item
<ss_uint24_t
, false>(inst
, addrbase
, endian
, disasm
);
1639 case 'i': return _disassemble_data_item
<ss_uint24_t
, true>(inst
, addrbase
, endian
, disasm
);
1640 case 'I': return _disassemble_data_item
<uint32_t, true>(inst
, addrbase
, endian
, disasm
);
1641 case 'q': return _disassemble_data_item
<int64_t, false>(inst
, addrbase
, endian
, disasm
);
1642 case 'Q': return _disassemble_data_item
<uint64_t, false>(inst
, addrbase
, endian
, disasm
);
1643 case 'r': return _disassemble_data_item
<uint64_t, true>(inst
, addrbase
, endian
, disasm
);
1644 case 'w': return _disassemble_data_item
<int16_t, false>(inst
, addrbase
, endian
, disasm
);
1645 case 'W': return _disassemble_data_item
<uint16_t, false>(inst
, addrbase
, endian
, disasm
);
1647 throw std::runtime_error("Invalid kind of data");
1650 void wxwin_disassembler::scroll_pane(uint64_t line
)
1652 unsigned r
= panel
->get_characters().second
;
1653 unsigned offset
= r
/ 2;
1655 scroll
->set_position(panel
->pos
= 0);
1656 else if(line
+ r
< panel
->rows
.size())
1657 scroll
->set_position(panel
->pos
= line
- offset
);
1659 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1660 panel
->request_paint();
1663 void wxwin_disassembler::on_wclose(wxCloseEvent
& e
)
1666 if(dirty
&& !wxwidgets_exiting
) {
1667 int r
= prompt_for_save(this, "Disassembly");
1668 if(r
< 0 || (r
> 0 && !do_exit_save()))
1676 void wxwin_disassembler::run_disassembler(const std::string
& disasm
, uint64_t addrbase
, uint64_t count
)
1678 std::map
<uint64_t, disasm_row
> rowdata
;
1679 if(regex_match("\\$data:.*", disasm
)) {
1680 if(run_show_error(this, "Error in disassember", "Error in disassember",
1681 [this, disasm
, &rowdata
, &addrbase
, count
]() {
1682 for(uint64_t i
= 0; i
< count
; i
++) {
1683 uint64_t base
= addrbase
;
1684 disasm_row r
= disassemble_data_item(this->inst
, addrbase
, disasm
);
1689 add_rows_main(rowdata
);
1693 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm
+ "' found",
1695 d
= &disassembler::byname(disasm
);
1698 for(uint64_t i
= 0; i
< count
; i
++) {
1699 uint64_t base
= addrbase
;
1701 r
.row
= d
->disassemble(addrbase
, [this, &addrbase
]() -> unsigned char {
1702 return this->inst
.memory
->read
<uint8_t>(addrbase
++);
1704 r
.cover
= addrbase
- base
;
1705 r
.language
= disasm
;
1708 add_rows_main(rowdata
);
1711 bool wxwin_disassembler::do_exit_save()
1716 std::string filename
= choose_file_save(this, "Save disassembly to",
1717 UI_get_project_otherpath(inst
), filetype_disassembly
);
1718 std::ofstream
s(filename
, std::ios::app
);
1719 if(!s
) throw std::runtime_error("Error opening output file");
1720 for(auto& i
: panel
->rows
)
1721 s
<< panel
->row_map
[i
].row
<< std::endl
;
1722 if(!s
) throw std::runtime_error("Error writing output file");
1724 } catch(canceled_exception
& e
) {
1726 } catch(std::exception
& e
) {
1727 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1734 wxwin_disassembler::wxwin_disassembler(wxWindow
* parent
, emulator_instance
& _inst
)
1735 : wxFrame(parent
, wxID_ANY
, towxstring("lsnes: Disassembler"), wxDefaultPosition
,
1736 wxDefaultSize
, wxMINIMIZE_BOX
| wxRESIZE_BORDER
| wxSYSTEM_MENU
| wxCAPTION
| wxCLOSE_BOX
|
1737 wxCLIP_CHILDREN
), inst(_inst
)
1742 wxBoxSizer
* top_s
= new wxBoxSizer(wxHORIZONTAL
);
1744 top_s
->Add(panel
= new _panel(this, inst
), 1, wxGROW
);
1745 top_s
->Add(scroll
= new scroll_bar(this, wxID_ANY
, true), 0, wxGROW
);
1746 scroll
->set_page_size(panel
->get_characters().second
);
1747 scroll
->set_handler([this](scroll_bar
& s
) {
1748 this->panel
->pos
= s
.get_position();
1749 this->panel
->request_paint();
1755 SetMenuBar(mb
= new wxMenuBar
);
1756 SetStatusBar(sb
= new wxStatusBar(this));
1757 mb
->Append(menu
= new wxMenu(), wxT("File"));
1758 menu
->Append(wxID_DISASM
, wxT("Disassemble..."));
1759 menu
->AppendSeparator();
1760 menu
->Append(wxID_SAVE
, wxT("Save"));
1761 menu
->AppendSeparator();
1762 menu
->Append(wxID_EXIT
, wxT("Close"));
1763 Connect(wxEVT_COMMAND_MENU_SELECTED
, wxCommandEventHandler(wxwin_disassembler::on_menu
),
1765 mb
->Append(menu
= new wxMenu(), wxT("Edit"));
1766 menu
->Append(wxID_GOTO
, wxT("Goto"));
1768 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_disassembler::on_wclose
),
1771 wxSize tmp
= panel
->GetMinSize();
1772 panel
->SetMinSize(panel
->DoGetBestSize());
1773 top_s
->SetSizeHints(this);
1774 wxSize tmp2
= GetClientSize();
1775 panel
->SetMinSize(tmp
);
1776 top_s
->SetSizeHints(this);
1777 SetClientSize(tmp2
);
1780 dialog_breakpoints::dialog_breakpoints(wxwin_tracelog
* parent
, emulator_instance
& _inst
)
1781 : wxDialog(parent
, wxID_ANY
, wxT("Breakpoints")), inst(_inst
)
1785 regions
= inst
.memory
->get_regions();
1786 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
1788 top_s
->Add(brklist
= new wxListBox(this, wxID_ANY
, wxDefaultPosition
, wxSize(300, 400)), 1, wxGROW
);
1789 brklist
->Connect(wxEVT_COMMAND_LISTBOX_SELECTED
,
1790 wxCommandEventHandler(dialog_breakpoints::on_selchange
), NULL
, this);
1791 populate_breakpoints();
1792 wxBoxSizer
* pbutton_s
= new wxBoxSizer(wxHORIZONTAL
);
1793 pbutton_s
->Add(addb
= new wxButton(this, wxID_ANY
, wxT("Add")), 0, wxGROW
);
1794 pbutton_s
->Add(delb
= new wxButton(this, wxID_ANY
, wxT("Remove")), 0, wxGROW
);
1795 pbutton_s
->AddStretchSpacer();
1796 pbutton_s
->Add(ok
= new wxButton(this, wxID_ANY
, wxT("Close")), 0, wxGROW
);
1797 delb
->Enable(false);
1798 addb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_add
), NULL
,
1800 delb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_delete
),
1802 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_ok
), NULL
,
1804 top_s
->Add(pbutton_s
, 0, wxGROW
);
1805 top_s
->SetSizeHints(this);
1809 void dialog_breakpoints::populate_breakpoints()
1812 auto t
= pwin
->get_breakpoints();
1814 std::string line
= format_line(i
);
1815 unsigned insert_pos
= get_insert_pos(i
);
1816 brklist
->Insert(towxstring(line
), insert_pos
);
1817 listsyms
.insert(listsyms
.begin() + insert_pos
, i
);
1821 void dialog_breakpoints::on_add(wxCommandEvent
& e
)
1825 debug_context::etype dtype
;
1826 dialog_breakpoint_add
* d
= new dialog_breakpoint_add(this, regions
);
1827 if(d
->ShowModal() != wxID_OK
) {
1831 rpair(addr
, dtype
) = d
->get_result();
1833 inst
.iqueue
->run_async([this, addr
, dtype
]() {
1834 pwin
->add_breakpoint(addr
, dtype
);
1835 }, [](std::exception
& e
) {});
1836 auto ent
= std::make_pair(addr
, dtype
);
1837 std::string line
= format_line(ent
);
1838 unsigned insert_pos
= get_insert_pos(ent
);
1839 brklist
->Insert(towxstring(line
), insert_pos
);
1840 listsyms
.insert(listsyms
.begin() + insert_pos
, ent
);
1843 void dialog_breakpoints::on_delete(wxCommandEvent
& e
)
1846 int idx
= brklist
->GetSelection();
1847 if(idx
== wxNOT_FOUND
)
1850 debug_context::etype dtype
;
1851 addr
= listsyms
[idx
].first
;
1852 dtype
= listsyms
[idx
].second
;
1853 inst
.iqueue
->run_async([this, addr
, dtype
]() {
1854 pwin
->remove_breakpoint(addr
, dtype
);
1855 }, [](std::exception
& e
) {});
1856 brklist
->Delete(idx
);
1857 listsyms
.erase(listsyms
.begin() + idx
);
1860 size_t dialog_breakpoints::get_insert_pos(std::pair
<uint64_t, debug_context::etype
> entry
)
1863 for(i
= 0; i
< listsyms
.size(); i
++)
1864 if(entry
< listsyms
[i
])
1869 std::string
dialog_breakpoints::format_line(std::pair
<uint64_t, debug_context::etype
> entry
)
1871 std::string base
= "";
1872 for(auto i
: regions
) {
1873 if(entry
.first
>= i
->base
&& entry
.first
< i
->base
+ i
->size
) {
1874 base
= format_vma_offset(*i
, entry
.first
- i
->base
);
1879 base
= hex::to
<uint64_t>(entry
.first
);
1880 if(entry
.second
== debug_context::DEBUG_READ
)
1881 return base
+ ": Read";
1882 if(entry
.second
== debug_context::DEBUG_WRITE
)
1883 return base
+ ": Write";
1884 if(entry
.second
== debug_context::DEBUG_EXEC
)
1885 return base
+ ": Execute";
1886 return base
+ ": Unknown";
1889 void dialog_breakpoints::on_selchange(wxCommandEvent
& e
)
1892 delb
->Enable(brklist
->GetSelection() != wxNOT_FOUND
);
1896 void wxeditor_tracelog_display(wxWindow
* parent
, emulator_instance
& inst
, int cpuid
, const std::string
& cpuname
)
1900 wxwin_tracelog
* d
= new wxwin_tracelog(parent
, inst
, cpuid
, cpuname
);
1902 } catch(std::exception
& e
) {
1903 show_message_ok(parent
, "Error opening trace logger", e
.what(), wxICON_EXCLAMATION
);
1907 void wxeditor_disassembler_display(wxWindow
* parent
, emulator_instance
& inst
)
1910 wxwin_disassembler
* d
= new wxwin_disassembler(parent
, inst
);