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>
43 wxID_FIND_NEXT
= wxID_HIGHEST
+ 1,
55 int prompt_for_save(wxWindow
* parent
, const std::string
& what
)
58 wxMessageDialog
* d
= new wxMessageDialog(parent
, towxstring(what
+ " has unsaved changes, "
59 "save before closing?"), towxstring("Save on exit?"), wxCENTER
| wxYES_NO
| wxCANCEL
|
61 d
->SetYesNoCancelLabels(wxT("Save"), wxT("Discard"), wxT("Cancel"));
62 int r
= d
->ShowModal();
64 if(r
== wxID_YES
) return 1;
65 if(r
== wxID_NO
) return 0;
66 if(r
== wxID_CANCEL
) return -1;
70 class dialog_find
: public wxDialog
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
); }
84 dialog_find::dialog_find(wxWindow
* parent
)
85 : wxDialog(parent
, wxID_ANY
, wxT("Find"))
88 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
,
108 top_s
->Add(pbutton_s
, 0, wxGROW
);
109 top_s
->SetSizeHints(this);
113 std::string
dialog_find::get_pattern()
116 if(tostdstring(text
->GetValue()) == "")
118 if(type
->GetSelection() == 2)
119 return "R" + tostdstring(text
->GetValue());
120 else if(type
->GetSelection() == 1)
121 return "W" + tostdstring(text
->GetValue());
123 return "F" + tostdstring(text
->GetValue());
126 class dialog_disassemble
: public wxDialog
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
); }
139 void init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
);
140 emulator_instance
& inst
;
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
)
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
)
169 init(true, dflt_base
, dflt_lang
);
172 void dialog_disassemble::init(bool spec
, uint64_t dflt_base
, std::string dflt_lang
)
175 std::map
<std::string
, std::pair
<uint64_t, uint64_t>> regions
;
176 std::set
<std::string
> disasms
;
177 inst
.iqueue
->run([®ions
, &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
);
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
),
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
),
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
),
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)),
244 address
->Connect(wxEVT_COMMAND_TEXT_UPDATED
, wxCommandEventHandler(dialog_disassemble::on_change
),
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
,
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
,
262 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_disassemble::on_cancel
),
264 top_s
->Add(pbutton_s
, 0, wxGROW
);
265 top_s
->SetSizeHints(this);
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;
301 //Set default disasm.
302 for(auto& i
: disasms
) {
307 if(j
< disasms
.size())
308 type
->SetSelection(j
);
310 //Set default address.
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
;
320 address
->SetValue(towxstring((stringfmt() << std::hex
<< dflt_base
).str()));
326 void dialog_disassemble::on_ok(wxCommandEvent
& e
)
332 std::string
dialog_disassemble::get_disassembler()
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) {
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
]);
359 std::string res
= tostdstring(type
->GetStringSelection());
366 uint64_t dialog_disassemble::get_address()
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
) {
380 uint64_t off
= hex::from
<uint64_t>(tostdstring(address
->GetValue()));
381 uint64_t res
= base
+ off
;
387 uint64_t dialog_disassemble::get_count()
390 return count
->GetValue();
393 void dialog_disassemble::on_change(wxCommandEvent
& e
)
398 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
399 } catch(std::exception
& e
) {
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);
412 class wxwin_tracelog
;
414 class dialog_breakpoint_add
: public wxDialog
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
);
423 std::list
<memory_space::region
*> regions
;
431 dialog_breakpoint_add::dialog_breakpoint_add(wxWindow
* parent
, std::list
<memory_space::region
*> _regions
)
432 : wxDialog(parent
, wxID_ANY
, wxT("Add breakpoint"))
436 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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,
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
,
465 cancel
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoint_add::on_cancel
),
467 top_s
->Add(pbutton_s
, 0, wxGROW
);
468 top_s
->SetSizeHints(this);
472 void dialog_breakpoint_add::on_address_change(wxCommandEvent
& e
)
476 hex::from
<uint64_t>(tostdstring(address
->GetValue()));
483 std::pair
<uint64_t, debug_context::etype
> dialog_breakpoint_add::get_result()
486 std::string vmaname
= tostdstring(vmasel
->GetStringSelection());
487 std::string addrtext
= tostdstring(address
->GetValue());
490 for(auto i
: regions
)
491 if(i
->name
== vmaname
)
496 addr
= base
+ hex::from
<uint64_t>(addrtext
);
497 } catch(std::exception
& e
) {
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
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
);
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
;
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
535 wxwin_tracelog(wxWindow
* parent
, emulator_instance
& _inst
, int _cpuid
, const std::string
& cpuname
);
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
);
548 class _panel
: public text_framebuffer_panel
551 _panel(wxwin_tracelog
* parent
, emulator_instance
& _inst
);
552 void on_size(wxSizeEvent
& e
);
553 void on_mouse(wxMouseEvent
& e
);
554 wxSize
DoGetBestSize() const;
556 std::vector
<std::string
> rows
;
557 void on_popup_menu(wxCommandEvent
& e
);
558 bool scroll_to_end_on_repaint
;
560 void prepare_paint();
562 emulator_instance
& inst
;
563 uint64_t pressed_row
;
564 uint64_t current_row
;
568 emulator_instance
& inst
;
570 void scroll_pane(uint64_t line
);
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);
582 threads::lock buffer_mutex
;
583 std::list
<std::string
> lines_waiting
;
584 bool unprocessed_lines
;
588 std::string find_string
;
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
)
602 if(dirty
&& !wxwidgets_exiting
) {
603 int r
= prompt_for_save(this, "Trace log");
604 if(r
< 0 || (r
> 0 && !do_exit_save()))
608 inst
.iqueue
->run([this]() { kill_debug_hooks(); });
609 trace_active
= false;
615 void wxwin_tracelog::kill_debug_hooks(bool kill_hard
)
617 CORE().dbg
->remove_callback(cpuid
, debug_context::DEBUG_FRAME
, *this);
619 CORE().dbg
->remove_callback(cpuid
, debug_context::DEBUG_TRACE
, *this);
620 threads::alock
h(buffer_mutex
);
621 for(auto& i
: rwx_breakpoints
) {
625 CORE().dbg
->remove_callback(i
.first
.first
, i
.first
.second
, *this);
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
)
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
);
663 void wxwin_tracelog::_panel::on_mouse(wxMouseEvent
& e
)
666 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
668 if(local_line
< rows
.size()) {
670 pressed_row
= local_line
;
672 }else if(e
.RightUp()) {
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"));
683 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
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()
723 threads::alock
h(this->buffer_mutex
);
724 size_t osize
= panel
->rows
.size();
726 panel
->rows
.push_back(std::string(120, '-'));
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;
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
)
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
);
753 case debug_context::DEBUG_TRACE
: {
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;
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(); });
787 void wxwin_tracelog::killed(uint64_t addr
, debug_context::etype 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;
798 case debug_context::DEBUG_TRACE
:
802 kill_debug_hooks(true);
804 this->enabled
->SetValue(false);
805 this->enabled
->Enable(false);
806 this->m_singlestep
->Enable(false);
809 case debug_context::DEBUG_FRAME
:
815 void wxwin_tracelog::on_enabled(wxCommandEvent
& e
)
818 bool enable
= enabled
->GetValue();
819 inst
.iqueue
->run([this, enable
]() {
821 threads::alock
h(buffer_mutex
);
824 for(auto& i
: rwx_breakpoints
) {
826 CORE().dbg
->add_callback(i2
.first
, i2
.second
, *this);
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
;
845 std::string tmp
= pattern
;
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
);
856 void wxwin_tracelog::on_menu(wxCommandEvent
& e
)
859 if(e
.GetId() == wxID_EXIT
) {
861 int r
= prompt_for_save(this, "Trace log");
862 if(r
< 0 || (r
> 0 && !do_exit_save()))
866 inst
.iqueue
->run([this]() { this->kill_debug_hooks(); });
868 trace_active
= false;
871 } else if(e
.GetId() == wxID_SAVE
) {
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
)
879 if(!s
) throw std::runtime_error("Error writing output file");
881 } catch(canceled_exception
& e
) {
882 } catch(std::exception
& e
) {
883 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
886 } else if(e
.GetId() == wxID_FIND
) {
888 dialog_find
* d
= new dialog_find(this);
889 if(d
->ShowModal() != wxID_OK
) {
893 tmp
= d
->get_pattern();
904 wxMessageBox(towxstring("Invalid search pattern"), _T("Invalid pattern"),
905 wxICON_EXCLAMATION
| wxOK
, this);
911 while(find_line
< panel
->rows
.size()) {
912 if(find_match(find_string
, panel
->rows
[find_line
]))
916 if(find_line
== panel
->rows
.size()) {
919 wxMessageBox(towxstring("Found nothing appropriate"), _T("Not found"),
920 wxICON_EXCLAMATION
| wxOK
, this);
922 scroll_pane(find_line
);
923 } else if(e
.GetId() == wxID_FIND_NEXT
) {
926 uint64_t old_find_line
= find_line
;
928 while(!panel
->rows
.empty() && find_line
!= old_find_line
) {
929 if(find_line
>= panel
->rows
.size())
931 if(find_match(find_string
, panel
->rows
[find_line
]))
935 scroll_pane(find_line
);
936 } else if(e
.GetId() == wxID_FIND_PREV
) {
939 uint64_t old_find_line
= 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
]))
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
);
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()))
972 panel
->request_paint();
977 void wxwin_tracelog::_panel::on_popup_menu(wxCommandEvent
& e
)
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());
987 for(uint64_t i
= m
; i
< M
&& i
< rows
.size(); i
++) {
989 std::string mline
= rows
[i
];
990 if(lines
== 1) str
+= "\n";
992 if(lines
>= 1) str
+= "\n";
1000 if (wxTheClipboard
->Open()) {
1001 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
1002 wxTheClipboard
->Close();
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";
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
|
1021 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1029 void wxwin_tracelog::scroll_pane(uint64_t line
)
1031 unsigned r
= panel
->get_characters().second
;
1032 unsigned offset
= r
/ 2;
1034 scroll
->set_position(panel
->pos
= 0);
1035 else if(line
+ r
<= panel
->rows
.size())
1036 scroll
->set_position(panel
->pos
= line
- offset
);
1038 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1039 panel
->request_paint();
1042 bool wxwin_tracelog::do_exit_save()
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");
1055 } catch(canceled_exception
& e
) {
1057 } catch(std::exception
& e
) {
1058 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
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
);
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
);
1079 //We'll register this later.
1080 rwx_breakpoints
[i2
] = false;
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
];
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
)
1104 singlestepping
= false;
1105 find_active
= false;
1108 trace_active
= false;
1109 unprocessed_lines
= false;
1113 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
),
1122 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxwin_tracelog::on_wclose
),
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();
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
),
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
);
1167 std::string language
;
1171 class wxwin_disassembler
: public wxFrame
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
);
1180 class _panel
: public text_framebuffer_panel
1183 _panel(wxwin_disassembler
* parent
, emulator_instance
& _inst
);
1184 void on_size(wxSizeEvent
& e
);
1185 void on_mouse(wxMouseEvent
& e
);
1186 wxSize
DoGetBestSize() const;
1188 std::vector
<uint64_t> rows
;
1189 std::map
<uint64_t, disasm_row
> row_map
;
1190 void on_popup_menu(wxCommandEvent
& e
);
1192 void prepare_paint();
1194 emulator_instance
& inst
;
1195 uint64_t pressed_row
;
1196 uint64_t current_row
;
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
;
1213 wxwin_disassembler::_panel::_panel(wxwin_disassembler
* parent
, emulator_instance
& _inst
)
1214 : text_framebuffer_panel(parent
, 20, 5, wxID_ANY
, NULL
), inst(_inst
)
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
,
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
,
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
);
1245 void wxwin_disassembler::_panel::on_mouse(wxMouseEvent
& e
)
1248 uint64_t local_line
= pos
+ e
.GetY() / get_cell().second
;
1250 if(local_line
< rows
.size()) {
1252 pressed_row
= local_line
;
1254 }else if(e
.RightUp()) {
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"));
1267 current_row
= min(local_line
, static_cast<uint64_t>(rows
.size()));
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();
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
)
1302 if(e
.GetId() == wxID_EXIT
) {
1304 int r
= prompt_for_save(this, "Disassembly");
1305 if(r
< 0 || (r
> 0 && !do_exit_save()))
1310 } else if(e
.GetId() == wxID_SAVE
) {
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");
1320 } catch(canceled_exception
& e
) {
1321 } catch(std::exception
& e
) {
1322 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
1325 } else if(e
.GetId() == wxID_DISASM
) {
1327 dialog_disassemble
* d
= new dialog_disassemble(this, inst
);
1328 if(d
->ShowModal() != wxID_OK
) {
1332 std::string disasm
= d
->get_disassembler();
1333 uint64_t addr
= d
->get_address();
1334 uint64_t count
= d
->get_count();
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
) {
1341 std::string to
= pick_text(this, "Goto", "Enter address to go to:", "");
1342 inst
.iqueue
->run_async([this, to
]() {
1347 std::string _to
= to
;
1348 size_t sp
= _to
.find_first_of("+");
1349 if(sp
>= _to
.length()) {
1352 vma
= _to
.substr(0, sp
);
1353 offset
= _to
.substr(sp
+ 1);
1357 for(auto i
: CORE().memory
->get_regions()) {
1358 if(i
->name
== vma
) {
1365 show_message_ok(this, "Error in address",
1366 "No such memory area known",
1367 wxICON_EXCLAMATION
);
1372 if(run_show_error(this, "Error in address", "Expected <hexdigits> or "
1373 " <name>+<hexdigits>", [&addr
, offset
]() {
1374 addr
= hex::from
<uint64_t>(offset
); }))
1377 runuifun([this, addr
]() {
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
)
1385 else if(this->panel
->rows
[nrow
] < addr
)
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.
1402 size_t high
= v
.size();
1405 mid
= (low
+ high
) / 2;
1414 v
.erase(v
.begin() + mid
);
1417 void wxwin_disassembler::_panel::on_popup_menu(wxCommandEvent
& e
)
1420 if(e
.GetId() == wxID_DISASM_MORE
)
1422 if(current_row
>= rows
.size())
1424 uint64_t base
= rows
[current_row
];
1425 uint64_t rbase
= base
;
1426 if(!row_map
.count(base
))
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
) {
1436 disasm
= d
->get_disassembler();
1437 uint64_t addr
= d
->get_address();
1438 uint64_t count
= d
->get_count();
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.
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.
1450 remove_from_array(rows
, i
);
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());
1459 for(uint64_t i
= m
; i
< M
; i
++) {
1461 std::string mline
= row_map
[rows
[i
]].row
;
1462 if(lines
== 1) str
+= "\n";
1464 if(lines
>= 1) str
+= "\n";
1472 if (wxTheClipboard
->Open()) {
1473 wxTheClipboard
->SetData(new wxTextDataObject(towxstring(str
)));
1474 wxTheClipboard
->Close();
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";
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
|
1493 for(uint64_t i
= m
; i
< M
; i
++)
1494 row_map
.erase(rows
[i
]);
1495 rows
.erase(rows
.begin() + m
, rows
.begin() + M
);
1503 std::string
format_vma_offset(memory_space::region
& region
, uint64_t offset
)
1505 std::ostringstream y
;
1507 size_t sizedigits
= 0;
1508 uint64_t tmp
= region
.size
- 1;
1513 y
<< "+" << std::hex
<< std::setfill('0') << std::setw(sizedigits
) << offset
;
1517 std::string
lookup_address(emulator_instance
& inst
, uint64_t raw
)
1519 auto g
= inst
.memory
->lookup(raw
);
1521 return hex::to
<uint64_t>(raw
);
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;
1533 void insert_into_array(std::vector
<uint64_t>& v
, uint64_t e
)
1535 //Binary search for the gap to insert to.
1537 size_t high
= v
.size();
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)
1545 else if(s1
== 0 || s2
== 0)
1552 if(mid
< v
.size() && v
[mid
] < e
)
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
;
1564 //We need to insert the row into rows.
1565 row_map
[addr
] = row
;
1566 insert_into_array(rows
, addr
);
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.
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
++) {
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
);
1607 r
.row
= (stringfmt() << "DATA 0x" << hex::to
<T
>(serialization::read_endian
<T
>(buf
, endian
))).
1609 else if(sizeof(T
) > 1)
1610 r
.row
= (stringfmt() << "DATA " << serialization::read_endian
<T
>(buf
, endian
)).str();
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
);
1619 disasm_row
disassemble_data_item(emulator_instance
& inst
, uint64_t& addrbase
, const std::string
& disasm
)
1622 if(disasm
[7] == 'l') endian
= -1;
1623 if(disasm
[7] == 'h') endian
= 0;
1624 if(disasm
[7] == 'b') endian
= 1;
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;
1652 scroll
->set_position(panel
->pos
= 0);
1653 else if(line
+ r
< panel
->rows
.size())
1654 scroll
->set_position(panel
->pos
= line
- offset
);
1656 scroll
->set_position(panel
->pos
= panel
->rows
.size() - r
);
1657 panel
->request_paint();
1660 void wxwin_disassembler::on_wclose(wxCloseEvent
& e
)
1663 if(dirty
&& !wxwidgets_exiting
) {
1664 int r
= prompt_for_save(this, "Disassembly");
1665 if(r
< 0 || (r
> 0 && !do_exit_save()))
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
);
1686 add_rows_main(rowdata
);
1690 if(run_show_error(this, "Error in disassember", "No disassembler '" + disasm
+ "' found",
1692 d
= &disassembler::byname(disasm
);
1695 for(uint64_t i
= 0; i
< count
; i
++) {
1696 uint64_t base
= addrbase
;
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
;
1705 add_rows_main(rowdata
);
1708 bool wxwin_disassembler::do_exit_save()
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");
1721 } catch(canceled_exception
& e
) {
1723 } catch(std::exception
& e
) {
1724 wxMessageBox(towxstring(e
.what()), _T("Error creating file"), wxICON_EXCLAMATION
|
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
)
1739 wxBoxSizer
* top_s
= new wxBoxSizer(wxHORIZONTAL
);
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();
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
),
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
),
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
)
1782 regions
= inst
.memory
->get_regions();
1783 wxBoxSizer
* top_s
= new wxBoxSizer(wxVERTICAL
);
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
,
1797 delb
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_delete
),
1799 ok
->Connect(wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEventHandler(dialog_breakpoints::on_ok
), NULL
,
1801 top_s
->Add(pbutton_s
, 0, wxGROW
);
1802 top_s
->SetSizeHints(this);
1806 void dialog_breakpoints::populate_breakpoints()
1809 auto t
= pwin
->get_breakpoints();
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
)
1822 debug_context::etype dtype
;
1823 dialog_breakpoint_add
* d
= new dialog_breakpoint_add(this, regions
);
1824 if(d
->ShowModal() != wxID_OK
) {
1828 rpair(addr
, dtype
) = d
->get_result();
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
)
1843 int idx
= brklist
->GetSelection();
1844 if(idx
== wxNOT_FOUND
)
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
)
1860 for(i
= 0; i
< listsyms
.size(); i
++)
1861 if(entry
< listsyms
[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
);
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
)
1889 delb
->Enable(brklist
->GetSelection() != wxNOT_FOUND
);
1893 void wxeditor_tracelog_display(wxWindow
* parent
, emulator_instance
& inst
, int cpuid
, const std::string
& cpuname
)
1897 wxwin_tracelog
* d
= new wxwin_tracelog(parent
, inst
, cpuid
, cpuname
);
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
)
1907 wxwin_disassembler
* d
= new wxwin_disassembler(parent
, inst
);